模板方法模式和策略模式大致相同。我理解它们之间的基本差异(模板方法是基于继承,策略是基于组合),但是有什么体面的指导方针可以选择何时选择其中一个?看起来他们基本上做了同样的事情。
答案 0 :(得分:32)
策略允许在多个地方使用可重用的算法。如果您的算法可以由您的消费者提供并且可以在多个地方使用,那么这是策略的一个好地方(排序算法,谓词,比较器......就是很好的例子)。
模板方法专门针对您希望人们能够从您的类继承并希望他们能够以受控方式覆盖您的实现的情况(基本上阻止他们更换所有管道并为他们提供特定的扩展点没有冒问题,因为他们没有调用基本方法或在错误的时间调用它。)
它们可以是相似的,它们可以起到同样的作用,具体取决于您实际在做什么。 与所有设计模式一样,很难回答这样的问题,因为没有确切的答案。在上下文中确定更容易......
答案 1 :(得分:18)
当算法需要了解运行它的对象的内部时,我使用Template方法。
在所有其他情况下(即当算法只需要使用对象的接口时),我尝试使用策略。
此外,策略仅在有实际算法要实现时才有用:如果类之间的唯一区别是(例如)要返回的简单值,请使用模板方法。
答案 2 :(得分:18)
这两者实际上可以非常有效地结合使用。
不要将模式视为具有特定代码的配方来实现它们。
设计意图是关键,可以有很多实现。通过在某处提到代码中的模式名称,您可以在编写代码时让读者了解您的意图。实施是次要的。
模板方法为您提供“具有可替换步骤的算法”。 (该算法通常以不可覆盖的方法定义(例如最终或私有))
此概念的GoF实现使用继承和方法覆盖来替换这些步骤。
然而,如果这些步骤被策略取代,您仍然在使用Template方法。
例如,考虑一个想要按顺序遍历二叉树并在每个节点“做某事”的类。
意图是inorder()方法是一个模板方法 - walk的结构总是相同的。
“钩子”方法,“做某事”的部分可以作为同一类中的方法实现(并在子类中重写以改变行为),或者在外部,在这种情况下,它是“做某事”的策略
答案 3 :(得分:13)
在以下情况下考虑使用策略:
在其他情况下,应该使用模板模式。
答案 4 :(得分:6)
我不同意这句话(来自this answer):
“模板方法专门针对您想要的情况 人们能够从你的班级继承并希望他们能够 以受控方式覆盖您的实施。“
如果你想让人们从你的班级继承,那么你就想要一个特定的实现,而不是想要一个特定的行为。那闻起来很糟糕。
WANT的有效之处在于能够覆盖或提供算法各个步骤的实现。这个目标可以通过模板方法(我们可以有选择地覆盖受保护的方法)或策略模式(我们注入实现)来实现。
如果您正在构建一个实现算法的类,并且您希望允许其他开发人员更改该算法中的步骤,那么这就是您的要求。你唯一的决定是允许他们通过继承或组合来做到这一点。
在所有其他条件相同的情况下,我们应该优先考虑构成而不是继承,但我们甚至应该首先弄清楚我们的目标是什么(我们可能不需要),从而得到继承/构成决定。
我永远不会以“我想让他们继承这个班级”开头。这是马IMO之前的推车。
答案 5 :(得分:5)
您可以创建大继承树,只是为了更改N行为之一。并且您可以创建第二个大继承树来更改N行为中的第二个。
但您也可以通过创建小型策略树来卸载树。
因此,如果您注意到添加了越来越多的类只是为了在某些行为中添加一些更改 - 现在是时候为您的类提供策略了。
答案 6 :(得分:3)
我想同意和斯科特的第二个解释。
模板模式=关心绘制将进行操作的泛型线 - 模板 - 基本上是“具有可替换步骤的算法”(非常好的创造),其中可替换的步骤可以被委派使用策略模式概念。
策略模式=仅关注将客户端与操作的下划线实现分离,其结果需要始终遵守某些预定规则(例如排序结果始终是排序列表但您可能deffer de actual sorting to bubble sort or quick sort)。
干杯。
答案 7 :(得分:2)
中心OO设计原则之一是“赞成组合而非继承”,因此建议赞成策略模式。这显然取决于您在特定情况下要完成的任务。
答案 8 :(得分:0)
我几乎总是寻求策略,因为客户端代码不依赖于实现这一非常重要的原因,而在模板模式中,实现的一部分保留在抽象类中,抽象类中的任何更改都可能需要经常更改客户端导致严格的代码,我们最终开发人员告诉“这出现了比我预期的更大的变化”。
但是,如果在抽象类中获取公共代码真的很有帮助,我会毫不犹豫地这样做,并且还会尝试将与客户端代码相关的代码保持在远离它的位置
答案 9 :(得分:0)
我的总结:战略模式比模板方法模式更松散地耦合,这通常是一件好事。
罗伯特C.马丁在TEMPLATE METHOD & STRATEGY: Inheritance vs. Delegation
因此,战略模式提供了一个额外的好处 模板方法模式。而TEMPLATE METHOD模式允许a 通用算法来操纵许多可能的详细信息 通过完全符合DIP STRATEGY模式实现 另外允许每个详细的实现被操纵 许多不同的通用算法。
DIP是依赖性倒置原则:
一个。高级模块不应该依赖于低级模块。两者都应该取决于抽象。 B.抽象不应该依赖于细节。细节应该取决于抽象。
答案 10 :(得分:-1)
我更喜欢使用两者的混合,将默认实现(从模板模式)转储到策略模式的Context类。这样,我可以强制用户调用我想让他们调用的方法,这样算法步骤的执行顺序仍然可以控制。
/**
* enables replaceable steps in algorithm
*/
public interface HouseStrategy{
void buildWalls();
void buildPillars();
}
public class HouseContext{
//public API that enforces order of execution
public void build(HouseStrategy strategy){
buildFoundation();//default implementation
strategy.buildPillars();//delegated to concrete strategy
strategy.buildWalls();//delegated to concrete strategy
buildWindows();//default implementation
}
//default implementation
private void buildWindows() {
System.out.println("Building Glass Windows");
}
//default implementation
private void buildFoundation() {
System.out.println("Building foundation with cement,iron rods and sand");
}
}
public class WoodenHouse implements HouseStrategy {
@Override
public void buildWalls() {
System.out.println("Building Wooden Walls");
}
@Override
public void buildPillars() {
System.out.println("Building Pillars with Wood coating");
}
}
public class GlassHouse implements HouseStrategy {
@Override
public void buildWalls() {
System.out.println("Building Wooden Of glass");
}
@Override
public void buildPillars() {
System.out.println("Building Pillars with glass coating");
}
}
正如我们所看到的,具体的策略仍然可以延伸。如,
public class GlassHouse implements HouseStrategy,EarthquakeResistantHouseStrategy{......}
用法
HouseContext context = new HouseContext();
WoodenHouse woodenHouseStrategy = new WoodenHouse();
context.build(woodenHouseStrategy);
GlassHouse glassHouseStrategy = new GlassHouse();
context.build(glassHouseStrategy);
我在这里看到的一个缺点是具体策略只能改变算法的变体行为,即buildWalls()和buildPillars()。如果我们需要更改不变的部分,即buildFoundation()和buildWindows(),我们需要创建另一个实现新行为的Context类。 仍然,我们得到了一些代码可重用性,这在纯策略模式中找不到: - )