有人可以向我解释一下模板方法模式和策略模式有什么区别吗?
据我所知,它们 99% 是相同的——唯一的区别是模板方法模式有一个抽象类作为基类,而策略类使用由每个具体策略类实现的接口。
然而,就客户而言,它们以完全相同的方式被消耗——这是正确的吗?
有人可以向我解释一下模板方法模式和策略模式有什么区别吗?
据我所知,它们 99% 是相同的——唯一的区别是模板方法模式有一个抽象类作为基类,而策略类使用由每个具体策略类实现的接口。
然而,就客户而言,它们以完全相同的方式被消耗——这是正确的吗?
当特定操作具有一些可以根据其他不同的原始行为定义的不变行为时,使用模板模式。抽象类定义了不变的行为,而实现类定义了依赖的方法。
在一个策略中,行为实现是独立的——每个实现类都定义了行为,并且它们之间没有共享代码。两者都是行为模式,因此,客户以几乎相同的方式消费。通常策略有一个公共方法——execute()
方法,而模板可以定义一组公共方法以及一组支持子类必须实现的私有原语。
这两种模式可以很容易地一起使用。您可能有一个策略模式,其中几个实现属于使用模板模式实现的一系列策略。
两者的主要区别在于选择具体算法的时间。
使用模板方法模式,这通过子类化模板在编译时发生。每个子类通过实现模板的抽象方法来提供不同的具体算法。当客户端调用模板外部接口的方法时,模板会根据需要调用其抽象方法(其内部接口)来调用算法。
class ConcreteAlgorithm : AbstractTemplate
{
void DoAlgorithm(int datum) {...}
}
class AbstractTemplate
{
void run(int datum) { DoAlgorithm(datum); }
virtual void DoAlgorithm() = 0; // abstract
}
相反,策略模式允许在运行时通过包含来选择算法。具体算法由单独的类或函数实现,这些类或函数作为参数传递给策略的构造函数或 setter 方法。为此参数选择哪种算法可以根据程序的状态或输入动态变化。
class ConcreteAlgorithm : IAlgorithm
{
void DoAlgorithm(int datum) {...}
}
class Strategy
{
Strategy(IAlgorithm algo) {...}
void run(int datum) { this->algo.DoAlgorithm(datum); }
}
您可能是指模板方法模式。你是对的,它们满足非常相似的需求。如果您有一个“模板”算法具有定义的步骤,其中子类覆盖这些步骤以更改一些细节,我会说最好使用模板方法。在策略的情况下,您需要创建一个接口,而不是继承,您使用的是委托。我会说这是一个更强大的模式,并且根据 DIP - 依赖倒置原则可能更好。它更强大,因为您清楚地定义了一种新的策略抽象——一种做某事的方式,它不适用于模板方法。所以,如果这个抽象是有意义的——使用它。但是,使用模板方法可能会在简单的情况下为您提供更简单的设计,这也很重要。考虑哪些词更合适:你有模板算法吗?或者这里的关键是你有一个抽象的策略 - 做某事的新方法
模板方法示例:
Application.main()
{
Init();
Run();
Done();
}
在这里,您从应用程序继承并替换将在 init、run 和 done 上完成的操作。
策略示例:
array.sort (IComparer<T> comparer)
在这里,在编写比较器时,您不会从数组继承。Array 将比较算法委托给比较器。
策略和模板方法模式之间有很多相似之处。策略模式和模板方法模式都可以用于满足开闭原则,使软件模块易于扩展而无需更改其代码。这两种模式都代表了通用功能与该功能的详细实现的分离。但是,它们在提供的粒度方面略有不同。
以下是我在研究这两种模式时观察到的一些差异:
图片取自一口大小的博客。
继承与聚合(is-a 与 has-a)。这是实现同一目标的两种方法。
这个问题显示了选择之间的一些权衡:继承与聚合
两者都非常相似,并且都以相似的方式被客户端代码使用。与上面最受欢迎的答案不同,两者都允许在运行时选择算法。
两者之间的区别在于,虽然策略模式允许不同的实现使用完全不同的方式来实现预期的结果,但模板方法模式指定了一种用于实现结果的总体算法(“模板”方法)—— - 留给特定实现(子类)的唯一选择是所述模板方法的某些细节。这是通过让模板方法调用一个或多个被子类覆盖(即实现)的抽象方法来完成的,这与模板方法本身不是抽象的并且不会被子类覆盖.
客户端代码使用抽象类类型的引用/指针来调用模板方法,该引用/指针指向可以在运行时确定的具体子类之一的实例,就像使用策略模式时一样。
我建议你阅读这篇文章。它解释了一个真实案例的差异。
引用文章
”正如我们所看到的,实现类也依赖于模板方法类。如果要更改算法的某些步骤,这种依赖关系会导致更改模板方法。另一方面,策略完全封装了算法。它给出了实现类来完全定义一个算法。因此,如果有任何变化,确实需要更改以前编写的类的代码。这是我选择设计类的策略的主要原因。
模板方法的一个特点是模板方法控制算法。在其他情况下这可能是一件好事,但在我的问题中,这限制了我设计课程。另一方面,策略不控制算法的步骤,这使我能够添加完全不同的转换方法。因此,就我而言,策略有助于我实施。
策略的一个缺点是代码冗余过多,代码共享较少。正如在本文的示例中很明显,我必须一次又一次地在四个类中重复相同的代码。因此很难维护,因为如果我们的系统的实现(例如对所有人通用的第 4 步)发生更改,那么我将不得不在所有 5 个类中更新它。另一方面,在模板方法中,我只能更改超类,更改会反映到子类中。因此模板方法提供了非常低的冗余量和类之间的大量代码共享。
策略还允许在运行时更改算法。在模板方法中,必须重新初始化对象。策略的这一特性提供了很大的灵活性。从设计的角度来看,人们必须更喜欢组合而不是继承。因此使用策略模式也成为了发展的首要选择。”
不,它们不一定以相同的方式消费。“模板方法”模式是一种为未来的实现者提供“指导”的方式。您是在告诉他们,“所有 Person 对象都必须有一个社会安全号码”(这是一个微不足道的例子,但它正确地理解了这个想法)。
策略模式允许切换多种可能的实现。它不是(通常)通过继承实现的,而是让调用者传入所需的实现。一个示例可能是允许为 ShippingCalculator 提供几种不同的税收计算方式之一(NoSalesTax 实现,可能还有 PercentageBasedSalesTax 实现)。
因此,有时,客户端实际上会告诉对象使用哪种策略。如在
myShippingCalculator.CalculateTaxes(myCaliforniaSalesTaxImpl);
但是客户端永远不会为基于模板方法的对象这样做。事实上,客户甚至可能不知道一个对象是基于模板方法的。模板方法模式中的那些抽象方法甚至可能受到保护,在这种情况下,客户端甚至都不知道它们的存在。
在策略模式中,子类正在运行并控制算法。这里的代码在子类中重复。该算法的知识以及如何实现它分布在许多类中。
在模板模式中,基类有算法。它最大化子类之间的重用。由于算法位于一个地方,基类保护它。
模板模式类似于策略模式。这两种模式在范围和方法上有所不同。
策略用于允许调用者改变整个算法,例如如何计算不同类型的税,而模板方法用于改变算法中的步骤。因此,策略的粒度更粗。模板允许在后续操作中进行更细粒度的控制,但允许这些细节的实现发生变化。
另一个主要区别是策略使用委托,而模板方法使用继承。在 Strategy 中,算法被委托给另一个 xxxStrategy 类,主题将引用该类,但是使用 Template 您将基类子类化并覆盖方法以进行更改。
来自http://cyruscrypt.blogspot.com/2005/07/template-vs-strategy-patterns.html
模板方法是关于让子类重新定义算法的某些步骤,而不改变基类中定义的算法的主要结构和步骤。模板模式通常使用继承,因此可以在基类中提供算法的通用实现,如果需要,子类可能会选择覆盖它。
public abstract class RobotTemplate {
/* This method can be overridden by a subclass if required */
public void start() {
System.out.println("Starting....");
}
/* This method can be overridden by a subclass if required */
public void getParts() {
System.out.println("Getting parts....");
}
/* This method can be overridden by a subclass if required */
public void assemble() {
System.out.println("Assembling....");
}
/* This method can be overridden by a subclass if required */
public void test() {
System.out.println("Testing....");
}
/* This method can be overridden by a subclass if required */
public void stop() {
System.out.println("Stopping....");
}
/*
* Template algorithm method made up of multiple steps, whose structure and
* order of steps will not be changed by subclasses.
*/
public final void go() {
start();
getParts();
assemble();
test();
stop();
}
}
/* Concrete subclass overrides template step methods as required for its use */
public class CookieRobot extends RobotTemplate {
private String name;
public CookieRobot(String n) {
name = n;
}
@Override
public void getParts() {
System.out.println("Getting a flour and sugar....");
}
@Override
public void assemble() {
System.out.println("Baking a cookie....");
}
@Override
public void test() {
System.out.println("Crunching a cookie....");
}
public String getName() {
return name;
}
}
请注意,在上面的代码中,go() 算法步骤将始终相同,但子类可能会为执行特定步骤定义不同的配方。
策略模式是让客户端在运行时选择具体的算法实现。所有算法都是孤立和独立的,但实现了一个通用接口,并且没有在算法中定义特定步骤的概念。
/**
* This Strategy interface is implemented by all concrete objects representing an
* algorithm(strategy), which lets us define a family of algorithms.
*/
public interface Logging {
void write(String message);
}
/**
* Concrete strategy class representing a particular algorithm.
*/
public class ConsoleLogging implements Logging {
@Override
public void write(String message) {
System.out.println(message);
}
}
/**
* Concrete strategy class representing a particular algorithm.
*/
public class FileLogging implements Logging {
private final File toWrite;
public FileLogging(final File toWrite) {
this.toWrite = toWrite;
}
@Override
public void write(String message) {
try {
final FileWriter fos = new FileWriter(toWrite);
fos.write(message);
fos.close();
} catch (IOException e) {
System.out.println(e);
}
}
}
如需完整的源代码,请查看我的 github存储库。
策略设计模式
模板方法设计模式
策略作为接口公开,模板方法作为抽象类公开。这通常在框架中大量使用。例如 Spring 框架的 MessageSource 类是一个解析消息的策略接口。客户端使用此接口的特定实现(策略)。
以及同一个接口AbstractMessageSource的抽象实现,它具有解析消息的通用实现,并暴露了resolveCode()抽象方法,以便子类可以按照自己的方式实现它们。AbstractMessageSource 是模板方法的一个示例。
在此设计模式的模板方法中,一个或多个算法步骤可以被子类覆盖以允许不同的行为,同时确保仍然遵循总体算法(Wiki)。
模式名称模板方法意味着它是什么。假设我们有一个方法 CalculateSomething() 并且我们想要模板化这个方法。这个方法会在基类中声明一个非虚方法。说方法看起来像这样。
CalculateSomething(){
int i = 0;
i = Step1(i);
i++;
if (i> 10) i = 5;
i = Step2(i);
return i;
} Step1 和 Step2 方法实现可以由派生类给出。
在 Strategy Pattern 中,base 没有提供实现(这就是为什么 base 在类图中实际上是一个接口的原因)
经典的例子是排序。根据需要排序的对象数量,创建适当的算法类(合并、气泡、快速等),并将整个算法封装在每个类中。
现在我们可以将排序实现为模板方法吗?当然可以,但是您不会发现太多/任何共性被抽象出来并放置在基本实现中。所以它违背了模板方法模式的目的。
我认为主要区别在于,使用模板您需要一个算法来做某事,但是让我们说在该算法的中间您想要运行不同的行为,以便您可以发送接口的实现以在运行时动态地使该算法。
但是使用策略,您实际上拥有完全不同的算法执行,而不仅仅是算法的变体,然后您选择运行哪个算法,但是模板您只有一个带有变体的算法。
最后,您可以根据需要实施并将模板用作策略,反之亦然,但我看到了不同之处。
模板方法模式擅长阐明整体算法步骤,而策略模式适合灵活性和可重用性,因此您可以根据需要将策略组合在一起,例如:jdk8中的许多功能接口就像Comparator.reversed().thenComparing(Comparator)
是策略的角色。
模板方法模式关注更高的内聚,而策略模式与上下文对象松散耦合以分离关注点。
策略很容易维护,因为上下文不知道具体的策略,只要上下文中的主要算法发生变化,就不会影响策略。另一方面,如果您在抽象模板类中更改了算法的骨架,可能会影响其子类的升级。
它们都是不同的技术来达到相同的结果,所以问题是什么时候使用。