模板方法模式和策略模式你真的知道有什么不同吗?

最近在重构自己的代码,所以学习到了模板方法模式和策略模式,但是用过两个设计模式之后,发现两者在使用的过程中不是很容易区分,可能很多人会有疑问两者有什么区别。下面通过一个例子来解释两者的区别。

模板方法模式

模板方法模式是一种行为设计模式,用于定义算法的框架结构,而将一些步骤的具体实现延迟到子类中。这使得子类可以在不改变算法结构的情况下重新定义算法的某些步骤。

下面看一个例子:

一个经典的例子是制作咖啡和茶。在这个例子中,制作咖啡和茶的过程有一些共同的步骤,但也有一些不同之处。

// 抽象类
abstract class Beverage {
    // 模板方法,定义了制作饮料的流程
    public final void prepareBeverage() {
        boilWater();
        brew();
        pourInCup();
        addCondiments();
    }

    // 具体方法
    private void boilWater() {
        System.out.println("Boiling water");
    }

    // 抽象方法,由子类实现
    protected abstract void brew();

    // 具体方法
    private void pourInCup() {
        System.out.println("Pouring into cup");
    }

    // 抽象方法,由子类实现
    protected abstract void addCondiments();
}

// 具体子类
class Coffee extends Beverage {
    protected void brew() {
        System.out.println("Dripping coffee through filter");
    }

    protected void addCondiments() {
        System.out.println("Adding sugar and milk");
    }
}

// 具体子类
class Tea extends Beverage {
    protected void brew() {
        System.out.println("Steeping the tea");
    }

    protected void addCondiments() {
        System.out.println("Adding lemon");
    }
}

// 客户端代码
public class Main {
    public static void main(String[] args) {
        Beverage coffee = new Coffee();
        System.out.println("Making coffee...");
        coffee.prepareBeverage();

        System.out.println();

        Beverage tea = new Tea();
        System.out.println("Making tea...");
        tea.prepareBeverage();
    }
}

在上面的示例中,Beverage 类是一个抽象类,其中包含了模板方法 prepareBeverage 和一些具体方法。CoffeeTea 类是具体的子类,它们分别实现了 brewaddCondiments 方法。

当客户端代码调用 prepareBeverage 方法时,它会依次调用抽象方法和具体方法,从而完成制作咖啡或茶的过程。这样,制作咖啡和茶的过程的框架结构是固定的,但具体的步骤可以在子类中进行定制。

可以看到,我们使用模板方法模式的时候,通常会定义一个模板方法,也就是通用的方法,该方法中包括了具体的一些业务逻辑,但是这个逻辑对于任何一个子类来说都是通用的。例如做咖啡和茶的过程。

策略模式

策略模式是一种行为设计模式,它允许在运行时选择算法的行为。这种模式定义了一系列算法,将每个算法封装起来,并使它们可以互相替换。策略模式使得算法可以独立于使用它们的客户端而变化。

当谈到策略模式时,一个常见的例子是电商系统中的支付方式。在电商系统中,用户可以选择不同的支付方式(如信用卡支付、支付宝支付、微信支付等),每种支付方式都有自己的支付算法。这种情况下,可以使用策略模式来实现不同支付方式的灵活切换。

// 策略接口
interface PaymentStrategy {
    void pay(int amount);
}

// 具体支付策略:信用卡支付
class CreditCardPayment implements PaymentStrategy {
    public void pay(int amount) {
        System.out.println("Paid " + amount + " using credit card");
        // 具体的信用卡支付逻辑
    }
}

// 具体支付策略:支付宝支付
class AlipayPayment implements PaymentStrategy {
    public void pay(int amount) {
        System.out.println("Paid " + amount + " using Alipay");
        // 具体的支付宝支付逻辑
    }
}

// 上下文Context
class ShoppingCart {
    private PaymentStrategy paymentStrategy;

    public void setPaymentStrategy(PaymentStrategy paymentStrategy) {
        this.paymentStrategy = paymentStrategy;
    }

    public void checkout(int amount) {
        paymentStrategy.pay(amount);
    }
}

// 客户端代码
public class Main {
    public static void main(String[] args) {
        ShoppingCart cart = new ShoppingCart();

        // 选择信用卡支付方式
        cart.setPaymentStrategy(new CreditCardPayment());
        cart.checkout(100);

        // 切换为支付宝支付方式
        cart.setPaymentStrategy(new AlipayPayment());
        cart.checkout(200);
    }
}

在上面的例子中,PaymentStrategy 是策略接口,定义了支付的抽象方法。CreditCardPaymentAlipayPayment 是具体的支付策略,分别实现了支付接口。ShoppingCart 是上下文,持有一个支付策略的引用,并在需要时调用支付策略来完成支付。

通过使用策略模式,客户端可以在运行时选择不同的支付方式,而不需要修改购物车类的代码。这种灵活性使得系统更易于扩展和维护。

区别

其实通过上面两个例子我们可以发现,模板方法模式和策略模式都是为了代码的复用。

模板方法模式注重的是算法的模板,关注算法的整体结构和流程,其实主要是一种算法的结构,可能不同的实现方式有略微不同的差别。

策略模式关注算法的可替换性和灵活性,就是对于某种特定场景,可以使用多个算法来解决,我们根据需要选择其中的一种,这种就是策略模式应用的场景,它着重于定义不同算法的接口和实现,并使其可以互换使用。

两者本质的区别,策略是对行为对象封装不同的算法; 模板是行为算法相似,封装算法中用到的不同对象。

我们在使用中会发现,策略模式更强调运行时策略是可变更的,而模板方法更强调子类达到父类的功能。

策略模式关注多种算法,模板模式关注一种算法。策略模式不同策略只间代码很少冗余。

总的来说,模板方法模式用于定义算法的整体结构,而策略模式用于在运行时选择算法的行为。这两种模式在不同的场景下有着各自的优势和应用价值。

其实在我们的使用过程中,可能一个场景下需要用到两种算法,因为模板方法其实就是把公共流程拿出来,策略方法注重算法的切换,所以说两者共同使用并不冲突。

例如,上面的不同支付方式的例子,假如说支付宝支付和信用卡支付有一部分流程是相同的,我们仍然可以使用模板方法模式将其抽象出来,提供一个抽象类,里面实现一些公共的流程,一些具体的逻辑由子类实现。

同时我们还想要灵活切换算法,那么我们再用策略模式将其包装即可。

总结

两者设计模式都是在开发中常用的模式,每个模式的关注点不同,模板方法模式注重的是算法的模板,策略模式关注算法的可替换性和灵活性,但是两者可以结合起来使用。

更多推荐