Answers:
两者之间的主要区别在于何时选择了具体算法。
使用Template方法模式,这可以通过在子类化模板时在编译时发生。通过实现模板的抽象方法,每个子类提供了不同的具体算法。当客户端调用模板外部接口的方法时,模板会根据需要调用其抽象方法(其内部接口)以调用算法。
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); }
}
if (config.useAlgoA) impl = new AlgoA() else impl = new AlgoB()
),因此此答案不正确。
new ConcreteAlgorithm1()
与进行选择new ConcreteAlgorithm2()
。显然,选择是在运行时发生的(在编译时选择算法就意味着要对其进行硬编码)。两者之间的主要区别在于具体算法的实现方式。它是作为子类还是作为单独的接口实现的?前者是模板。后者是一种策略。差异可以概括为组合与继承,这是GoF书中的常见主题。
当特定操作具有一些可以根据其他变化的原始行为定义的不变行为时,将使用模板模式。抽象类定义不变的行为,而实现类定义依赖的方法。
在策略中,行为实现是独立的-每个实现类都定义行为,并且它们之间没有共享的代码。两者都是行为模式,因此,客户端以几乎相同的方式使用它们。通常,策略只有一个公共方法- execute()
方法,而模板可以定义一组公共方法以及子类必须实现的一组支持私有原语。
这两种模式很容易一起使用。您可能有一个策略模式,其中多个实现属于使用模板模式实现的一系列策略。
您可能是指模板方法模式。没错,它们满足非常相似的需求。我要说的是,如果您有一个“模板”算法定义了步骤的情况,最好使用模板方法,其中子类会覆盖这些步骤以更改一些细节。在采取策略的情况下,您需要创建一个接口,而不是继承,而是在使用委派。我会说这是一个更强大的模式,按照DIP(依赖倒置原则),可能会更好。它功能更强大,因为您可以清楚地定义新的策略抽象-一种做事的方式,不适用于模板方法。因此,如果这种抽象有意义,请使用它。但是,使用模板方法可以在简单的情况下为您提供更简单的设计,这也很重要。考虑哪个词更合适:你有模板算法吗?还是这里的关键是您有一个抽象的策略-做某事的新方法
模板方法的示例:
Application.main()
{
Init();
Run();
Done();
}
在这里,您从应用程序继承,并替换了在init,运行和完成时将要执行的操作。
策略示例:
array.sort (IComparer<T> comparer)
在这里,编写比较器时,您不会从数组继承。数组将比较算法委托给比较器。
策略和模板方法模式之间有很多相似之处。策略和模板方法模式均可用于满足“开闭原则”,并使得软件模块易于扩展而无需更改其代码。两种模式都代表将一般功能与该功能的详细实现分开。但是,它们提供的粒度略有不同。
这是我在研究这两种模式时观察到的一些差异:
图片取自被咬的博客。
两者非常相似,并且都以相似的方式被客户端代码使用。与上面最流行的答案不同,两者都允许在运行时选择算法。
两者之间的区别在于,虽然策略模式允许不同的实现使用完全不同的方式来实现期望的结果,但是模板方法模式指定了一种用于实现结果的总体算法(“模板”方法)- -特定实现(子类)的唯一选择是所述模板方法的某些细节。这是通过让模板方法调用一个或多个被子类覆盖(即实现)的抽象方法来完成的,这与模板方法本身不是抽象的且未被子类覆盖的方法不同。
客户端代码使用抽象类类型的引用/指针对模板方法进行调用,该引用/指针指向一个具体子类之一的实例,可以像在使用策略模式时那样在运行时确定该实例。
模板方法:
战略:
策略结构:
相关文章:
不,它们不一定以相同的方式消耗。“模板方法”模式是向未来的实施者提供“指导”的一种方式。您正在告诉他们,“所有对象对象都必须具有社会安全号码”(这是一个简单的示例,但可以正确地理解这个想法)。
该策略模式允许切换多种可能的实现。它(通常)不是通过继承实现的,而是通过调用方传递所需的实现来实现的。例如,可能为ShippingCalculator提供了几种不同的税额计算方法之一(可能是NoSalesTax实现,而PercentageBasedSalesTax实现)。
因此,有时,客户实际上会告诉对象使用哪种策略。如
myShippingCalculator.CalculateTaxes(myCaliforniaSalesTaxImpl);
但是客户端永远不会对基于模板方法的对象执行此操作。实际上,客户端甚至可能不知道对象是基于模板方法的。模板方法模式中的那些抽象方法甚至可能受到保护,在这种情况下,客户端甚至都不知道它们的存在。
我建议您阅读这篇文章。它在一个实际案例中解释了差异。
引用文章
“ 可以看到,实现类也取决于模板方法类。如果要更改算法的某些步骤,则这种依赖关系会导致模板方法发生变化。另一方面,策略完全封装了算法。它给出了实现的方法。类来完全定义一种算法,因此,如果有任何更改,确实需要更改先前编写的类的代码,这就是我选择设计类策略的主要原因。
模板方法的一个特征是模板方法控制算法。在其他情况下这可能是一件好事,但是在我的问题中,这限制了我设计类的时间。另一方面,策略无法控制算法的步骤,这使我可以添加完全不同的转换方法。因此,就我而言,战略可以帮助我实施。
该策略的缺点之一是太多的代码冗余和更少的代码共享。显而易见,在本文的示例中,我必须一次又一次地在四个类中重复相同的代码。因此很难维护,因为如果更改了我们通用的系统(如步骤4)的实现,那么我将不得不在所有5个类中对其进行更新。另一方面,在模板方法中,我只能更改超类,并且更改将反映到子类中。因此,模板方法提供了非常低的冗余量,并且在类之间提供了大量的代码共享。
策略还允许在运行时更改算法。在模板方法中,必须重新初始化对象。策略的此功能提供了很大的灵活性。从设计的角度来看,人们必须更喜欢组合而不是继承。因此,采用战略模式也成为发展的首要选择。”
模板模式类似于策略模式。这两种模式的范围和方法不同。
策略用于允许呼叫者更改整个算法,例如如何计算不同类型的税费,而模板方法用于更改算法中的步骤。因此,策略的粒度更粗。模板允许在后续操作中进行更细粒度的控制,但允许更改这些详细信息的实现。
另一个主要区别是策略使用委派,而模板方法使用继承。在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)。
模式名称Template方法的含义是什么。假设我们有一个方法CalculateSomething(),并且希望对该方法进行模板化。该方法将在基类中声明为非虚拟方法。说方法看起来像这样。
CalculateSomething(){
int i = 0;
i = Step1(i);
i++;
if (i> 10) i = 5;
i = Step2(i);
return i;
} Step1和Step2方法的实现可以通过派生类给出。
在策略模式中,基础没有提供任何实现(这就是为什么基础实际上是类图中的接口的原因)
经典示例是排序。根据需要分类的对象数量,创建适当的算法类(合并,气泡,快速等),并将整个算法封装在每个类中。
现在我们可以将排序作为模板方法来实现吗?当然可以,但是您不会发现很多/任何共性要抽象出来并放入基础实现中。因此,它违反了模板方法模式的目的。