设计模式之策略模式

一、策略模式简介

策略模式:

指对象有某个行为,但是在不同的场景中,该行为有不同的实现算法。比如每个人都要“交个人所得税”,但是“在美国交个人所得税”和“在中国交个人所得税”就有不同的算税方法。本模式使得算法可独立于使用他的用户而变化

解决的问题:

在多种策略(算法)相似的情况下,使用if…else所带来的的复杂和难以维护

优点:
  • 策略模式提供了对“开闭原则”的完美支持,用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为。
  • 策略模式提供了管理相关的算法的办法。
  • 策略模式提供了可以替换继承关系的办法。
  • 使用策略模式可以避免使用多重条件转移语句。
缺点:
  • 初学者觉得设计过重
  • 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。
  • 策略模式将造成产生很多策略类,可以通过使用享元模式在一 定程度上减少对象的数量。

二、策略模式组成机构

  • 上下文环境(Context):持有一个策略类的引用,最终给客户端使用,便于隔离客户端和算法
  • 抽象策略(Strategy):策略类,通常是一个接口或抽象类
  • 具体策略(ConcreteStrategy):实现了策略类中的策略方法,封装相关的算法和行为

三、策略模式UML图解

四、应用场景

  • 多个类之区别在行为表现不同,可使用策略模式,在运行时动态选择具体要执行的行为
  • 需要在不同情况下使用不同的策略(算法),或者策略还可能在未来用其他方式来实现
  • 对用户隐藏具体策略(算法)的实现细节,彼此完全独立

五、案例实现

测试案例只为简单的展现策略模式,其中如参各参数未进行校验,可能存在数据问题;

上下文环境:
  package com.chorany.policy.service.impl;

import com.chorany.policy.service.CalculatorService;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * @author : chorany
 * @date: 2022/5/27 0:25
 * @description: 策略模式上下文类,用于隔绝客户端和算法
 */
@Component
public class CalcContext {

    @Autowired
    private List<CalculatorService> calculatorServices;

    @SneakyThrows
    public CalculatorService getCalculatorServiceBean(String operationType) {

        for (CalculatorService calculatorService : calculatorServices) {
            //根据类型匹配相对应的实现
            if (calculatorService.getOperationType().getCode().equals(operationType)) {
                return calculatorService;
            }
        }
        throw new Exception("根据操作类型,未找到相应策略");
    }
}
抽象策略:
package com.chorany.policy.service;

import com.chorany.policy.CalculatorVO;
import com.chorany.policy.enums.OperationTypeEnum;

/**
 * 计算类接口
 *
 * @author : chorany
 * @date : 2022/5/23 23:50
 * @description
 */
public interface CalculatorService {

    public OperationTypeEnum getOperationType();

    public Double Calculator(CalculatorVO calculatorVO);
}


具体策略实现:
  1. 加法实现
package com.chorany.policy.service.impl;

import com.chorany.policy.CalculatorVO;
import com.chorany.policy.enums.OperationTypeEnum;
import com.chorany.policy.service.CalculatorService;
import org.springframework.stereotype.Service;

/**
 * 加法运算类
 *
 * @author : chorany
 * @date : 2022/5/23 23:50
 * @description
 */
@Service
public class AddCalculatorProcess implements CalculatorService {

    @Override
    public OperationTypeEnum getOperationType() {
        return OperationTypeEnum.ADD;
    }

    @Override
    public Double Calculator(CalculatorVO calculatorVO) {
        return calculatorVO.getFirstNum() + calculatorVO.getSecondNum();
    }
}

  1. 减法实现
package com.chorany.policy.service.impl;

import com.chorany.policy.CalculatorVO;
import com.chorany.policy.enums.OperationTypeEnum;
import com.chorany.policy.service.CalculatorService;
import org.springframework.stereotype.Service;

/**
 * 减法运算类
 *
 * @author : chorany
 * @date : 2022/5/23 23:50
 * @description
 */
@Service
public class SubCalculatorProcess implements CalculatorService {

    @Override
    public OperationTypeEnum getOperationType() {
        return OperationTypeEnum.SUB;
    }

    @Override
    public Double Calculator(CalculatorVO calculatorVO) {
        return calculatorVO.getFirstNum() - calculatorVO.getSecondNum();
    }
}

  1. 乘法实现
package com.chorany.policy.service.impl;

import com.chorany.policy.CalculatorVO;
import com.chorany.policy.enums.OperationTypeEnum;
import com.chorany.policy.service.CalculatorService;
import org.springframework.stereotype.Service;

/**
 * 乘法运算类
 *
 * @author : chorany
 * {@code @date} : 2022/5/23 23:50
 * @ description
 */
@Service
public class MulCalculatorProcess implements CalculatorService {

    @Override
    public OperationTypeEnum getOperationType() {
        return OperationTypeEnum.MUL;
    }

    @Override
    public Double Calculator(CalculatorVO calculatorVO) {
        return calculatorVO.getFirstNum() * calculatorVO.getSecondNum();
    }
}

  1. 除法实现
package com.chorany.policy.service.impl;

import com.chorany.policy.CalculatorVO;
import com.chorany.policy.enums.OperationTypeEnum;
import com.chorany.policy.service.CalculatorService;
import org.springframework.stereotype.Service;

/**
 * 除法运算类
 *
 * @author : chorany
 * @date : 2022/5/23 23:50
 * @description
 */
@Service
public class DivCalculatorProcess implements CalculatorService {

    @Override
    public OperationTypeEnum getOperationType() {
        return OperationTypeEnum.DIV;
    }

    @Override
    public Double Calculator(CalculatorVO calculatorVO) {
        return calculatorVO.getFirstNum() / calculatorVO.getSecondNum();
    }
}

操作类型枚举类:
package com.chorany.policy.enums;


/**
 * @author : chorany
 * @date : 2022/5/23 23:27
 * @description :
 */
public enum OperationTypeEnum {

    ADD("ADD", "加法"),
    SUB("SUB", "减法"),
    MUL("MUL", "乘法"),
    DIV("DIV", "除法"),

    ;
    private String code;
    private String name;


    OperationTypeEnum(String code, String name) {
        this.code = code;
        this.name = name;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

ControllerD调用测试类及VO类:

vo类:

package com.chorany.policy.vo;

import lombok.Data;

/**
 * 请求入参VO
 *
 * @author : chorany
 * @date : 2022/5/23 23:46
 * @description
 */
@Data
public class CalculatorVO {
    private Double firstNum;
    private Double secondNum;
    private String operationType;
}

测试接口类:此处采用多态方式引用当前策略的所有实现类

package com.chorany.policy.controller;

import com.chorany.policy.service.impl.CalcContext;
import com.chorany.policy.vo.CalculatorVO;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * @author : chorany
 * @date : 2022/5/23 23:22
 * @description
 */
@RestController
@RequestMapping("/v1/calc")
public class CalculatorController {

    @Autowired
    private CalcContext calcContext;

    /**
     * 使用上下文类对客户端和算法进行隔离,其实就是相当于把上面匹配的代码挪到上下文类中,我喜欢使用上面一种方式,学习起来更方便,看前来更简洁
     *
     * @param calculator
     * @return
     */
    @PostMapping("/testByContext")
    public Double CalculatorByContext(@RequestBody CalculatorVO calculator) {
        return calcContext.getCalculatorServiceBean(calculator.getOperationType()).Calculator(calculator);
    }
}

六、个人总结

在实际开发中策略模式应用十分广泛,我们可以根据自己开发时的需要对策略模式进行稍微变更,例如:可结合工厂模式在上下文类中动态创建策略实现类等,我个人不喜欢在客户端和算法之间增加上下文类,因为这样spring容器中相当于多创建另一个bean,增加管理成本

策略模式可省略上下文类,采用多态方式引用当前策略(算法)的实现类集合

省略上下文类,直接引用策略(算法)实现类集合(这种方式可省略上下文类)

package com.chorany.policy.controller;

import com.chorany.policy.vo.CalculatorVO;
import com.chorany.policy.service.CalculatorService;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * @author : chorany
 * @date : 2022/5/23 23:22
 * @description
 */
@RestController
@RequestMapping("/v1/calc")
public class CalculatorController {

    @Autowired
    private List<CalculatorService> calculatorService;
    
    /**
     * 初始化bean的时候采用集合注入所有接口实现类的方式
     *
     * @param calculator
     * @return
     */
    @SneakyThrows
    @PostMapping("/test")
    public Double Calculator(@RequestBody CalculatorVO calculator) {
        //根据传入计算类型,匹配上就执行相对应的处理方式
        for (CalculatorService service : calculatorService) {
            if (service.getOperationType().getCode().equals(calculator.getOperationType())) {
                return service.Calculator(calculator);
            }
        }
        throw new Exception("根据操作类型,未找到相应策略");
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值