美文网首页Exceptional C++
【Exceptional C++(12)】Classes之间的关

【Exceptional C++(12)】Classes之间的关

作者: downdemo | 来源:发表于2018-01-29 15:08 被阅读10次

    问题

    • 资源管理软件往往需要对某个表格中的所有记录做操作,先执行一次只读动作走一遍表格,放进cache,然后再进行修改操作。程序员不希望重复撰写这种常见逻辑,于是在以下抽象类中提供了一个泛型可复用框架。想法是抽象类将重复工作封装起来,收集需要操作的各个表格行,其次再对每一行执行必要的操作,派生类负责实现特定行为
    class GenericTableAlgorithm {
    public:
        GenericTableAlgorithm(const string& table);
        virtual ~GenericTableAlgorithm();
        // process()成功时返回true,该函数负责所有工作
        // 读取表格中的记录,为每笔记录调用Filter决定是否置于待处理的rows中
        // 当待处理的rows组成的list架构完成,为每个row调用ProcessRow()
        bool process();
    private:
        virtual bool Filter(const Record&);
        virtual bool ProcessRow(const PrimaryKey&) = 0;
        class GenericTableAlgorithmImpl* pimpl_;
    };
    
    • 比如客户端生成一个具体的类,在main函数中这样使用
    class MyAlgorithm : public GenericTableAlgorithm {
    // 重写Filter和ProcessRow做出特定行为
    };
    int main()
    {
        MyAlgorithm a("Customer");
        a.Process();
    }
    
    • 这是什么设计模式,为什么用在此处,pimpl_的作用是什么,这个设计如何改善

    解答

    • Template Method pattern,用在这里是因为只需要遵循相同步骤,就可以将某个常见解法一般化,只有细节部分可能不同,此部分由派生类实现,甚至一个派生类可以再次使用Template Method,将虚函数重写为另一个虚函数的wrapper,这样不同的步骤就可以填进class的不同层级中
    • pimpl_巧妙地将实现细节隐藏起来,它指向的结构内含private成员函数和变量,它们的改变不会造成到客户端需要重新编译的问题
    • GenericTableAlgorithm承担两个不同且不相干的任务,可以被有效隔离,因为这两个任务的客户不同,这两种客户是
      • client端,使用泛型演算法
      • GenericTableAlgorithm,使用特殊化后的concrete “detail” class实现某种特定行为
    • 下面是改进后的代码
    // File gta.h
    // 提供一个公开接口封装共用功能使之成为一个template method
    // 可巧妙地被隔离,使本身成为一个集中注意力的class
    // 客户目标锁定GenericTableAlgorithm的外部使用者
    class GTAClient;
    class GenericTableAlgorithm {
    public:
        // 构造函数现在接受一个具象的implementation对象
        GenericTableAlgorithm(const string& table, GTAClient& worker)
        // 现在从继承关系抽离出来了,不需要用虚析构函数
        ~GenericTableAlgorithm();
        bool Process();
    private:
        class GenericTableAlgorithmImpl* pimpl_;
    };
    // File gtaclient.h
    // 提供一个抽象接口,目的是提供扩展性
    // 这是GenericTableAlgorithm的一个实作细节,与外部client无关
    // 可被巧妙抽离为一个注意力集中的抽象protocal类
    // 客户目标锁定concrete "implementation detail" classes的撰写者
    // 他们使用并扩展GenericTableAlgorithm
    class GTAClient {
    public:
        virtual ~GTAClient() = 0;
        virtual bool Fliter(const Record&);
        virtual bool ProcessRow(const PrimaryKey&) = 0;
    };
    // File gtaclient.cpp
    bool GTAClient::Fliter(const Record&)
    {
        return true;
    }
    
    • 现在客户端看起来和以前看起来和以前非常近似
    class MyWorker : public GTAClient {
    // 重写Filter和ProcessRow做出特定行为
    };
    int main()
    {
        GenericTableAlgorithm a("Customer", MyWorker());
        a.Process();
    }
    
    • 虽然看起来近似,但有三个重要影响
      • 如果GenericTableAlgorithm的公开接口改变了,原来所有具象的worker class都要重新编译,因为它们派生自GenericTableAlgorithm,但现在GenericTableAlgorithm的任何改变都被隔离了,不会影响到worker class
      • 如果GenericTableAlgorithm的可扩充协定改变了,比如Filter和ProcessRow增加了一些额外参数,原来GenericTableAlgorithm的所有外部client都要重新编译,但现在GenericTableAlgorithm的扩充协定被隔离了,其变化不会影响外部使用者
      • 任何具象的work class现在可以在其他任何演算法中被使用,只要该算法能使用Filter和ProcessRow来进行运算

    相关文章

      网友评论

        本文标题:【Exceptional C++(12)】Classes之间的关

        本文链接:https://www.haomeiwen.com/subject/bvpxzxtx.html