zoukankan      html  css  js  c++  java
  • 表驱动与工厂模式

    关于表驱动

            首次接触表驱动。还是在毕业不久之后。当时某部门经理给我们解说重构。即《重构:改善既有代码的设计》一书中简化条件表达式部分。关于if语句的处理。将其替换为多态形式,比如说工厂模式。

    可是即使替换为工厂,switch或者if的推断依然不能去除,那么有什么办法解决问题呢?

            当时我还在研究STL源代码,想到了traits编程技术,能够在编译期解决if的推断问题(尽管有这个想法,可是一直没有实现成功)。

    各路大牛提出了不同的见解,大家基本上都允许一条:使用“表”来解决。

    当时见识尚浅,不懂详细说的是什么意思,直到我知道了“表驱动”。

            “表驱动”来自于《代码大全》,此书在我的定义里,为一本软件project类的书。表驱动作为单独一章出现。而且在序言中推荐为0基础程序猿首读章节,可见其重要性。

            首先,为什么要有表驱动呢?表驱动的目的是避免逻辑语句(if和case),而使用表来查找推断信息。

    那么为什么要这么做呢?《代码大全》第5.2章节提到,软件的首要技术使命:管理复杂度。复杂度能够靠圈复杂度(一个函数可运行路径的数目)来推断。详细要涉及到图论等等方方面面。不再展开说明。

    那么表驱动的使用就能够大幅度的减少复杂度。

            其次,表驱动是什么?不论什么能够用逻辑语句来选择的事物,都能够通过查表来选择。比如说:情况1,选择事物1;情况2,选择事物2等等。存储在表中就是例如以下格式:

    情况

    事物

    1

    1

    2

    2

            那么,凡是用if和case来选择事物的语句。都能够替换为以下形式:

            Table[选择的情况i];

            这样就能够直接通过首地址+偏移量直接获取相应的内容,取消了推断逻辑。假设要选择第n个事物,那么就是首地址+n。直接得到了第n个事物。假设用正常的推断逻辑。那么可能须要推断n次才干够得到第n个事物。

            其它详细内容,大家能够自己去百度一下~

    工厂模式

            工厂模式来源于《设计模式》,是最最主要的模式之中的一个,也是最最经常使用的模式之中的一个。工厂模式也很easy。先来一个简单工厂说明一下表驱动的问题,其UML图例如以下:


            那么创建Product时。大部分要经过此过程:

    Product *product = nullptr;
    switch(productType)
    {
    case TYPE_PRODUCT1:
        product = new(std::nothrow)Product1();
        break;
    case TYPE_PRODUCT2:
        product = new(std::nothrow)Product2();
        break;
    case TYPE_PRODUCT3:
        product = new(std::nothrow)Product3();
        break;
    case TYPE_PRODUCT4:
        product = new(std::nothrow)Product4();
        break;
    default:
        break;
    }

            问题就这么随着出来了。逻辑语句怎么用表驱动替换呢?

    函数指针

            进入正式主题之前,另一些内容须要解决,由于表里面存储信息须要这一部分内容。

            函数指针想必大家都有所了解,比如以下的代码:

    // 定义一个函数指针
    typedef void (*FuncPtr)();
     
    // 定义与函数指针相应的函数
    void Func()
    {
        std::cout <<"Func." << std::endl;
    }
     
    int main(int argc, char **argv)
    {
        // 将函数指针指向相应的函数
        FuncPtr ptr = Func;
        // 调用函数
        ptr();
        return 0;
    }

    工厂表驱动

            有了产品类型。有了创建产品的方法,那么怎样将其写入表中呢?一般我们会这么存储:

    TYPE_PRODUCT1

    创建TYPE_PRODUCT1类型的函数指针

    TYPE_PRODUCT2

    创建TYPE_PRODUCT2类型的函数指针

    TYPE_PRODUCT3

    创建TYPE_PRODUCT3类型的函数指针

    TYPE_PRODUCT4

    创建TYPE_PRODUCT4类型的函数指针

            可是,我们在C++语言中应该怎样实现呢?表能够用数组,map等方式实现,比如以下的代码:

    typedef Product* (*NewProduct)();
     
    struct ProductCreator
    {
        int            m_productType;
        NewProduct     m_newProductFuncPtr;
    };
     
    const ProductCreator PRODUCT_CREATOR[] =
    {
        { TYPE_PRODUCT1, newProduct1 },
        { TYPE_PRODUCT2, newProduct2 },
        { TYPE_PRODUCT3, newProduct3 },
        { TYPE_PRODUCT4, newProduct4 },
    };

            可是这样能够吗?由于new会把实际对象创建出来。不能转化为一个函数指针,所以肯定是不能够的。怎样解决呢?

    使用仿函数

            百思不得其解,可是是问题总有解决的办法。要实现不同类型创建不同对象,不就是模板的思想么?从这个角度出发,问题立即就攻克了~

            解决方式,使用模板,创建一个仿函数(函数对象),通过函数对象创建实际的对象。

    实现代码例如以下:

    typedef Product* (*NewProduct)();
     
    template <class T>
    struct TypeCreator
    {
        static Product *New()
        {
            return(new(std::nothrow) T());
        }
    };
     
    struct ProductCreator
    {
        int            m_productType;
        NewProduct     m_newProductFuncPtr;
    };
     
    const ProductCreator PRODUCT_CREATOR[] =
    {
        { TYPE_PRODUCT1,TypeCreator<Product1>::New },
        { TYPE_PRODUCT2,TypeCreator<Product2>::New },
        { TYPE_PRODUCT3,TypeCreator<Product3>::New },
        { TYPE_PRODUCT4,TypeCreator<Product4>::New },
    };

            这样。就能够通过查PRODUCT_CREATOR这个表。取得函数对象。然后调用其方法就能够取得详细的对象,比如:

    Product *product = PRODUCT_CREATOR[i].m_newProductFuncPtr();

    使用指向Member function的指针

            近期看了《深度探索C++对象模型》,收获颇丰,当看到指向Member function的指针时。突发奇想,果断来试一把,看看是否能解决此问题。

            指向Memberfunction的指针,顾名思义,就是指向一个类成员函数的指针,事实上相似于函数指针,其声明方法例如以下:

    class A
    {
    public:
        void Func() {std::cout << "A Func." << std::endl; }
    };
     
    int main(int argc, char **argv)
    {
        void (A::* funcPtr)();
        funcPtr =&A::Func;
     
        A a;
        (a.*funcPtr)();
            
        A *b = new A;
        (b->*funcPtr)();
     
        return 0;
    }

            看到这种代码,真是有一种“山穷水尽疑无路,柳暗花明又一村”的感觉呐,大快人心。赶紧来看看是否能解决问题呢?
            终于结果,失败了。原因有两个:

            1. 实在想不出构造函数的指向Member function的指针怎么写。

    由于构造函数没有返回值,可是指向Member function的指针必须要有返回值的定义。

            2. 还记得C++第一节课老师讲过的内容吗?老实说,一个类会默认自己主动生成构造函数。析构函数,拷贝构造函数。

    事实上这个是错误的,依据构造函数语义学,一个类仅在下列四种情况下自己主动生成构造函数:

           1> 假设一个类没有不论什么构造函数,但它的一个成员内部有默认构造函数。那么这个类也须要生成默认构造函数,只是这个操作仅在构造函数被调用时才会发生。

            2> 当基类含有默认构造函数时。子类假设没有不论什么构造函数,则需合成默认构造函数。

            3> 当类含有虚函数时,假设未定义不论什么构造函数。则需合成默认构造函数。

            4> 当类有虚继承时,假设未定义不论什么构造函数,则需合成默认构造函数。

            事实上上面前两点是依赖于后两点的,为什么呢?看第一点和第二点,其都要求父类或者成员中包括默认构造。首先。觉得声明的构造函数不叫默认构造;其次,既然存在默认构造,那么肯定是第三点或者第四点造成的。所以说第一点和第二点依赖于后两点。

            所以说。以下这个类是没有构造函数的,包括默认构造函数:

    class Product
    {
    public:
             int m_IntVal;
    };

            那么要去通过一个指向Memberfunction的指针指向构造函数,肯定是失败的,所以编译器禁止指向构造函数的指针,并提示消息:

            Error:a constructor or destructor may not have its address taken

    进一步思考

            首先,表驱动方法是必须掌握的一个技巧,使用它将带来程序效率上的提升,代码的整洁等等各个方面的优点。

            其次,project的管理必须进行相关方面标准的定义及控制,使用SourceMonitor等工具把握项目质量至关重要。这周员工培训上,听老韩这么多年经验的总结,让我深深认识了“没有银弹”这个深刻的道理。在“银弹”没有造出来的前提下,不论什么过程都必须严格控制。否则将陷入无穷无尽的“焦油坑”。

            第三。继续给自己多挖几个坑吧。假设一直走平地。貌似永远也登不上高峰。由于同往高峰的路永远没有平路。

    參考书目

            《代码大全第二版》 Steve McConnell

            《STL源代码剖析》侯捷

            《重构:改善既有代码的设计》Martin Fowler

            《设计模式:可复用面向对象软件的基础》GoF四人帮

            《大话设计模式》程杰

            《深度探索C++对象模型》Stanley B.Lippman

            《人月神话》Frederick P.Brooks.Jr.

  • 相关阅读:
    Security+考试通过心得
    Splunk Power User认证
    Splunk笔记
    关于工作
    智能合约安全-parity多重签名钱包安全漏洞
    kickstart构建Live CD 添加文件问题
    Local Authentication Using Challenge Response with Yubikey for CentOS 7
    计算Linux权限掩码umask值
    IntelliJ IDEA 常用快捷键
    关于常量池-----小例子
  • 原文地址:https://www.cnblogs.com/mqxnongmin/p/10851922.html
Copyright © 2011-2022 走看看