2

我有一堆算法和集合,并且我正在使用基于策略的设计(参见Modern C++ Design一书)来处理任意组合复杂性。这很好,但是为了防止使用指向策略的指针破坏 Host 类,1建议将策略的析构函数设为受保护。但是,如果我使算法和集合析构函数受到保护,我不能单独使用它们,而只能作为策略使用。此外,与通用工厂模式相比,我没有看到基于策略的设计的好处......

这是代码的模型:

#include <iostream>

template<class Collection>
class AlgorithmOne
{
    public: 
        void doSomethingWithData(Collection& data) 
        {
            // Use Collection iterators to build up an algorithm. 
        }
};

template<class Collection>
class AlgorithmTwo
{
    public: 
        void doSomethingWithData(Collection& data) 
        {
            // Use Collection iterators to build up an algorithm. 
        }
};

template<class Collection>
class AlgorithmThree
{
    public: 
        void doSomethingWithData(Collection& data) 
        {
            // Use Collection iterators to build up an algorithm. 
        }
};


template<class Element>
class CollectionOne
{
    public: 
        typedef Element ElementType;

};

template<class Element>
class CollectionTwo
{
    public: 
        typedef Element ElementType;

};

template<class Element>
class CollectionThree
{
    public: 
        typedef Element ElementType;

};

template<typename HostTraits>
class HostInheritsData
:
    public HostTraits::Collection, 
    public HostTraits::Algorithm
{
    public: 
        typedef HostTraits Traits;

        using Traits::Algorithm::doSomethingWithData;

        void doSomethingWithData() 
        {
            doSomethingWithData(*this);
        }
};

template<typename HostTraits>
class HostCompositsData 
:
    public HostTraits::Algorithm
{
    typename HostTraits::Collection data_;

    public: 
        typedef HostTraits Traits;

        using Traits::Algorithm::doSomethingWithData; 

        void doSomethingWithData() 
        {
            doSomethingWithData(data_);
        }

        // Clumsy and breaking encapsulation
        typename HostTraits::Collection& data()
        {
            return data_;
        }
};

template<typename HostTraits>
class GenericStrategy
{
    typename HostTraits::Collection data_; 
    typename HostTraits::Algorithm algorithm_; 

    public: 

        void doSomethingWithData() 
        {
            algorithm_.doSomethingWithData(data_);
        }
};

class ElementOne {}; 
class ElementTwo {}; 
class ElementThree {}; 

struct MyConfig
{
    typedef ElementOne                  Element;
    typedef CollectionThree<Element>      Collection;
    typedef AlgorithmOne<Collection>  Algorithm;
};

int main(int argc, const char *argv[])
{
    HostInheritsData<MyConfig> hostInherits;
    hostInherits.doSomethingWithData(); 

    // This must be a mistake, are policies meant to be used this way? 
    hostInherits.doSomethingWithData(hostInherits); 

    HostCompositsData<MyConfig> hostComposits;
    hostComposits.doSomethingWithData(); 

    // Clumsy to use, not intuitive and breaking encapsulation.
    hostComposits.doSomethingWithData(hostComposits.data()); 

    // Combinatorics are there, I can combine whatever I want in MyConfig as for
    // policies, but I can also have global Algorithm and Collection objects 
    // (no protected destructors).
    GenericStrategy<MyConfig> strategy; 
    strategy.doSomethingWithData(); 

    return 0;
}

以下是我的问题:

我正在使用策略自定义一个 Host 类的结构,当每个现实的算法都需要一个 Collection 来工作,而 Collection 被封装在 host 中时,如何才能真正丰富 Host 类的接口?

当我将基于策略的设计与通用工厂进行比较时,通用工厂不会给我带来相同的组合复杂性吗?似乎使用通用工厂更好,因为我也可以在所有可能的组合中交换元素、容器和算法,并且我仍然可以为所有策略拥有公共析构函数,这允许我以任何我想要的方式组合它们,例如元素、集合和算法的全局组合。

在我看来,一旦结构被定制,丰富的策略就会成为一个问题。即使我稍后将成员函数添加到算法中,它也可能具有与 Collection 相关的参数(例如 Collection 迭代器):如果 Host 使用组合封装 Collection,我需要向它询问自己的成员函数的参数:

// Clumsy to use, not intuitive and breaking encapsulation.
hostComposits.doSomethingWithData(hostComposits.data());

如果主机使用继承封装集合,它(至少对我来说)变得更加奇怪:

 // This must be a mistake, are policies meant to be used this way? 
    hostInherits.doSomethingWithData(hostInherits); 

我是否完全误解了基于策略的设计(再次),我是否正确使用了这些特征?在这种情况下,通用策略模式是更好的选择吗?

4

1 回答 1

1

您可能需要仔细考虑设计中的耦合量。例如,确保您真的希望您的算法将 aCollection作为模板参数。这引入了算法和它所操作的容器之间的耦合。看看标准库:它的算法是以迭代器作为模板参数的函数模板。迭代器也不知道他们指向的容器(你的词汇表中的集合)。

除了迭代和访问之外,算法采用稍微丰富的类型作为参数,例如back_inserters访问push_back()容器的成员。但总的来说——没有先验知识——绝对没有必要将整个容器接口传递给所有算法。要真正做容器特定的事情,将算法嵌入为容器成员函数(例如 的sort()成员函数std::list)更合适。

为了做一些稍微不同的事情,有几个std::transform相同函数名的重载(例如 of )。只有当您的算法需要维护状态时,才真正需要将其设为类模板,而最好的方法是将其设为函数对象,即包含重载的 operator(),而不是DoSomethingWithData()成员函数。

您的数据以与标准库相同的方式参数化:具有Elementas 模板参数的类模板。将此类数据提供给算法的方法是以成​​员函数的形式提供对数据的迭代器访问。标准容器(、等)也将策略作为模板参数,例如一个或类,您可以通过它自定义数据的行为。标准智能指针采用策略参数来自定义它们的行为。begin()end()vectormapunorderd_mapAllocatorCompareHashDeleter

简而言之:仔细检查您的设计。你想做什么?每个组件(算法、数据结构)需要了解对方什么?正确使用标准库你能做到吗?可以肯定的是,您可能想要的大部分内容已经在其中编码,您可以专注于编写应用程序的逻辑,而不是算法或数据结构细节。

于 2013-05-02T12:37:08.707 回答