模板方法和策略模式之间的区别是什么?

谁能给我解释一下模板方法模式和策略模式的区别是什么?

据我所知,它们99%是相同的——唯一的区别是 模板方法模式有一个抽象类作为基础 类,而策略类使用已实现的接口

然而,就客户端而言,它们是以完全相同的方式被消费的——这是正确的吗?

78651 次浏览

继承与聚合(is-a与has-a)。这是实现同一目标的两种方法。

这个问题显示了选择之间的一些权衡

模板模式用于特定操作具有某些可以根据其他变化的原语行为定义的不变行为。抽象类定义了不变行为,而实现类定义了相关方法。

在策略中,行为实现是独立的——每个实现类定义行为,它们之间没有共享代码。两者都是行为模式,因此被客户以大致相同的方式消费。通常策略只有一个公共方法——execute()方法,而模板可以定义一组公共方法以及一组支持的私有原语,这些原语必须由子类实现。

这两种模式可以很容易地结合使用。您可能有一个策略模式,其中几个实现属于使用模板模式实现的策略家族。

你可能是指模板方法模式。 你说得对,它们的需求非常相似。 我想说,当你有一个“模板”算法定义了步骤,其中子类覆盖这些步骤来更改一些细节时,最好使用模板方法。 对于策略,您需要创建一个接口,而不是使用继承,而是使用委托。我想说,这是一个更强大的模式,也许更好地符合DIP依赖反转原则。它更强大,因为您清楚地定义了一种新的策略抽象——一种做某事的方式,这不适用于模板方法。所以,如果这个抽象是有意义的-使用它。但是,使用模板方法可以在简单的情况下为您提供更简单的设计,这也是重要的。 考虑一下哪个词更合适:你有模板算法吗?或者这里的关键是你有一个抽象的策略-做某事的新方法

模板方法的例子:

Application.main()
{
Init();
Run();
Done();
}

这里你继承了application,并替换了init, run和done的操作。

策略的例子:

array.sort (IComparer<T> comparer)

在这里,当编写比较器时,您不继承数组。数组将比较算法委托给比较器。

不,它们的消费方式不一定相同。“模板方法”模式是为未来的实现者提供“指导”的一种方式。您告诉他们,“所有Person对象都必须有一个社会安全号码”(这是一个微不足道的例子,但它正确地传达了思想)。

策略模式允许切换多个可能的实现。它(通常)不是通过继承实现的,而是通过让调用者传入所需的实现来实现的。例如,允许为ShippingCalculator提供几种不同的计算税收的方法之一(可能是NoSalesTax实现和PercentageBasedSalesTax实现)。

所以,有时候,客户端实际上会告诉对象使用哪种策略。就像在

myShippingCalculator.CalculateTaxes(myCaliforniaSalesTaxImpl);

但是客户端永远不会为基于模板方法的对象这样做。事实上,客户端甚至可能不知道对象是基于模板方法的。模板方法模式中的那些抽象方法甚至可能受到保护,在这种情况下,客户端甚至不知道它们的存在。

两者的主要区别在于具体算法的选择。

对于模板方法模式,这发生在模板子类化编译时处。每个子类通过实现模板的抽象方法提供不同的具体算法。当客户端调用模板外部接口的方法时,模板会根据需要调用它的抽象方法(内部接口)来调用算法。

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); }
}

总而言之:

  • 模板方法模式:通过子类化选择编译时算法
  • 策略模式:由容器选择运行算法

我认为这两种模式的类图显示了差异。

< p > 策略
在类
中封装算法 图片链接 enter image description here

< p > 模板方法
将算法的具体步骤推迟到子类
图片链接 enter image description here

模板模式类似于策略模式。这两种模式在范围和方法上有所不同。

策略用于允许调用者改变整个算法,比如如何计算不同类型的税,而模板方法用于改变算法中的步骤。因此,策略的粒度更粗。模板允许在操作序列中进行细粒度的控制,但允许这些细节的实现有所不同。

另一个主要区别是策略使用委托,而模板方法使用继承。在Strategy中,算法被委托给主题将引用的另一个xxxStrategy类,但在Template中,您可以继承基类并重写方法来进行更改。

http://cyruscrypt.blogspot.com/2005/07/template-vs-strategy-patterns.html

在策略模式中,子类起主导作用,它们控制算法。这里的代码是跨子类复制的。算法的知识和如何实现它分布在许多类中。

在模板模式中,基类具有算法。它最大化了子类之间的重用。由于算法在一个地方,基类保护它。

两者都非常相似,客户端代码以类似的方式使用它们。不像上面最流行的答案所说的,两者都允许在运行时选择算法

两者之间的区别在于,虽然策略模式允许不同的实现使用完全不同的方式来实现所需的结果,但模板方法模式指定了一个用于实现结果的总体算法(“模板”方法)——留给特定实现(子类)的唯一选择是所述模板方法的某些细节。这是通过让模板方法调用一个或多个摘要方法来完成的,这些方法由子类覆盖(即实现),不像模板方法本身不是抽象的,也不被子类覆盖。

客户端代码使用抽象类类型的引用/指针调用模板方法,该引用/指针指向具体子类之一的实例,该实例可以在运行时确定,就像使用策略模式时一样。

我建议你阅读文章。它解释了一个实际案例的差异。

引用自文章

可以看到,实现类也依赖于模板 类方法。此依赖关系将导致更改模板方法 有人想要改变算法的一些步骤。另一方面 侧策略完全封装了算法。它给出了 实现类来完整地定义一个算法。因此,如果 任何更改都需要更改之前的代码 写的类。这是我选择策略的主要原因

.设计类 模板方法的一个特点是模板方法控制 算法。这在其他情况下可能是件好事,但在我的 这个问题限制了我设计类。另一方面 侧策略不控制算法的步骤 我添加完全不同的转换方法。因此在我的案例中 策略帮助我实现。

策略的一个缺点是有太多的代码冗余和 减少代码共享。在这个例子中很明显 我不得不在四节课上重复同样的代码 一次。因此很难维护,因为如果实现 我们的系统,如步骤4,这是共同的,是改变,然后我 将不得不更新这在所有5类。另一边,往里 模板方法,我只能改变超类和变化 反映到子类中。因此模板法给出了一个很好的方法 系统间低冗余和高代码共享 类。< / em > < / p >

Strategy还允许在运行时更改算法。在模板 方法一必须重新初始化对象。的特点 策略提供了大量的灵活性。从设计角度 视图1更喜欢组合而不是继承。因此使用 战略模式也成为企业发展的首要选择。" < / em > < / p >

模板方法:

  1. 它基于继承
  2. 定义了不能被子类改变的算法框架。只有某些操作可以在子类中被重写
  3. 父类完全控制算法并且只与具体类的某些步骤不同
  4. 绑定在编译时完成

Template_method结构:

enter image description here

策略:

  1. 它基于代表团/成分
  2. 它改变了对象的内部结构通过修改方法行为
  3. 它被用于算法族之间的切换
  4. 它完全通过在运行时将一个算法替换为另一个算法改变对象在运行时的行为
  5. 绑定在运行时完成

策略结构:

enter image description here

为了更好地理解,可以查看模板方法策略文章。

相关文章:

JDK中的模板设计模式,无法找到定义方法集的方法按顺序执行

策略模式的真实例子

模板模式:

Template方法是关于让子类重新定义算法的某些步骤,而不改变基类中定义的算法的主要结构和步骤。 模板模式通常使用继承,因此可以在基类中提供算法的泛型实现,如果需要,子类可以选择覆盖它。< / p >
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的抽象实现,AbstractMessageSource具有解析消息的通用实现,并公开了resolveCode()抽象方法,以便子类可以以自己的方式实现它们。AbstractMessageSource是模板方法的一个例子。

http://docs.spring.io/spring/docs/4.1.7.RELEASE/javadoc-api/org/springframework/context/support/AbstractMessageSource.html

在此设计模式的模板方法中,子类可以覆盖一个或多个算法步骤,以允许不同的行为,同时确保仍然遵循总体算法(Wiki)。

模式名Template方法的意思是它是什么。假设我们有一个方法calculatessomething(),我们想要创建这个方法的模板。此方法将在基类中声明为非虚方法。假设这个方法是这样的。

CalculateSomething(){
int i = 0;
i = Step1(i);
i++;
if (i> 10) i = 5;
i = Step2(i);
return i;
< p >} Step1和Step2方法实现可以由派生类给出

在策略模式中,基类没有提供实现(这就是为什么基类实际上是类图中的接口)。

经典的例子是排序。根据需要排序的对象数量,创建适当的算法类(merge, bubble, quick等),并将整个算法封装在每个类中。

现在我们可以将排序实现为模板方法了吗?当然可以,但是您不会发现有太多/任何共性可以抽象出来并放置在基本实现中。因此,它违背了模板方法模式的目的。

 different between Strategy and Template Method Pattern Strategy vs Template Method .


相似之处

策略和模板方法模式之间有很多相似之处。策略和模板方法模式都可以用于满足开闭原则,并使软件模块易于扩展而无需更改其代码。这两种模式都表示通用功能与该功能的详细实现的分离。但是,它们在提供的粒度方面略有不同。


差异

以下是我在研究这两种模式时观察到的一些差异:

  1. 在Strategy中,客户端与策略的耦合更多 而在模板方法中,两个模块更紧密 李耦合。< / > 在Strategy中,大多数接口是通过抽象类can来使用的 也要视情况而定,而具体类是不用的 而在模板方法中使用的大多是抽象类或具体类
  2. . . 在策略模式中,一般来说,整个类的行为是 另一方面,使用Template方法 中定义了样板代码,以减少代码重复 基框架或抽象类。在模板方法中,甚至可以有一个具体的类
  3. 简单来说,你可以改变整个策略(算法) 不过,策略模式在模板方法中只有一些东西 改变(部分算法),其余的东西保持不变。在模板方法中,不变步骤在抽象基类中实现,而 变量步骤要么给出一个默认实现,要么没有 根本不需要执行。在模板方法中,组件设计器 的顺序 步骤,但允许组件客户端扩展或替换一些

图片来自一口体积博客。

策略设计模式

  • 支持组成。
  • 为您提供在运行时更改对象行为的灵活性。
  • 减少客户端代码和解决方案/算法代码之间的耦合。

模板方法设计模式

  • 更喜欢继承而不是组合
  • 在基类中定义算法。算法的各个部分可以在子类中定制。

我认为主要的区别是,有了模板,你需要一个算法来做一些事情,但让我们说,在算法的中间,你想要运行不同的行为,所以你可以发送一个接口的实现,使算法在运行时动态。

但是对于策略,你实际上有完全不同的算法执行而不仅仅是算法的一个变体,然后你选择运行哪个算法,但是模板你只有一个变体的算法。

最后,你可以实现你想要的,使用模板作为策略,反之亦然,但我看到了区别。

模板方法模式善于阐明算法的整体步骤,而策略模式适合灵活性和可重用性,因此如果需要,可以将策略组合在一起,例如:jdk8中的许多功能接口,如Comparator.reversed().thenComparing(Comparator)是策略的一个角色。

模板方法模式侧重于更高的内聚性,而策略模式则与上下文对象松散耦合以分离关注点。

策略易于维护,因为上下文不知道具体的策略,无论主要算法在上下文中发生什么变化都不会影响策略。另一方面,如果在抽象模板类中改变算法的框架,可能会影响其子类的升级。

它们都是不同的技术来达到相同的结果,所以问题是在什么时候使用哪一种。

  • 如果你正在使用一个框架或库,你不能访问源代码,你想改变一个类的一些行为,所以你必须选择模板方法。这仅仅意味着继承。
  • 如果您正在开发一个类,并且很明显需要以不同的方式实现逻辑的某些部分以处理各种情况,则采用Strategy模式。策略比模板法更可靠。它涵盖了依赖倒置和打开/关闭原则。因此,它是可扩展的,而且易于测试。
  • 如果您正在开发一个类,并且您不知道将来会发生什么变化,请将您的逻辑尽可能地划分为独立的和单一的负责函数。只是这一点。(既不是模板方法也不是策略)。