模板方法和策略模式有什么区别?


161

有人可以告诉我模板方法模式和策略模式有什么区别吗?

据我所知,它们是99%相同-唯一的区别是模板方法模式具有一个抽象类作为基类,而策略类则使用由每个具体策略类实现的接口。

但是,就客户而言,它们的消费方式完全相同-这是正确的吗?


2
这篇文章中的SO有同样的问题更好的答案: stackoverflow.com/questions/464524/...
Gob00st

12
gob00st链接到的问题是策略和桥接之间的区别。这根本不是这个问题的答案。
bluekeys

Answers:


135

两者之间的主要区别在于何时选择了具体算法。

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

综上所述:

  • 模板方法模式:通过子类选择编译时算法
  • 策略模式:按包含选择运行时算法

47
两种模式都支持对所使用算法的运行时选择(对于模板方法,您将执行if (config.useAlgoA) impl = new AlgoA() else impl = new AlgoB()),因此此答案不正确。
Borek Bernard 2010年

13
当然可以,但是您没有使用模板模式。实际上,这几乎就是创建Strategy实例的代码的样子!
thehouse

21
-1,我认为这个答案(虽然不是完全错误的)错过了真正的区别所在。@tvanfosson的答案要好得多。
布朗

1
@Karoly Nyisztor他们都可以“替换行为”和“提供扩展点”。不管是行为还是扩展,这实际上取决于您应用给定模式的环境。您也可以将模板方法模式的每个子类称为“策略”,也可以将策略模式中的每个策略类称为“扩展”,仅是措辞。事实是他们做同样的事情,除了这个答案提到的差异。因此,这是正确的答案。
安迪

1
对于两种模式,以相同的方式选择具体算法。通过调用new ConcreteAlgorithm1()与进行选择new ConcreteAlgorithm2()。显然,选择是在运行时发生的(在编译时选择算法就意味着要对其进行硬编码)。两者之间的主要区别在于具体算法的实现方式。它是作为子类还是作为单独的接口实现的?前者是模板。后者是一种策略。差异可以概括为组合与继承,这是GoF书中的常见主题。
jaco0646

138

当特定操作具有一些可以根据其他变化的原始行为定义的不变行为时,将使用模板模式。抽象类定义不变的行为,而实现类定义依赖的方法。

在策略中,行为实现是独立的-每个实现类都定义行为,并且它们之间没有共享的代码。两者都是行为模式,因此,客户端以几乎相同的方式使用它们。通常,策略只有一个公共方法- execute()方法,而模板可以定义一组公共方法以及子类必须实现的一组支持私有原语。

这两种模式很容易一起使用。您可能有一个策略模式,其中多个实现属于使用模板模式实现的一系列策略。


这听起来对我来说是正确的,但是,为什么WikiPedia提到“策略模式是在运行时选择算法的行为”?它也可以用来选择算法在编译时的行为,就像模板方法一样?我想念什么吗?
BornToCode 2015年

2
@BornToCode我想假设他们在说的是在运行时选择特定的策略。例如,有几种方法可以用数字方式找到方程式的根。根据问题域或数据,您可以选择Newton-Raphson,Euler或其他用于求解方程的策略。其中的每一项都是一项策略。较大的算法(解决方程式的一部分)是一种算法,它根据问题的某些性质来选择要采用的策略。
tvanfosson 2015年

是的,但这不是仅在这些情况下才应使用策略模式吗?我的意思是,如果只需要在编译时选择算法的行为,我是否仍应使用策略模式,否则就不是要使用它吗?
BornToCode 2015年

1
@BornToCode我想说的是,动态选择策略最有用。模板基本上是一种建立已知的不同相关行为的方法。您将使用某种策略(虽然不一定是策略模式)来选择采用哪种模板化行为。例如,产品继承-您将创建基本产品,为其他产品添加功能。选择要实例化的产品类型(类)可能取决于从哪个表/视图加载。战略模式并没有真正发挥作用。
tvanfosson 2015年

2
@BornToCode这不是一个非此即彼的东西,它是肯定的。在适当的地方应用模式,在有用的地方组合模式。
tvanfosson 2015年


24

您可能是指模板方法模式。没错,它们满足非常相似的需求。我要说的是,如果您有一个“模板”算法定义了步骤的情况,最好使用模板方法,其中子类会覆盖这些步骤以更改一些细节。在采取策略的情况下,您需要创建一个接口,而不是继承,而是在使用委派。我会说这是一个更强大的模式,按照DIP(依赖倒置原则),可能会更好。它功能更强大,因为您可以清楚地定义新的策略抽象-一种做事的方式,不适用于模板方法。因此,如果这种抽象有意义,请使用它。但是,使用模板方法可以在简单的情况下为您提供更简单的设计,这也很重要。考虑哪个词更合适:你有模板算法吗?还是这里的关键是您有一个抽象的策略-做某事的新方法

模板方法的示例:

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

在这里,您从应用程序继承,并替换了在init,运行和完成时将要执行的操作。

策略示例:

array.sort (IComparer<T> comparer)

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


3
我认为这是一个很好的答案
Calanus

23

策略与模板方法模式之间的区别策略与模板方法


相似之处

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


差异性

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

  1. 在策略中,客户端与策略之间的耦合更为宽松,而在模板方法中,两个模块之间的耦合更为紧密。
  2. 在Strategy中,虽然根据情况也可以使用抽象类,但主要使用接口,并且不使用具体类,而在Template方法中,主要使用抽象类或具体类,而不使用接口。
  3. 在策略模式中,通常用接口表示类的整个行为,而使用Template方法来减少代码重复,并且在基础框架或抽象类中定义样板代码。在Template Method中,甚至可以有一个带有默认实现的具体类。
  4. 简而言之,您可以在“策略”模式中更改整个策略(算法),但是在“模板”方法中,仅某些更改(算法的一部分),其余的保持不变。在Template Method中,不​​变步骤是在抽象基类中实现的,而变异步骤要么是默认实现,要么根本没有实现。在模板方法中,组件设计者要求算法的必需步骤和步骤的顺序,但允许组件客户端扩展或替换其中一些步骤。

图片取自被咬的博客。


19

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

这个问题显示了选择之间的一些权衡:继承与聚合


11

两者非常相似,并且都以相似的方式被客户端代码使用。与上面最流行的答案不同,两者都允许在运行时选择算法

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

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


9

模板方法:

  1. 它基于继承
  2. 定义子类无法更改的算法框架。子类中只能覆盖某些操作
  3. 父类完全控制算法,并且仅对某些特定步骤有所不同
  4. 绑定在编译时完成

Template_method结构:

在此处输入图片说明

战略:

  1. 基于委派/组成
  2. 通过修改方法行为来改变对象胆量
  3. 用于在一系列算法之间切换
  4. 通过在运行时将一种算法完全替换为另一种算法,它可以在运行时改变对象的行为
  5. 绑定在运行时完成

策略结构:

在此处输入图片说明

参阅“模板方法”和“ 策略”文章,以更好地理解。

相关文章:

JDK中的模板设计模式找不到用于定义要按顺序执行的方法集的方法

战略模式的真实例子


3

不,它们不一定以相同的方式消耗。“模板方法”模式是向未来的实施者提供“指导”的一种方式。您正在告诉他们,“所有对象对象都必须具有社会安全号码”(这是一个简单的示例,但可以正确地理解这个想法)。

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

因此,有时,客户实际上会告诉对象使用哪种策略。如

myShippingCalculator.CalculateTaxes(myCaliforniaSalesTaxImpl);

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


3

我建议您阅读这篇文章。它在一个实际案例中解释了差异。

引用文章

可以看到,实现类也取决于模板方法类。如果要更改算法的某些步骤,则这种依赖关系会导致模板方法发生变化。另一方面,策略完全封装了算法。它给出了实现的方法。类来完全定义一种算法,因此,如果有任何更改,确实需要更改先前编写的类的代码,这就是我选择设计类策略的主要原因。

模板方法的一个特征是模板方法控制算法。在其他情况下这可能是一件好事,但是在我的问题中,这限制了我设计类的时间。另一方面,策略无法控制算法的步骤,这使我可以添加完全不同的转换方法。因此,就我而言,战略可以帮助我实施。

该策略的缺点之一是太多的代码冗余和更少的代码共享。显而易见,在本文的示例中,我必须一次又一次地在四个类中重复相同的代码。因此很难维护,因为如果更改了我们通用的系统(如步骤4)的实现,那么我将不得不在所有5个类中对其进行更新。另一方面,在模板方法中,我只能更改超类,并且更改将反映到子类中。因此,模板方法提供了非常低的冗余量,并且在类之间提供了大量的代码共享。

策略还允许在运行时更改算法。在模板方法中,必须重新初始化对象。策略的此功能提供了很大的灵活性。从设计的角度来看,人们必须更喜欢组合而不是继承。因此,采用战略模式也成为发展的首要选择。”


2

模板模式类似于策略模式。这两种模式的范围和方法不同。

策略用于允许呼叫者更改整个算法,例如如何计算不同类型的税费,而模板方法用于更改算法中的步骤。因此,策略的粒度更粗。模板允许在后续操作中进行更细粒度的控制,但允许更改这些详细信息的实现。

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

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


2

在策略模式中,子类正在运行节目并控制算法。这里的代码在子类之间重复。该算法的知识及其实现方法分布在许多类中。

在模板模式中,基类具有算法。它使子类之间的重用最大化。由于算法位于一个位置,因此基类对其进行保护。


2

策略设计模式

  • 支持合成。
  • 使您可以灵活地在运行时更改对象的行为。
  • 客户代码与解决方案/算法代码之间的耦合较少。

模板方法设计模式

  • 优先考虑继承而不是组成
  • 在基类中定义算法。可以在子类中自定义算法的各个部分。

1

模板模式:

模板方法是让子类重新定义算法的某些步骤,而不更改基类中定义的算法的主要结构和步骤。模板模式通常使用继承,因此可以在基类中提供算法的通用实现,子类可以根据需要选择重写。

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 存储库


0

策略作为抽象类作为接口和模板方法公开。通常在框架中使用很多。例如,Spring框架的MessageSource类是用于解析消息的策略接口。客户端使用此接口的特定实现(策略)。

以及同一接口AbstractMessageSource的抽象实现,该接口具有解析消息的通用实现,并公开resolveCode()抽象方法,以便子类可以按其方式实现它们。AbstractMessageSource是模板方法的示例。

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


0

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

模式名称Template方法的含义是什么。假设我们有一个方法CalculateSomething(),并且希望对该方法进行模板化。该方法将在基类中声明为非虚拟方法。说方法看起来像这样。

CalculateSomething(){
    int i = 0;
    i = Step1(i);
    i++;
    if (i> 10) i = 5;
    i = Step2(i);
    return i;

} Step1和Step2方法的实现可以通过派生类给出。

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

经典示例是排序。根据需要分类的对象数量,创建适当的算法类(合并,气泡,快速等),并将整个算法封装在每个类中。

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.