掘金 后端 ( ) • 2024-04-25 09:57

策略模式具有相对稳定的形式,如“避实就虚”、“出奇制胜”等。一定的策略模式,既可应用于战略决策,也可应用于战术决策;既可实施于大系统的全局性行动,也可实施于大系统的局部性行动。

2.jpg

策略模式定义

背景

在软件系统开发中,有许多算法可以实现某一功能,如查找、排序等,一种常见的方法是 硬编码(Hard Coding)

例如:

  1. 将这些算法直接写到一个类中,提供调用方法。
  2. 将这些算法封装在一个统一的方法中,用if...else条件判断。

问题:

  1. 这种硬编码的方式在修改或新增算法时,需要修改封装算法类中的源码,这种做法会导致程序变得难以维护
  2. 存在大量可选的算法,会将问题变更加严重
  3. 不符合 开闭原则

定义

为了解决上面的问题,我们定义独立的类来封装不同的算法,让每一个类封装一个具体算法,作为一个策略。

1.png

正如如今社会的出行方式:定义一系列出行方式(自行车、汽车、火车、飞机),每个出行方式独立,且可以相互替换,出行方式独立于人。

策略模式:定义一系列算法,将每一个算法封装起来,并让它们可以相互替换。

策略模式结构分析

结构

3.webp

策略模式包含如下角色:

1. Context(环境类):持有一个策略类的引用,最终给客户端调用。

2. Strategy(抽象策略类):定义所有支持的算法的公共接口。

3. ConcreteStrategy(具体策略类):实现具体算法。

实例

1. 定义策略接口(Strategy)

public interface Strategy {
    void AlgorithmInterface();
}

2. 实现具体策略(ConcreteStrategy)

public class ConcreteStrategyA implements Strategy {
    @Override
    public void AlgorithmInterface() {
        // 算法A
    }
}

public class ConcreteStrategyB implements Strategy {
    @Override
    public void AlgorithmInterface() {
        // 算法B
    }
}

public class ConcreteStrategyC implements Strategy {
    @Override
    public void AlgorithmInterface() {
        // 算法C
    }
}

3. 定义环境类(Context)

public class Context {

    private Strategy strategy;

    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }

    public void ContextInterface() {
        strategy.AlgorithmInterface();
    }
}

案例

购买商品案例,用户通过客户端选择商品,创建环境类,调用环境类中的购买商品方法进行支付,根据不同的策略,选择不同的支付方式。

流程图:

案例流程图.png

类图:

image.png

代码:

抽象策略类

/**
 * 抽象策略类
 * 付款策略接口
 */
public interface PaymentStrategy {
    double pay(double amount);
}

具体策略类

/**
 * 具体策略类
 * 支付宝支付策略
 */
public class AliPayStrategy implements PaymentStrategy {
    @Override
    public double pay(double amount) {
        System.out.println("------ 支付宝支付策略 ------");
        return amount;
    }
}

--------------------------------------------------------------

/**
 * 具体策略类
 * 银行卡支付策略
 */
public class CreditCardStrategy implements PaymentStrategy {

    @Override
    public double pay(double amount) {
        System.out.println("------ 银行卡支付策略 ------");
        return amount;
    }
}

--------------------------------------------------------------

/**
 * 具体策略类
 * 微信支付策略
 */
public class WeChatPayStrategy implements PaymentStrategy {

    @Override
    public double pay(double amount) {
        System.out.println("------ 微信支付策略 ------");
        return amount;
    }
}

环境类

import java.util.ArrayList;
import java.util.List;

/**
 * 环境类
 * 买车
 */
public class ShoppingCart {
    /**
     * 购物车
     */
    private List<Double> items;
    /**
     * 引用抽象策略类
     */
    private PaymentStrategy paymentStrategy;

    /**
     * 初始化 购物车
     */
    public ShoppingCart() {
        items = new ArrayList<>();
    }
    /**
     * 购物车中 添加商品
     * @param price 商品价格
     */
    public void addItem(double price) {
        items.add(price);
    }
    /**
     * set 方法注入 抽象策略类
     * @param paymentStrategy 抽象策略类
     */
    public void setPaymentStrategy(PaymentStrategy paymentStrategy) {
        this.paymentStrategy = paymentStrategy;
    }
    /**
     * 计算购物车中 商品总价格
     * @return 总价
     */
    public double calculateTotal() {
        double total = 0;
        for (double price : items) {
            total += price;
        }
        return total;
    }
    /**
     * 调用具体策略类中的支付方法
     * @return 购买物品总的价格
     */
    public double buy() {
        return paymentStrategy.pay(calculateTotal());
    }
}

客户端

/**
 * 客户端
 */
public class ShoppingClient {
    public static void main(String[] args) {

        ShoppingCart cart = new ShoppingCart();

        cart.addItem(100.0);
        cart.addItem(200.0);

        // 选择银行卡刷卡支付
        cart.setPaymentStrategy(new CreditCardStrategy());
        System.out.println("银行卡支付" + cart.buy() + "元");

        // 选择微信支付
        cart.setPaymentStrategy(new WeChatPayStrategy());
        System.out.println("微信支付" + cart.buy() + "元");

        // 选择支付宝支付
        cart.setPaymentStrategy(new AliPayStrategy());
        System.out.println("支付宝支付" + cart.buy() + "元");
    }
}

XMLutil工具

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
public class XMLUtil
{
    public static Object getBean()
    {
        try
        {

            DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = dFactory.newDocumentBuilder();
            Document doc;
            doc = builder.parse(new File("Strategy.xml"));


            NodeList nl = doc.getElementsByTagName("className");
            Node classNode=nl.item(0).getFirstChild();
            String cName=classNode.getNodeValue();
            Class c=Class.forName(cName);
            Object obj=c.newInstance();
            return obj;
        }
        catch(Exception e)
        {
            e.printStackTrace();
            return null;
        }
    }
}

优缺点

优点

  1. 提高了代码的复用性和可维护性,将算法的定义与其具体实现进行解耦。
  2. 可以在运行时动态替换算法,提高了程序的灵活性。
  3. 符合开闭原则,新增算法无需修改现有代码。

缺点

  1. 客户端需要知道所有的策略类,并根据具体场景选择合适的策略,增加了客户端的复杂度。
  2. 如果策略类较多,会导致类的数量增多,增加系统的复杂度。

应用场景

  1. 当一个系统中存在多个类只有它们的行为或算法不同时。
  2. 当一个类定义了多种行为,而这些行为在这个类的操作中以多个条件语句的形式出现,可以将相关的条件分支移入它们各自的策略类中,以替换这些条件语句。
  3. 当系统需要动态地在几种算法中选择一种时,如根据不同的配置、用户选择或者环境条件等。

总结

策略模式是一种行为型设计模式,通过封装一系列算法并使它们可以相互替换,实现了算法的变化独立于使用它的客户。策略模式在实际开发中应用较为广泛,如排序算法、缓存策略、支付方式等。在合适的场景下使用策略模式,可以提高代码的复用性、可维护性和灵活性。