struct InkPen
{
void Write()
{
this->WriteImplementation();
}
void WriteImplementation()
{
std::cout << "Writing using a inkpen" << std::endl;
}
};
struct BoldPen
{
void Write()
{
std::cout << "Writing using a boldpen" << std::endl;
}
};
template<class PenType>
class Writer : public PenType
{
public:
void StartWriting()
{
PenType::Write();
}
};
int main()
{
Writer<InkPen> writer;
writer.StartWriting();
Writer<BoldPen> writer1;
writer1.StartWriting();
return 0;
}
我将上述代码编写为学习基于策略的设计的一部分。关于上面的代码我几乎没有问题
1 - 此实施看起来是否正确?我的意思是:它看起来真的像基于政策的设计吗?
2 - 我现在可以将任何类型的笔钩到作家身上。但是当我拿到没有默认构造函数的笔(仅参数化构造函数)时,我会怎么做?我将如何处理这种情况?
template<class PenType>
class Writer : public PenType
{
public:
void StartWriting()
{
PenType::Write();
}
};
3 - 使用上述代码时
Writer<InkPen> writer;
我猜编译器会将 PenType 替换为 InkPen 。如果是,为什么我无法从 StartWriting()中调用 Write()而不是为基类名添加前缀( PenType :: Write())?
4 - 我认为基于策略的设计会迫使您从语义无效的类派生。在上面的代码中,只有因为作者使用笔才能从笔中导出作者。但是说作家是笔在语义上是无效的。有没有其他更好的方法来解决这个问题,或者我在这里遗漏了什么?
有什么想法吗?
答案 0 :(得分:24)
以下是我将如何实现该类:
template<class PenType>
class Writer
{
public:
Writer(const PenType& pen = PenType()) : pen(pen) {}
void StartWriting()
{
pen.Write();
}
private:
PenType pen;
};
这允许用户将特定的Pen对象传递给构造函数,如果它没有默认构造函数,或者您不希望它被使用,其次,它仍然允许您省略PenType对象,如果您很乐意让它使用默认构造函数创建一个对象。 C ++标准库在许多类中都是这样做的(例如,考虑容器类的分配器)。
我删除了继承。它似乎没有添加任何东西(并且可能会导致问题。您可能不希望Writer类的用户直接调用PenType :: Write函数。您可以使用私有继承,但通常,组合是一个更简单,更传统的设计。
通常,基于策略的设计不需要继承。将其添加为成员也同样有效。如果您继续进行继承,请将其设为私有,这样就不会出现您提到的问题#4。
答案 1 :(得分:12)
这看起来像基于策略的智能指针实现的一个很好的例子:link。 Andrei Alexandrescu在他的一本书中描述了基于策略的智能指针实现。至于你现在的问题。我对这些东西有一些经验,但还不足以将我的话理所当然:
Ad 1&amp;我认为基于策略的设计更多是关于模板而不是继承。您编写模板类,模板参数是策略类,如:
template<class FooPolicy, class BarPolicy>
class Baz {
// implementation goes here
};
然后使用类中策略类的方法:
void Baz::someMethod(int someArg) {
FooPolicy::methodInit();
// some stuff
BarPolicy::methodDone();
}
我在此示例中使用静态方法,因为策略通常不需要任何状态。如果是,则按合成而不是通过继承合并策略的状态:
template<class FooPolicy, class BarPolicy>
class Baz {
private:
FooPolicy::State fooState; // might require 'typename' keyword, I didn't
// actually tried this in any compiler
// rest of the Baz class
};
广告2.您可以编写模板专门化 - 对于主类的特定组合及其策略,您可以编写任何方法或构造函数的特殊版本,AFAIK:
template <>
Baz<SomeConcreteFooPolicy, SomeConcreteBazPolicy>::Baz(someArgument)
: fooState(someArgument)
{
// stuff here
}
希望它对你有所帮助,
麦克
答案 2 :(得分:5)
1 - 这个实现看起来如何 正确?我的意思是它真的看起来 像基于政策的设计?
策略类通过组合行为以产生丰富多样的组合来获得它们的有用性。当你有一个这样的模板参数时,它就不是一个策略类了。
2 - 我现在可以挂钩任何类型的笔 作家。但是当我得到时,我会怎么做 没有默认构造函数的笔 (只有参数化构造函数)?怎么样 我会处理这种情况吗?
同样,这是政策类的一个奇怪的例子。但是,要直接回答您的问题,您可以提供一个接受PenType的构造函数。您还应该避免继承PenType并将其存储为成员(不需要将策略类与其策略紧密耦合)。
我猜编译器将取代PenType 用InkPen。如果是,为什么我不能 从中调用Write() StartWriting()而不是前缀 基类名称(PenType :: Write())?
从类模板继承时,必须指定this-&gt; member或BaseClass :: member。
4 - 我认为基于政策的设计力量 你从派生的派生出来的 语义无效。在上面 代码,作家是从笔中派生出来的 只是因为作家使用钢笔。但 说作家是笔是语义上的 无效。还有其他更好的方法吗? 解决这个或我失踪 什么东西在这里?
将PenType存储为上面建议的成员。总是喜欢使用组合继承,因为它避免了继承的紧密耦合关系。
答案 3 :(得分:3)
我知道这个帖子已经过时了,但是最初的帖子中存在一个主要缺陷,这个帖子是Google的最佳结果之一......所以:
不要将public
继承用于基于策略的设计!这将说“is-a”而不是“has-a”/“uses-a”。因此,您应该使用private
继承!