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

时间:2009-03-21 13:06:13

标签: design-patterns strategy-pattern template-method-pattern

有人可以向我解释模板方法模式和策略模式之间的区别是什么?

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

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

19 个答案:

答案 0 :(得分:118)

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

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

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

总结:

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

答案 1 :(得分:116)

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

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

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

答案 2 :(得分:22)

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

模板方法示例:

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

在这里,您从应用程序继承并替换将在init,run和done上完成的操作。

策略示例:

array.sort (IComparer<T> comparer)

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

答案 3 :(得分:22)

我认为两种模式的类图都显示出差异。

<强>策略
将算法封装在类中 Link to image enter image description here

模板方法
将算法的确切步骤推迟到子类
Link to Image enter image description here

答案 4 :(得分:19)

Difference between Strategy and Template Method Pattern Strategy vs Template method

相似性

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

差异

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

  1. 在战略中,客户与战略之间的耦合更多 松散而在模板方法中,两个模块更紧密 耦合。
  2. 在策略中,虽然抽象类可以使用大多数接口 也可以根据具体情况使用,具体类不是 在Template方法中使用的主要是抽象类或具体 使用类,不使用接口。
  3. 在策略模式中,通常整个类的行为是 另一方面,使用模板方法表示接口 减少代码重复,并在中定义样板代码 基础框架或抽象类。在Template Method中,甚至可以有一个具体的类 默认实施。
  4. 简单来说,您可以更改整个策略(算法) 然而,在Template方法中,策略模式只有一些东西 改变(算法的一部分)和其余的东西保持不变。在Template Method中,不​​变步骤在抽象基类中实现,而 变体步骤要么是默认实现,要么是否 实施完全。在Template方法中,组件设计器 强制要求算法的必要步骤,以及算法的顺序 步骤,但允许组件客户端扩展或替换一些 这些步骤的数量。
  5. 图片来自bitesized博客。

答案 5 :(得分:16)

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

这个问题显示了选择之间的一些权衡:Inheritance vs. Aggregation

答案 6 :(得分:10)

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

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

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

答案 7 :(得分:5)

模板方法:

  1. 它基于继承
  2. 定义无法通过子类更改的算法框架。只能在子类中覆盖某些操作
  3. 父类完全控制算法,并且仅将某些步骤与具体类区别开来
  4. 绑定在编译时完成
  5. Template_method结构:

    enter image description here

    策略:

    1. 它基于委托/撰写
    2. 通过修改方法行为
    3. 来更改对象的内容
    4. 它曾经在算法族之间切换
    5. 它在运行时通过完全在运行时用其他算法替换一个算法来改变对象的行为
    6. 绑定在运行时完成
    7. Strategy结构:

      enter image description here

      查看Template methodStrategy条款,以便更好地理解。

      相关帖子:

      Template design pattern in JDK, could not find a method defining set of methods to be executed in order

      Real World Example of the Strategy Pattern

答案 8 :(得分:3)

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

引用文章

  

正如人们所见,实现类也依赖于模板   方法类。此依赖关系导致更改模板方法if   一个人想要改变算法的一些步骤。在另一   边策略完全封装了算法。它给了   实现类以完全定义算法。因此如果   任何更改到达之前需要更改以前的代码   书面课。这是我选择战略的主要原因   设计课程。

     

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

     

策略的一个缺点是代码冗余太多了   更少的代码共享。正如在此示例中显而易见的那样   文章我必须再次在四个类中重复相同的代码   再次。因此很难维护,因为如果实施   我们的系统,例如所有人共同的步骤4,然后我改变了   将不得不在所有5个类中更新它。另一方面,在   模板方法,我只能更改超类和更改   反映到子类中。因此模板方法非常有用   冗余量少,代码共享量大   类。

     

策略还允许在运行时更改算法。在模板中   方法一必须重新初始化对象。这个特点   战略提供了大量的灵活性。从设计角度来看   视图一个人喜欢组合而不是继承。因此使用   战略模式也成为发展的首选。“

答案 9 :(得分:3)

不,它们不一定以同样的方式消费。 “模板方法”模式是向未来实施者提供“指导”的一种方式。你告诉他们,“所有人物对象必须有一个社会安全号码”(这是一个简单的例子,但它可以正确理解)。

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

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

myShippingCalculator.CalculateTaxes(myCaliforniaSalesTaxImpl);

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

答案 10 :(得分:2)

在策略模式中,子类正在运行show并且它们控制着算法。这里代码在子类中重复。算法的知识以及如何实现它分布在许多类中。

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

答案 11 :(得分:2)

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

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

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

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

答案 12 :(得分:2)

  

战略设计模式

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

模板方法设计模式

  • 优先于作品继承
  • 在基类中定义算法。可以在子类中定制各个算法。

答案 13 :(得分: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 repository

答案 14 :(得分:0)

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

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

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

答案 15 :(得分:0)

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

模式名称模板方法意味着它是什么。假设我们有一个方法CalculateSomething(),我们想要模板化这个方法。此方法将在基类中声明为非虚方法。假设方法看起来像这样。

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

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

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

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

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

答案 16 :(得分:0)

我认为主要的区别在于,使用模板时,您需要一个算法来做某事,但是让我们说在该算法的中间您想要运行不同的行为,以便您可以发送接口的实现以动态地使该算法在运行时。

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

最后,您可以根据需要实施并使用模板作为策略,反之亦然,但我看到了不同之处。

答案 17 :(得分:0)

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

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

策略易于维护,因为上下文没有关于具体策略的知识,每当上下文中的主要算法发生变化时,都不会影响策略。另一方面,如果你在抽象模板类中改变了算法的骨架,可能会影响其子类的升级。

答案 18 :(得分:0)

它们都是实现相同结果的不同技术,所以问题是在何时使用。

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