基于策略的设计:如何以合适的方式定制主机结构
Policy based design: how to customize the Host structure in a proper way?
我有一堆算法和集合,我正在使用基于策略的设计(参见《现代c++设计》一书)来处理任意组合的复杂性。这很好,但是为了防止使用指向策略的指针破坏Host类,我建议将受策略保护的析构函数设置为protected。然而,如果我使Algorithm和Collection析构函数受到保护,我就不能单独使用它们,而只能将它们作为策略使用。此外,与通用工厂模式相比,我看不出基于策略的设计有什么好处……
下面是代码的模型:
#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;
}
以下是我的问题:
我正在使用策略自定义宿主类的结构,当每个实际的算法都需要一个集合来工作,并且集合被封装在宿主中时,我如何才能真正丰富宿主类的接口?
当我比较基于策略的设计和通用工厂时,通用工厂不是给我带来了相同的组合复杂性吗?似乎使用通用工厂是更好的,因为我可以在所有可能的组合中交换元素,容器和算法,我仍然可以为所有策略提供公共析构函数,这允许我以任何我想要的方式组合它们,例如元素,集合和算法的全局组合。
在我看来,一旦结构被定制,富集策略就会成为一个问题。即使我稍后向算法添加成员函数,它也可能有与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);
我是否完全误解了基于策略的设计(又一次),我是否正确使用了这些特征?在这种情况下,通用策略模式是更好的选择吗?
您可能需要仔细考虑设计中耦合的数量。例如,确保你真的希望你的算法将Collection
作为模板参数。这引入了算法与其操作的容器之间的耦合。看一下标准库:它的算法是函数模板,以迭代器作为模板形参。迭代器对它们所指向的容器(在您的词汇表中是Collection)一无所知。
为了做迭代和访问之外的其他事情,算法采用稍微丰富的类型作为参数,例如back_inserters
访问容器的push_back()
成员。但在一般情况下——在没有先验知识的情况下——绝对没有必要将整个容器接口传递给所有算法。要真正做到容器特定的事情,将算法嵌入为容器成员函数(例如std::list
的sort()
成员函数)更合适。
对于做稍微不同的事情,有几个重载(例如std::transform
)相同的函数名。只有当你的算法需要维护状态时,才有必要将其作为类模板,最好的方法是将其作为函数对象,即包含重载操作符(),而不是DoSomethingWithData()
成员函数。
您的数据以与标准库相同的方式参数化:以Element
作为模板参数的类模板。将这些数据提供给算法的方法是以begin()
和end()
成员函数的形式提供对数据的迭代器访问。标准容器(vector
, map
, unorderd_map
等)也将策略作为模板参数,例如Allocator
, Compare
或Hash
类,您可以通过它们自定义数据的行为。标准智能指针使用Deleter
策略参数来定制它们的行为。
总之:仔细检查你的设计。你到底想做什么?每个组件(算法、数据结构)需要了解对方的哪些信息?你能正确使用标准库吗?可以肯定的是,您可能想要的大多数东西都已经在这里编码了,您可以专注于编写应用程序的逻辑,而不是算法或数据结构细节。
- 在基于范围的for循环中使用结构化绑定声明
- 使用结构化绑定'Reflection'
- 我可以使用哪种数据结构来处理这种方式
- 为什么结构化绑定不使用"auto&"返回对结构成员的引用,而是返回成员本身
- 为什么 boost::comb 对结构化绑定的支持缺少结构化绑定机制对 boost::tuples::cons 的适应?
- 结构化绑定初始值设定项表单 { 赋值表达式 } 对于 clang 上的数组类型失败
- 在 C++14 中手动实现结构化绑定
- C++结构模板变量快捷方式定义不起作用
- 为什么结构化绑定不支持可变数组?
- 在只读(即 const)访问器上执行结构化绑定的最佳实践是什么?
- 没有类型结构绑定不起作用?
- 对结构成员的临时绑定引用
- 更正GLSL无绑定纹理句柄中的结构布局
- 使用字节数组具有单字节对齐方式的结构是否安全
- 以类似数组的方式访问结构成员:在与数组不同的结构中填充?
- 以C++方式为结构和数组着色
- 当您有结构名称和字段值的列表时,是否可以以编程方式创建结构的对象并填充字段
- 为什么不应该以这种方式隐藏结构实现
- 当使用spirit以其他方式解析结构时,会导致输出混乱
- 按成员方式发送结构体