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

211

请问有人能解释一下模板方法模式和策略模式的区别吗?

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

然而,在客户端看来,它们以完全相同的方式被调用 - 这样正确吗?


2
这篇SO的帖子对于同样的问题有更好的答案:https://dev59.com/a3RB5IYBdhLWcg3w7riV - RoundPi
22
gob00st提供的链接是关于战略和桥牌之间区别的问题,与这个问题无关。请注意,这不是对这个问题的回答。 - bluekeys
19个回答

3
在策略模式中,子类运行程序并控制算法。在这里,代码在子类中重复出现。算法的知识和如何实现它都分布在多个类中。
在模板模式中,基类拥有算法。它最大化了子类间的重用性。由于算法只存在于一个地方,基类可以保护它。

2

模板模式:

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

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


2

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

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

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

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


2

策略设计模式

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

模板方法设计模式

  • 偏向于继承而非组合。
  • 在基类中定义算法。可以在子类中定制算法的各个部分。

2

两种技术都是实现相同结果的不同方式,所以问题在于何时使用哪种技术。

  • 如果你正在使用一个没有源代码访问权限的框架或库,而你想要更改某个类的一些行为,那么你必须选择模板方法。这意味着继承。
  • 如果你正在开发一个类并且显然某些逻辑的某些部分需要以不同的方式实现以处理各种情况,请使用策略模式。策略模式比模板方法更符合SOLID原则。它涵盖了依赖反转和开放/关闭原则。因此,它是可扩展的,也易于测试。
  • 如果你正在开发一个类,并且你不知道将来会发生什么变化,请尽量将你的逻辑划分为单独的、单一的负责函数。就是这样。(既不是模板方法也不是策略模式)。

0
在这个设计模式的模板方法中,一个或多个算法步骤可以被子类重写,以允许不同的行为,同时确保仍然遵循总体算法(维基百科)。
模式名称“模板方法”意味着它是什么。假设我们有一个方法CalculateSomething(),我们想要将此方法模板化。该方法将在基类中声明为非虚拟方法。假设该方法看起来像这样。
CalculateSomething(){
    int i = 0;
    i = Step1(i);
    i++;
    if (i> 10) i = 5;
    i = Step2(i);
    return i;

Step1和Step2方法的实现可以由派生类提供。

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

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

现在我们能否将排序实现为模板方法?当然可以,但您不会找到太多/任何通用性可抽象出并放置在基本实现中。因此,这违背了模板方法模式的目的。


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

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

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

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


0

我认为主要的区别在于模板需要一个执行某些操作的算法,但是假设在该算法中间你想运行不同的行为,那么你可以发送一个接口的实现来使该算法在运行时动态生成。

但是使用策略模式,你实际上有完全不同的算法执行,而不仅仅是算法的变量,然后你选择要运行哪个算法,但是模板只有一种带有变体的算法。

最终,你可以按照自己的意愿进行实现并将模板用作策略,反之亦然,但我认为存在区别。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接