策略模式

1、有什么用

在生活中我们经常遇到这样的场景,当我们在购物时,进场需要仅限在线支付,我们可以选择微信、支付宝等等支付方式,策略模式就是应用在这些场景,虽然我们选择了不同的方式,当时我们达到了相同的目的,那就只能付款。因此,策略模式的核心就是一组可以相互替换的算法。

2、结构实现

策略模式会实现同一个接口,或者继承通过抽象类,当我们使用是,能够根据用户的选择实现自动替换

具体的代码实现如下

首先,我们定义一个顶层的接口Calculate

/** * @author haidechizi */public interface Calculate {    /**     * 计算     * @param x     * @param y     * @param operate     * @return     */    int calculate(int x, int y, String operate);}

然后再定义具体的实现类

  • AddCalculate
/** * @author haidechizi */public class AddCalculate implements Calculate {    /**     * 计算     *     * @param x     * @param y     * @param operate     * @return     */    @Override    public int calculate(int x, int y, String operate) {        return x + y;    }}
  • SubtractCalculate
/** * @author haidechizi */public class SubtractCalculate implements Calculate {    /**     * 计算     *     * @param x     * @param y     * @param operate     * @return     */    @Override    public int calculate(int x, int y, String operate) {        return x - y;    }}
  • MultiplyCalculate
/** * @author haidechizi */public class MultiplyCalculate implements Calculate {    /**     * 计算     *     * @param x     * @param y     * @param operate     * @return     */    @Override    public int calculate(int x, int y, String operate) {        return x * y;    }}

然后我们再定义一个类,用于选择具体的策略CalculateFactory

/** * @author haidechizi */public class CalculateFactory {    /**     * 获取具体的计算方式     *     * @param operate     * @return     */    public static Calculate getInstance(String operate) {        if ("+".equals(operate)) {            return new AddCalculate();        } else if ("-".equals(operate)) {            return new SubtractCalculate();        } else if ("*".equals(operate)) {            return new MultiplyCalculate();        }        throw new UnsupportedOperationException("不支持当前操作");    }}
/** * @author haidechizi */public class Calculator {    public static int calculate(int x, int y, String operate) {        Calculate calculate = CalculateFactory.getInstance(operate);        return calculate.calculate(x, y, operate);    }    public static void main(String[] args) {        int calculate = Calculator.calculate(1, 2, "+");        System.out.println(calculate);        calculate = Calculator.calculate(1, 2, "*");        System.out.println(calculate);    }}

通过上面的代码,我们可以看到,只要我们传递相应的策略标识,程序就会自动为我们选择相应的策略,实现无缝的替换,同时如果需要增加其他策略,我们只需直线接口就行。

3、优点缺点

  • 优点

策略模式就是一组算法族,他可以完美的替换掉我们代码中的if/else,同时符合开闭原则,易于扩展

  • 缺点

策略模式会在代码中增加大量的类,同时用户必须知道所有的策略,才可以实现自由的选择

4、应用实例

在工作中,我们有一种解析条码的业务场景,对于不同的商家,条码的解析规则不同,因此,我们需要实现一组算法,来解析条码

  • BarCode
/** * @author haidechizi */@Data@AllArgsConstructor@NoArgsConstructorpublic class BarCode {    /**     * 条码类型     */    private Integer type;    /**     * 商品编码     */    private String code;    /**     * 价格     */    private Integer price;}
  • BarCodeAnalysis
/** * @author haidechizi */public interface BarCodeAnalysis {    /**     * 解析条码     *     * @param itemCode     * @return     */    BarCode analysis(String itemCode);}
  • DefaultBarCodeAnalysis
/** * @author haidechizi */public class DefaultBarCodeAnalysis implements BarCodeAnalysis {    /**     * 解析条码     *     * @param itemCode     * @return     */    @Override    public BarCode analysis(String itemCode) {        String type = itemCode.substring(0, 1);        String code = itemCode.substring(1, 6);        String price = itemCode.substring(6, 10);        return new BarCode(Integer.parseInt(type), code, Integer.parseInt(price));    }}
  • SpecialBarCodeAnalysis
/** * @author haidechizi */public class SpecialBarCodeAnalysis implements BarCodeAnalysis {    /**     * 解析条码     *     * @param itemCode     * @return     */    @Override    public BarCode analysis(String itemCode) {        String type = itemCode.substring(0, 1);        String code = itemCode.substring(1, 7);        String price = itemCode.substring(7, 10);        return new BarCode(Integer.parseInt(type), code, Integer.parseInt(price));    }}
  • BarCodeFactory
/** * @author haidechizi */public class BarCodeFactory {    public static BarCodeAnalysis getAnalysis(Long vendorId) {        if (1 == vendorId) {            return new SpecialBarCodeAnalysis();        }        return new DefaultBarCodeAnalysis();    }}
  • AnalysisHelper
/** * @author haidechizi */public class AnalysisHelper {    public static BarCode analysis(Long vendorId, String itemCode) {        BarCodeAnalysis analysis = BarCodeFactory.getAnalysis(vendorId);        return analysis.analysis(itemCode);    }    public static void main(String[] args) {        BarCode barCode = analysis(1L, "5478654214");        System.out.println(barCode);    }}

通过这个实际的案例,我们可以更好的在工作中使用策略模式。

5、模式扩展

通过上面的两个案例,我们可以发现,在使用的过程中,虽然我们能够很好的增加不同的策略,但是,我们依旧还要修改获取策略的工厂,这显然不能做到不修改之前的代码,那么,我们能够完全不修改代码,使其完全负责开闭原则,满足这一点缺憾呢。

显然是可以做到的,目前Spring使用的最后广泛,相信使用Spring实现,大部分人都能够用得上。

  • BarCode
/** * @author haidechizi */@Data@AllArgsConstructor@NoArgsConstructorpublic class BarCode {    /**     * 条码类型     */    private Integer type;    /**     * 商品编码     */    private String code;    /**     * 价格     */    private Integer price;}
  • BarCodeAnalysis
/** * @author haidechizi */public interface BarCodeAnalysis {    /**     * 解析条码     *     * @param itemCode     * @return     */    BarCode analysis(String itemCode);}
  • AbstractBarCodeAnalysis
/** * @author dejun.wang * @date 2021/11/25 */public abstract class AbstractBarCodeAnalysis implements BarCodeAnalysis {    @PostConstruct    public void init() {    }    protected abstract String type();}
  • DefaultBarCodeAnalysis
/** * @author haidechizi */@Lazy(value = false)@Componentpublic class DefaultBarCodeAnalysis extends AbstractBarCodeAnalysis {    /**     * 解析条码     *     * @param itemCode     * @return     */    @Override    public BarCode analysis(String itemCode) {        String type = itemCode.substring(0, 1);        String code = itemCode.substring(1, 6);        String price = itemCode.substring(6, 10);        return new BarCode(Integer.parseInt(type), code, Integer.parseInt(price));    }    @Override    protected String type() {        return "default";    }}
  • SpecialBarCodeAnalysis
/** * @author haidechizi */@Lazy(value = false)@Componentpublic class SpecialBarCodeAnalysis extends AbstractBarCodeAnalysis {    /**     * 解析条码     *     * @param itemCode     * @return     */    @Override    public BarCode analysis(String itemCode) {        String type = itemCode.substring(0, 1);        String code = itemCode.substring(1, 7);        String price = itemCode.substring(7, 10);        return new BarCode(Integer.parseInt(type), code, Integer.parseInt(price));    }    @Override    protected String type() {        return "special";    }}
  • BarCodeFactory
/** * @author haidechizi */public class BarCodeFactory {    private static final Map analysisHolder = new ConcurrentHashMap<>();    public static void addAnalysis(String type, BarCodeAnalysis barCodeAnalysis) {        analysisHolder.put(type, barCodeAnalysis);    }    public static BarCodeAnalysis getAnalysis(String type) {        return Optional.ofNullable(analysisHolder.get(type)).orElse(analysisHolder.get("default"));    }}
  • AnalysisHelper
/** * @author haidechizi */public class AnalysisHelper {    public static BarCode analysis(Long vendorId, String itemCode) {        // 通过数据库获取商家对于的条码解析规则        String type = "default";        BarCodeAnalysis analysis = BarCodeFactory.getAnalysis(type);        return analysis.analysis(itemCode);    }    public static void main(String[] args) {        BarCode barCode = analysis(1L, "5478654214");        System.out.println(barCode);    }}

在Spring中,@PostConstruct在bean初始化时,会进行调用,通过这种方式,我们可以将type和bean的映射关系保存起来,然后直通通过类型去获取相应的bean,这样,我们便可以不用在增加策略类的时候,修改原有的代码,完美的符合了开闭原则。

当然,在Spring中,还有其他方式实现这个功能

/** * @author haidechizi */@Componentpublic class BarCodeFactory2 {    private static final Map analysisHolder = new ConcurrentHashMap<>();    @Autowired    public void init(List barCodeAnalysisList) {        barCodeAnalysisList.forEach(barCodeAnalysis -> {            analysisHolder.put(barCodeAnalysis.type(), barCodeAnalysis);        });    }    public static BarCodeAnalysis getAnalysis(String type) {        return Optional.ofNullable(analysisHolder.get(type)).orElse(analysisHolder.get("default"));    }}

通过@Autowired注入的方式,我们也可以映射类型和对应的条码的解析之前的关系

发表评论
留言与评论(共有 0 条评论) “”
   
验证码:

相关文章

推荐文章