美文网首页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