什么时候更喜欢模板化的基于策略的设计而非基于非模板化继承的设计

时间:2015-06-02 13:02:01

标签: c++ templates inheritance polymorphism policy

我试图了解基于策略的设计使用模板的真正要求。通过C ++中新的模板化设计,我发现基于策略的类设计是一种高度建议的设计方式,允许您“插入”来自策略类的不同行为。一个最小的例子是以下(维基的缩短版本):

template <typename LanguagePolicy>
class HelloWorld : private LanguagePolicy
{
    using LanguagePolicy::message;

public:
    // Behaviour method
    void run() const
    {
        // policy methods
        cout << message();
    }
};

class LanguagePolicyA
{
protected:
    std::string message() const
    {
        return "Hello, World!";
    }
};
//usage
HelloWorld<LanguagePolicyA> hello_worlda;
hello_worlda.run(); // prints "Hello, World!"

快速分析表明,为了获得不同的可插入方法message(),我们继承了模板化类型,其定义可由任何人提供(并在编译时识别)。

但是,不使用模板化代码和简单的旧学校运行时多态,可以实现相同级别的抽象(和可配置方法),如下所示。

class HelloWorld
{
    LanguagePolicy *lp; //list of all plugable class
public:
    HelloWorld(LanguagePolicy *lpn) {
        lp = lpn;
    }

    // Behaviour method
    void run() const
    {
        // policy methods
        cout << lp->message();
    }
};
class LanguagePolicy
{
protected:
    virtual std::string message() const;
};

class LanguagePolicyA: LanguagePolicy
{
protected:
    std::string message() const
    {
        return "Hello, World!";
    }
};
//usage
HelloWorld helloworld(new LanguagePolicyA);
helloworld.run();

功能性和抽象级别我没有看到两种方法有太大差异(即使第二种方法对LanguagePolicy几乎没有额外的代码行,我认为其他用户需要它了解界面;否则理解LanguagePolicy取决于文档)。但我确实认为后者是“干净的”(来自未使用模板的人)。这是因为在我看来,非模板化的课程看起来和理解都比较清晰。一个非常好的例子是流行的库VTK(可视化工具包),它使用第二种方法解决了许多不同的问题。即使没有关于VTK的大量文档,我们大多数人 - 它的用户,只能看看它的类图(有时候它们很大)并推断出类的行为;并在我们的应用程序中开发高度可配置和复杂的管道(无法将VTK映像为基于模板:))。相反的是像STL / BOOST这样的库,我认为任何人都无法在不使用大量文档的情况下识别类的工作。

所以我的问题是,基于模板的策略设计是否真的优于(仅在基于策略的设计的场景中)而不是基于虚拟继承?如果是的话,何时以及为何?

2 个答案:

答案 0 :(得分:1)

两者都是有效的结构化方式,实际上取决于要求。 E.g。

运行时与编译时多态性。

你想什么/能/必须实现多态?

虚拟来电的性能开销

模板生成没有间接的代码

该课程的实际用法。

当你必须存储异构集合时,需要一个基类,所以你必须使用继承。

关于基于政策的设计的一本非常好的书(有点过时但很好)Modern C++ Design

答案 1 :(得分:1)

取决于我猜的情况......使用模板的一个可能的缺点是该类型应该在编译时知道:

HelloWorld<English> hw; // English is plugged at compile-time

在你的第二个例子中,你使用指向基类的指针,这个指针可能指向各种派生类。它指向的内容不需要在编译时知道,因此可以在运行时通过(用户)输入确定。这种方法的一个可能的缺点是虚拟呼叫开销。在某些应用程序中,以及在某些平台上,这可能是不受欢迎的。