所谓表驱动法(Table-Driven Approach),简单讲是指用查表的方法获取值。表驱动是将一些通过较为复杂逻辑语句来得到数据信息的方式,通过查询表的方式来实现,将数据信息存放在表里。对于消除长的switch-case和if-else-if语句来说很有效,比如下面的代码:

string GetDayName(int day)
{
string dayName;
if(day==)
{
dayName="星期一";
}
else if(day==)
{
dayName="星期二";
}
else if(day==)
{
dayName="星期三";
}
else if(day==)
{
dayName="星期四";
}
else if(day==)
{
dayName="星期五";
}
else if(day==)
{
dayName="星期日";
}
}

这样的主要问题是:

  • 代码太长,逻辑重复,圈复杂度高;
  • 可维护性低,当新增一个流程分支时就要添加一个判断语句。

通过表驱动就一两条语句就可以代替上面长长的if-else语句。

string GetDayName(int day)
{
string dayNames[] = {"星期日","星期一","星期二","星期三","星期四","星期五","星期六"};
return dayNames[day]
}

这样不仅代码看起来简明,而且后面如果增加新的数值的话维护起来也更简单、方便。

表驱动法虽然对于消除长的if-else语句、提高代码质量很有用,但是一般的表驱动难以重用,即这个类的表驱动难以被其它类的表驱动所重用,因为其它类的表驱动逻辑和当前类的逻辑并不一定相同,具体来说是逻辑分支的函数不尽相同,这个问题导致表驱动模式的实现不够通用。

如果要做一个通用的表驱动模式就会面临两个问题,第一个问题是如何在表中注册各种类型的执行函数,这些执行函数形参不尽相同,用function是不行的,因为function的类型在一开始就定下来了;第二个问题是表找到了相应的执行函数之后调用该函数的问题,因为每个函数的形参不尽相同,如何以一种统一的方式去调用也是一个问题;问题二比较好解决,用c++11的可变模板参数即可;问题一需要通过类型擦除来解决,这要用到前面2.3.5节中实现的any做这个事情。c++11版本的表驱动模式,支持各种类型的key,执行函数支持普通函数、函数对象、lamda表达式和成员函数。

c++11之前写一个通用表驱动模式面临两个问题,第一个问题是如何在表中注册各种类型的执行函数,这些执行函数形参不尽相同,用function是不行的,因为function的类型在一开始就定下来了;第二个问题是表找到了相应的执行函数之后调用该函数的问题,因为每个函数的形参不尽相同,如何以一种统一的方式去调用也是一个问题;问题二比较好解决,用c++11的可变模板参数即可;问题一需要通过类型擦除来解决,这要用到boost::any做这个事情。c++11版本的表驱动模式,支持各种类型的key,执行函数支持普通函数、函数对象、lamda表达式和成员函数。

上代码:

#include <functional>
#include <type_traits>
template<typenameKey>
class TableDriver
{
public:
template<typename... Args, typenameFunc>
void Register(Key&&key, Func&&func)
{
//typedef decltype(std::declval<Func>()(std::declval<Args>()...)) rettype;
typedeftypename std::result_of<Func(Args...)>::typerettype;
auto f = std::function<rettype(Args&& ...)>([=](Args&& ... args){return func(std::forward<Args>(args)...); });
m_map[key] = f;
} template<typenameR = void, typename... Args>
R Execute(Key&&key, Args&&... args)
{
auto it = m_map.find(key);
if (it == m_map.end())
return (R)nullptr; auto f = it->second.AnyCast<std::function<R(Args&& ...)>>();
return f(std::forward<Args>(args)...);
} //不带参数的执行
template<typenameR>
R Execute(Key&&key)
{
auto it = m_map.find(key);
if (it == m_map.end())
return (R)nullptr; auto f = it->second.AnyCast<std::function<void()>>();
return f();
} private:
std::map<Key, Any> m_map;
};

测试代码:

struct MyStruct2
{
void g(int x)
{
cout<<x<<endl;
} void g1()
{
cout<<2<<endl;
}
}; struct MyStruct3
{
void operator()(int x) const
{
cout<< x +1<<endl;
}
}; void TestTbDriver()
{
TableDriver<int> dv;
auto f = [](int a, int b){cout<<a+b<<endl;};
auto fuc = [](int a){cout<<a<<endl;};
auto fuc1 = [](){cout<<1<<endl;}; dv.Register<decltype(f),int,int>(1, f); //带参数的lamda表达式
dv.Register<decltype(fuc), int>(2, fuc);
dv.SimpleRegister<decltype(fuc1)>(22, fuc1); //不带参数的lamda表达式 MyStruct2 st2;
dv.Register<int>(4, &st2, &MyStruct2::g); //带参数的成员函数
MyStruct3 st3;
dv.Register<decltype(st3), int>(11, st3); //函数对象
dv.SimpleRegister(44, &st2, &MyStruct2::g1); //不带参数的成员函数 int a=3,b=4;
dv.Execute(2, a);
dv.Execute(1, a, b);
dv.Execute(22);
dv.Execute(44);
dv.Execute(11, a);
}
structTdd
{
int Test(intx)
{
returnx + ;
}
}; void TestDriver()
{
TableDriver<string> dv;
dv.Register("aa", []{cout <<"aa test;"<< endl; });
dv.Execute("aa"); int y = ;
dv.Register<int, int>("aa", [](inta, intb){returna + b; });
auto t = dv.Execute<decltype(y)>("aa", , ); Tdd a;
dv.Register<int>("aa", [&a](intx){return a.Test(x); });
auto t1 = dv.Execute<int>("aa", ); dv.Register<string>("aa", [](stringx){returnx; });
auto t2 = dv.Execute<string>(std::move(string("aa")), std::move(string("test")));
}

测试结果:

/**********************做一个更新,对代码进行简化和优化,更少的代码,并且可以带返回值了 **********************/

代码中用到的Any就是博客:http://www.cnblogs.com/qicosmos/p/3420095.html 中的Any

#include "Any.hpp"

template<typename Key>
class TableDriver
{
public:
template<typename... Args, typename Func>
void Register(Key && key, Func&& func)
{
//typedef decltype(std::declval<Func>()(std::declval<Args>()...)) rettype;
typedef typename std::result_of<Func(Args...)>::type rettype;
auto f = std::function<rettype(Args && ...)>([=](Args && ... args){return func(std::forward<Args>(args)...); });
m_map[key] = f;
}

template<typename R = void, typename... Args>
R Execute(Key && key, Args&&... args)
{
auto it = m_map.find(key);
if (it == m_map.end())
return (R)nullptr;

auto f = it->second.AnyCast<std::function<R(Args && ...)>>();
return f(std::forward<Args>(args)...);
}

//不带参数的执行
template <typename R>
R Execute(Key && key)
{
auto it = m_map.find(key);
if (it == m_map.end())
return (R)nullptr;

auto f = it->second.AnyCast<std::function<void()>>();
return f();
}

private:
std::map<Key, Any> m_map;
};

测试代码:

struct Tdd
{
int Test(int x)
{
return x + 2;
}
};

void TestDriver()
{
TableDriver<string> dv;
dv.Register("aa", []{cout << "aa test;" << endl; });
dv.Execute("aa");

int y = 0;
dv.Register<int, int>("aa", [](int a, int b){return a + b; });
auto t = dv.Execute<decltype(y)>("aa", 3, 4);

Tdd a;
dv.Register<int>("aa", [&a](int x){return a.Test(x); });
auto t1 = dv.Execute<int>("aa", 3);

dv.Register<string>("aa", [](string x){return x; });
auto t2 = dv.Execute<string>(std::move(string("aa")), std::move(string("test")));

}

(原创)c++11改进我们的模式之改进表驱动模式的更多相关文章

  1. Tomcat内核之ASCII解码的表驱动模式

    我们知道Tomcat通信是建立在Socket的基础上,而套接字在服务器端和客户端传递的报文都是未经过编码的字节流,每8位组成1个字节,计算机以二进制为基础,这是由于使用晶体管的开合状态表示1和0,这样 ...

  2. (原创)c++11改进我们的模式之改进访问者模式

    本次讲c++11改进我们的模式之改进访问者模式 访问者模式是GOF23个设计模式中比较复杂的模式之一,但是它的功能也很强大,非常适合稳定的继承层次中对象的访问,可以在不修改被访问对象的情况下,动态添加 ...

  3. C++11在时空性能方面的改进

    C++11在时空性能方面的改进 这篇我们聊聊C++11在时间和空间上的改进点: 主要包括以下方面: 新增的高效容器:array.forward_list以及unordered containers: ...

  4. [原创].NET 分布式架构开发实战五 Framework改进篇

    原文:[原创].NET 分布式架构开发实战五 Framework改进篇 .NET 分布式架构开发实战五 Framework改进篇 前言:本来打算这篇文章来写DAL的重构的,现在计划有点改变.之前的文章 ...

  5. 【JavaScript】设计模式-module模式及其改进

    写在前面 编写易于维护的代码,其中最重要的方面就是能够找到代码中重复出现的主题并优化他们,这也是设计模式最有价值的地方 说到这里...... <head first设计模式>里有一篇文章, ...

  6. 设计模式之module模式及其改进

    写在前面 编写易于维护的代码,其中最重要的方面就是能够找到代码中重复出现的主题并优化他们,这也是设计模式最有价值的地方 <head first设计模式>里有一篇文章,是说使用模式的心智, ...

  7. 【原创实现】C 多线程入门Demo CAS Block 2种模式实现

    分Cas和Block模式实现了demo, 供入门学习使用,代码全部是远程实现. 直接上代码: /* ================================================== ...

  8. Windows Azure Cloud Service (41) 修改云服务IIS托管管道模式为4.0经典模式

    <Windows Azure Platform 系列文章目录> 这是笔者在之前的项目中遇到的问题,做一下总结,给网友做参考. 在一般情况下,Visual Studio开发的Cloud Se ...

  9. 微博feed系统的推(push)模式和拉(pull)模式和时间分区拉模式架构探讨

    sns系统,微博系统都应用到了feed(每条微博或者sns里的新鲜事等我们称作feed)系统,不管是twitter.com或者国内的新浪微博,人人网等,在各种技术社区,技术大会上都在分享自己的feed ...

随机推荐

  1. Java多线程编程:Callable、Future和FutureTask浅析

    通过前面几篇的学习,我们知道创建线程的方式有两种,一种是实现Runnable接口,另一种是继承Thread,但是这两种方式都有个缺点,那就是在任务执行完成之后无法获取返回结果,那如果我们想要获取返回结 ...

  2. Android APP通用型拒绝服务、漏洞分析报告

    点评:记得曾经有段时间很多SRC平台被刷了大量APP本地拒绝服务漏洞(目前腾讯金刚审计系统已经可检测此类漏洞),移动安全团队发现了一个安卓客户端的通用型拒绝服务漏洞,来看看他们的详细分析吧. 0xr0 ...

  3. android dialog加载中动画效果

    //显示动画 dialog = new Dialog(context, R.style.loading); dialog.setContentView(R.layout.loadinglayout); ...

  4. 【jQuery】jquery插件封装

    扩展jQuery插件开发的基本知识,最佳做法和常见的陷阱. 入门 编写一个jQuery插件开始于给jQuery.fn加入​​新的功能属性,此处添加的对象属性的名称就是你插件的名称: jQuery.fn ...

  5. TFS安装配置

    安装TFS的准备工作: 1.需要安装IIS(必选): 2.需要安装SQL Server数据库(可选): 安装TFS的步骤如下: 选择左面的“基本”选项之后,点“启动向导”按钮,如下图: 进入”基本配置 ...

  6. C# Httpclient客户端操作

    原文地址:https://www.cnblogs.com/Xujg/p/4113387.html HttpClient 当前主流用法,异步请求,自.NET4.5开始可从Nuget包管理中获取. usi ...

  7. C#文件夹权限操作工具类

    using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Sec ...

  8. Swift 类型检查与类型转换

    前言 在 Swift 语言中一般使用 is 关键字实现类型检查,使用 as 关键字实现类型转换,除此之外还可以通过构造器和方法来实现类型转换. 1.类型检查 1.1 使用 is 检查 类型检查操作符 ...

  9. python学习笔记——提取网页中的信息正则表达式re

    被用来检索\替换那些符合某个模式(规则)的文本,对于文本过滤或规则匹配,最强大的就是正则表达式,是python爬虫里必不可少的神兵利器. 1 正则表达式re基本规则 [0-9] 任意一个数字,等价\d ...

  10. 整合大量开源库项目(八)能够载入Gif动画的GifImageView

    转载请注明出处王亟亟的大牛之路 上周大多数时间都是依据兴起,想到什么做什么写了几个自己定义控件,把Soyi丢在那没怎么动,今天就把写的东西整合进来,顺便把SOyi"个人研发的结构理一下&qu ...