策略模式

点击阅读:设计模式系列文章


1. 策略模式定义

策略模式(Strategy):它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。

策略模式封装了变化,定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。策略模式就是用来封装算法的,可以用它来封装几乎任何类型的规则,只要遇到需要在不同时间应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性。避免了在有多种算法相似的情况下,使用 if...else 所带来的复杂和难以维护。

2. 策略模式结构

  1. Strategy 类:定义所有支持的算法的公共接口:

    java 复制代码
    public abstract class Strategy{
        public abstract void algorithmInterface();
    }
  2. ConcreteStrategy 类:封装了具体的算法,继承Strategy 类:

    java 复制代码
    class ConcreteStrategyA extends Strategy {
        public void algorithmInterface(){
            sout("算法A实现");
        }
    }
    
    class ConcreteStrategyB extends Strategy {
        public void algorithmInterface(){
            sout("算法B实现");
        }
    }
    
    class ConcreteStrategyC extends Strategy {
        public void algorithmInterface(){
            sout("算法C实现");
        }
    }
  3. Context 类:用一个 ConcreteStrategy 来配置,维护一个 Strategy 对象的引用:

    java 复制代码
    class Context {
        Strategy strategy;
        public Context(Strategy strategy){
            this.strategy = strategu;
        }
        public void contextInterface(){
            strategy.algorithmInterface();
        }
    }

客户端代码调用:

java 复制代码
Context context;

context = new Context(new ConcreteStrategyA());
context.contextInterface();

context = new Context(new ConcreteStrategyB());
context.contextInterface();

context = new Context(new ConcreteStrategyC());
context.contextInterface();

3. 策略模式举例

3.1 问题背景

设计一个程序,通过不同的方式进行支付。

很容易想到,先设计一个支付类:

java 复制代码
public class PaymentMethod {
    public static void pay(String paymentType, int amount){
        switch(paymentType){
            case "alipay":
                sout("使用支付宝进行支付 " + amount +" 元");
                break;
            case "wechatpay":
                sout("使用微信进行支付 " + amount +" 元");
                break;
        }
    }
}

客户端代码:

java 复制代码
Scanner sc = new Scanner(System.in);
sout("请输入支付方式(alipay、wechatpay):");
String paymentType = sc.nextLine();
sout("请输入支付金额:");
double amount = sc.nextLine();
PaymentMethod.pay(paymentType, amount);

3.2 简单工厂模式实现

如果想要增加或者修改支付方式,此时必须修改 PaymentMethod 类,这时可能就会影响到其他代码,可能不小心将其他代码进行了修改。应该将不同的支付方式进行分离,修改一个而不影响另外几个,增加支付方式也不影响其他代码。

使用简单工厂模式进行实现(点击阅读:简单工厂模式详解):

定义 Payment 支付接口:

java 复制代码
interface PaymentMethod {
    void pay(int amount);
}

支付宝、微信、信用卡支付类:

java 复制代码
public class AliPay extends PaymentMethod{
    @Override 
    public void pay(int amount) { 
        System.out.println("Paid " + amount + " using AliPay"); 
    }
}

public class WeChatPay extends PaymentMethod{
    @Override 
    public void pay(int amount) { 
        System.out.println("Paid " + amount + " using WeChatPay"); 
    }
}

public class CreditCardPay implements PaymentMethod { 
    private String cardNumber; 
    
    public CreditCardPay(String cardNumber) { 
        this.cardNumber = cardNumber; 
    } 
    
    @Override 
    public void pay(int amount) { 
            System.out.println("Paid " + amount + " using Credit Card ending with " + cardNumber); 
    } 
}

工厂类:

java 复制代码
public class PaymentFactory {
    public static PaymentMethod createPayment(String paymentType,String cardNumber){
        switch(paymentType.toLowerCase()) {
            case "alipay":
                return new AliPay();
                break;
            case "wechatpay":
                return new WeChatPay();
                break;
            case "creditcardpay":
                return new CreditCardPay(cardNumber);
                break;
            default: 
                throw new IllegalArgumentException("Invalid payment type: " + paymentType); }
        }
    }
}

客户端代码;

java 复制代码
PaymentMethod paymentMethod = PaymentFactory.createPayment(paymentType, cardNumber);
paymentMethod.pay(100);

3.3 策略模式实现

使用简单工厂模式时,假设想要增加或者修改一个支付方式,需要增加一个类继承 PaymentMethod 类,并且需要在 PaymentFactory 中增加一个 case。每次改动都需要修改工厂。

其实支付方式都是一些算法,用工厂来生成算法对象,算法本身就是一种策略,这些算法随时都可能相互替换。

简单工厂模式中的 PaymentMethod 类就是抽象策略,支付宝、微信、信用卡支付类就是三个具体策略。

将简单工厂模式进行修改为策略模式:

java 复制代码
public class PaymentMethodContext {
    private PaymentMethod paymentMethod;
    
    public PaymentMethodContext(PaymentMethod paymentMethod){
        this.paymentMethod = paymentMethod;
    }
    
    public void pay(int amount){
        return this.paymentMethod.pay(amount);
    }
}

客户端代码:

java 复制代码
PaymentMethodContext paymentMethodContext;

swith(paymentType.toLowerCase()){
    case "alipay":
        paymentMethodContext = new PaymentMethodContext(new AliPay());
        break;
    case "wechatpapy":
        paymentMethodContext = new PaymentMethodContext(new WeChatPay());
        break;
    case "*":
        paymentMethodContext = new PaymentMethodContext(new CreditCardPay());
        break;
    default: 
        throw new IllegalArgumentException("Invalid payment type: " + paymentType); }
}

paymentMethodContext.pay(100);

再看一次策略模式定义:

策略模式它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。

策略模式关注的是算法的封装和替换,强调的是算法的多态性,让客户端能够选择不同的算法。

3.4 策略模式与简单工厂的结合

将判断的过程从客户端转移:

java 复制代码
public class PaymentMethodContext {
    private PaymentMethod paymentMethod;
    
    public PaymentMethodContext(String paymentType, String creditCardNumber){
        swith(paymentType.toLowerCase()){
            case "alipay":
                paymentMethod = new AliPay();
                break;
            case "wechatpapy":
                paymentMethod = new WeChatPay();
                break;
            case "*":
                paymentMethod = nnew CreditCardPay(creditCardNumber);
                break;
            default: 
                throw new IllegalArgumentException("Invalid payment type: " + paymentType); }
        }
    }
    
    public void pay(int amount){
        return this.paymentMethod.pay(amount);
    }
}

客户端代码;

java 复制代码
PaymentMethodContext paymentMethodContext = new PaymentMethodContext(paymentType, creditCardNumber);
paymentMethodContext.pay(amount);

简单工厂模式与策略模式客户端代码对比:

java 复制代码
// 简单工厂模式
PaymentMethod paymentMethod = PaymentFactory.createPayment(paymentType, cardNumber);
paymentMethod.pay(100);
// 策略模式
PaymentMethodContext paymentMethodContext = new PaymentMethodContext(paymentType, creditCardNumber);
paymentMethodContext.pay(amount);

对于简单工厂模式,客户端需要认识两个类 PaymentFactory 和 PaymentMethod。

对于策略模式和简单工厂结合:客户端只需认识一个类 PaymentMethodContext。耦合度更低,具体的算法彻底地与客户端分离。

策略模式和简单工厂模式的应用场景:

  1. 策略模式:主要用于使算法的变化独立于使用算法的客户。策略模式用于在运行时选择不同的算法变体。

    如果需要根据不同情况使用不同的业务规则,那么策略模式更为合适。

  2. 简单工厂模式:目的在于创建对象。它将对象的创建封装起来,使得客户端在实例化对象时不需要知道具体的类。

    如果需要根据一定的逻辑实例化不同的对象类,但不关心具体的实现细节,那么简单工厂模式更为合适。

4. 策略模式的优点和缺点

4.1 优点

  1. 分离关注点:策略模式将定义算法的代码从使用算法的代码中分离出去,符合单一职责原则。
  2. 提高可扩展性:可以在不修改客户代码的情况下引入新的策略。
  3. 替换灵活性:策略模式允许在运行时更改对象的行为。
  4. 复用策略:策略可以被多个上下文复用。

4.2 缺点

  1. 客户端需要了解不同策略:策略模式强制客户端选择一个具体策略,因此客户端需要了解每个策略的差异。

    解决方法:可以通过使用一个配置文件或工厂模式来创建策略对象,将具体策略类的创建逻辑从客户端代码中抽离。

  2. 增加对象数量:每一个策略都是一个对象,如果策略过多会导致很多策略类。

    解决方法:可以通过使用享元模式来缓存和复用策略对象,以减少实例创建。

  3. 协作策略问题:如果策略需要协作产生较大的数据结构交互,这将使策略使用变得复杂。

    解决方法:可以通过引入中介类来封装那些交互,从而简化策略类之间的通信复杂性。


点击阅读:设计模式系列文章

相关推荐
秋凉 づᐇ2 小时前
每日一题------面试
面试·职场和发展
钢板兽2 小时前
力扣hot100二刷——链表
后端·算法·leetcode·链表·面试
池鱼ipou2 小时前
春招面试拷打实录(一):揭秘高频难题与应对策略🧐
前端·vue.js·面试
3 小时前
📌 面试答不上的问题 - WebSocket通信
前端·面试
哦baby3 小时前
《HarmonyOS导航深度解析:从传统路由到声明式导航》
前端·设计模式
找了一圈尾巴3 小时前
设计模式-观察者模式、状态模式
观察者模式·设计模式·状态模式
seven97_top3 小时前
【设计模式】探索状态模式在现代软件开发中的应用
java·设计模式·状态模式
只会写Bug的程序员4 小时前
面试之《实现Event Bus》
前端·面试
钢板兽5 小时前
力扣hot100——子串、普通数组、矩阵
java·后端·算法·leetcode·面试·矩阵
香菇滑稽之谈5 小时前
状态模式的C++实现示例
c++·设计模式·状态模式