设计模式之策略模式XXOO

一、设计模式一策略模式定义

策略模式的目的是封装一系列的算法,它们具有共性,可以相互替换,也就是说让算法独立于使用它的客户端而独立变化,客户端仅仅依赖于策略接口 。

汇总:定义一系列的算法,把每一个算法封装起来, 并且使它们可相互替换。 策略模式把对象本身和运算规则区分开来,因此我们整个模式也分为三个部分。 

1).环境类(Context):用来操作策略的上下文环境。

2).抽象策略类(Strategy):策略的抽象。 

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

例如:解析报文,为MT1101时是A,为MT1102是B,如上所说,解析报文的可以分为三个方面

解析器(操作策略的上下文环境)、解析方式(策略的抽象)、解析方式的实现(具体的策略实现)。

或者 学生选择交通方式去上学,公交/地铁/打车。

二、策略模式+Map字典解决if-else

现在根据解析报文的动作来代码实践,优化if-else操作

首先,环境类(Context):用来操作策略的上下文环境,也就是解析器

/**
 * 描述: 上下文类,持有策略接口
 *
 * 环境类(Context):用来操作策略的上下文环境,也就是解析器
 *
 * @author: yanglin
 * @Date: 2020-06-30-13:16
 * @Version: 1.0
 */
@Component
public class ReceiptStrategyContext {

    private IReceiptHandleStrategy receiptHandleStrategy;

    /**
     * 设置策略接口
     * @param receiptHandleStrategy
     */
    public void setReceiptHandleStrategy(IReceiptHandleStrategy receiptHandleStrategy){
        this.receiptHandleStrategy = receiptHandleStrategy;
    }

    /**
     * 执行接口
     * @param receipt
     */
    public void handleReceipt(Receipt receipt){
        receiptHandleStrategy.handleReceipt(receipt);
    }
}

抽象策略类(Strategy):策略的抽象。 也就是解析方式接口

@Service
public interface IReceiptHandleStrategy {

    void handleReceipt(Receipt receipt);
}

具体策略类(ConcreteStrategy):具体的策略实现。解析方式的实现。

@Slf4j
@IsElseEnd(type = "MT1101")
public class Mt1101ReceiptHandleStrategy implements IReceiptHandleStrategy{

    public static Mt1101ReceiptHandleStrategy newReceiptHandleStrategy(){
        return new Mt1101ReceiptHandleStrategy();
    }

    @Override
    public void handleReceipt(Receipt receipt) {
        log.info("Mt1101ReceiptHandleStrategy 解析报文MT1101 : {}", receipt.getMessage());
    }
}

@Slf4j
@IsElseEnd(type = "MT2101")
public class Mt2101ReceiptHandleStrategy implements IReceiptHandleStrategy{

    public static Mt2101ReceiptHandleStrategy newReceiptHandleStrategy(){
        return new Mt2101ReceiptHandleStrategy();
    }

    @Override
    public void handleReceipt(Receipt receipt) {
        log.info("Mt2101ReceiptHandleStrategy 解析报文MT2101 : {}", receipt.getMessage());
    }
}

@Slf4j
@IsElseEnd(type = "MT8104")
public class Mt8104ReceiptHandleStrategy implements IReceiptHandleStrategy{

    public static Mt8104ReceiptHandleStrategy newReceiptHandleStrategy(){
        return new Mt8104ReceiptHandleStrategy();
    }

    @Override
    public void handleReceipt(Receipt receipt) {
        log.info("Mt8104ReceiptHandleStrategy 解析报文MT8104 : {}", receipt.getMessage());
    }
}

@Slf4j
@IsElseEnd(type = "MT9999")
public class Mt9999ReceiptHandleStrategy implements IReceiptHandleStrategy{

    public static Mt9999ReceiptHandleStrategy newReceiptHandleStrategy(){
        return new Mt9999ReceiptHandleStrategy();
    }

    @Override
    public void handleReceipt(Receipt receipt) {
        log.info("Mt9999ReceiptHandleStrategy 解析报文MT9999 : {}", receipt.getMessage());
    }
}

 

再来个策略工厂,处理数据。

/**
 * 描述: 策略工厂
 *
 * 具体策略类(ConcreteStrategy):具体的策略实现,每一种出行方式的具体实现。
 *
 * @author: yanglin
 * @Date: 2020-06-30-13:19
 * @Version: 1.0
 */
@Component
@Slf4j
public class  ReceiptHandleStrategyFactory {

    public Map<String, IReceiptHandleStrategy> receiptHandleStrategyMap;

    public static ReceiptHandleStrategyFactory newReceiptHandleStrategyFactory(){

        return new ReceiptHandleStrategyFactory();
    }

    /**
     * 初始化加载IsElseEnd注解对应的类
     */
    private ReceiptHandleStrategyFactory() {
        receiptHandleStrategyMap = new ConcurrentHashMap<>();
        try{
            // 1.反射工具包,扫描注解插入语数据
            //反射工具包,指明扫描路径
            Reflections reflections = new Reflections("com.yl.strategy");
            //获取带Handler注解的类
            Set<Class<?>> classList = reflections.getTypesAnnotatedWith(IsElseEnd.class);
            for (Class classes : classList) {
                IsElseEnd annotation = (IsElseEnd) classes.getAnnotation(IsElseEnd.class);
                String type = annotation.type();
                receiptHandleStrategyMap.put(type, (IReceiptHandleStrategy) classes.newInstance());
                log.info("addListType type : {}, ", type);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        // 2.这是写死数据
        /*receiptHandleStrategyMap.put("MT1101", Mt1101ReceiptHandleStrategy.newReceiptHandleStrategy());
        receiptHandleStrategyMap.put("MT2101", Mt2101ReceiptHandleStrategy.newReceiptHandleStrategy());
        receiptHandleStrategyMap.put("MT8104", Mt8104ReceiptHandleStrategy.newReceiptHandleStrategy());
        receiptHandleStrategyMap.put("MT9999", Mt9999ReceiptHandleStrategy.newReceiptHandleStrategy());*/
    }

    public IReceiptHandleStrategy getReceiptHandleStrategy(String receiptType){
        return receiptHandleStrategyMap.get(receiptType);
    }
}

或者使用springAop切面获取数据

@Component
@Aspect
@Slf4j
public class IfElseAspect {

    /**
     * 包名的方式
     * @Pointcut("execution(* com.yl.strategy.service.impl.*.*(..))")
     * 注解发送
     * @Pointcut("@annotation(com.yl.strategy.annotation.IsElseEnd)")
     */
    @Pointcut("execution(* com.yl.strategy.service.impl.*.*(..))")
    public void addListType(){

    }

    @Before("addListType()")
    public void doBefore(JoinPoint joinPoint) {
        log.info("******拦截前的逻辑******");
        log.info("目标方法名为:" + joinPoint.getSignature().getName());
        log.info("目标方法所属类的简单类名:" + joinPoint.getSignature().getDeclaringType().getSimpleName());
        log.info("目标方法所属类的类名:" + joinPoint.getSignature().getDeclaringTypeName());
        log.info("目标方法声明类型:" + Modifier.toString(joinPoint.getSignature().getModifiers()));
        //获取传入目标方法的参数
        Object[] args = joinPoint.getArgs();
        for (int i = 0; i < args.length; i++) {
            log.info("第" + (i + 1) + "个参数为:" + args[i]);
        }
        log.info("被代理的对象:" + joinPoint.getTarget());
        log.info("代理对象自己:" + joinPoint.getThis());

    }

    /**
     * 环绕通知
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("addListType()&&@annotation(com.yl.strategy.annotation.IsElseEnd))")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable{
        Object res = null;
        long time = System.currentTimeMillis();
        try {
            res =  joinPoint.proceed();
            time = System.currentTimeMillis() - time;
            // 得到类注解 获得所在切点的该类的class对象,也就是UserController这个类的对象
            Class<?> aClass = joinPoint.getTarget().getClass();
            IsElseEnd annotationClass = aClass.getAnnotation(IsElseEnd.class);
            if (Objects.nonNull(annotationClass)) {
                String type = annotationClass.type();
                // 将类型增加到Map集合中
                ReceiptHandleStrategyFactory.newReceiptHandleStrategyFactory()
                        .receiptHandleStrategyMap.put(type, (IReceiptHandleStrategy) aClass.newInstance());
                log.info("addListType type : {},   time : {} ", type, time);
            }
            // 得到方法注解
           /**
            *  MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            *  IsElseEnd annotation = signature.getMethod().getAnnotation(IsElseEnd.class);
            */
            return res;
        } finally {
            try {
            }catch (Exception e){
               log.info("IfElseAspect 操作失败:" + e.getMessage());
                e.printStackTrace();
            }
        }

    }

    @After("addListType()")
    public void doAfter() {
        log.info("******拦截后的逻辑******");
    }

}

IsElseEnd 注解类

// 接口、类、枚举、注解
@Target(ElementType.TYPE) 
// 这种类型的Annotations将被JVM保留,所以他们能在运行时被JVM或其他使用反射机制的代码所读取和使用.
@Retention(RetentionPolicy.RUNTIME)
public @interface IsElseEnd {

    String type() default "";
}

回执生成器,创建测试数据。

@Data
@Accessors()
public class Receipt {

    /**
     * 回执信息
     */
    String message;

    /**
     * 回执类型(`MT1101、MT2101、MT4101、MT8104、MT8105、MT9999`)
     */
    String type;

    static Receipt newReceipt(){
        return new Receipt();
    }

    public static Builder builder(){
        return new Builder();
    }

    public static class Builder{

        private String message;

        private String type;

        public Builder message(String message){
            this.message = message;
            return this;
        }

        public Builder type(String type){
            this.type = type;
            return this;
        }

        public Receipt build(){
            Receipt receipt = newReceipt();
            receipt.setMessage(message);
            receipt.setType(type);
            return receipt;
        }
    }
}


public class ReceiptBuilder {

    public static List generateReceiptList(){
        //直接模拟一堆回执对象
        List receiptList = new ArrayList<>();
        receiptList.add(Receipt.builder().message("我是MT2101回执喔").type("MT2101").build());
        receiptList.add(Receipt.builder().message("我是MT1101回执喔").type("MT1101").build());
        receiptList.add(Receipt.builder().message("我是MT8104回执喔").type("MT8104").build());
        receiptList.add(Receipt.builder().message("我是MT9999回执喔").type("MT9999").build());
        return receiptList;
    }


}

最后测试一波

@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class StrategyTest {

    @Autowired
    ReceiptHandleStrategyFactory receiptHandleStrategyFactory;

    @Test
    public void strategyRun() throws Throwable {

        //模拟回执
        List<Receipt> receiptList = ReceiptBuilder.generateReceiptList();
        //策略上下文
        ReceiptStrategyContext receiptStrategyContext = new ReceiptStrategyContext();
        // ReceiptHandleStrategyFactory receiptHandleStrategyFactory = ReceiptHandleStrategyFactory.newReceiptHandleStrategyFactory();
        for (Receipt receipt : receiptList) {
            //获取并设置策略
            IReceiptHandleStrategy receiptHandleStrategy = receiptHandleStrategyFactory.getReceiptHandleStrategy(receipt.getType());
            receiptStrategyContext.setReceiptHandleStrategy(receiptHandleStrategy);
            //执行策略
            receiptStrategyContext.handleReceipt(receipt);
        }
    }
}

三、分析策略模式

策略模式的优点:

①我们之前在选择出行方式的时候,往往会使用if-else语句,也就是用户不选择A那么就选择B这样的一种情况。 这种情况耦合性太高了,而且代码臃肿,有了策略模式我们就可以避免这种现象。

②策略模式遵循开闭原则,实现代码的解耦合。扩展新的方法时也比较方便,只需要继承策略接口就好了。

缺点:

①客户端必须知道所有的策略类,并自行决定使用哪一个策略类。

②策略模式会出现很多的策略类。

③context在使用这些策略类的时候,这些策略类由于继承了策略接口,所以有些数据可能用不到,但是依然初始化了。 3.2.与其他模式的区别

1).与状态模式的区别

策略模式只是条件选择方法,只执行一次方法,而状态模式是随着状态的改变不停地更改执行方法。举个例子, 就好比我们旅游,对于策略模式我们只需要选择其中一种出行方法就好了,但是状态模式不一样,可能我们到了 A地点选择的是火车,到了B地点又选择飞机,根据不同的状态选择不同的出行方式。

2).与工厂模式的区别

工厂模式是创建型模式 ,它关注对象创建,提供创建对象的接口,让对象的创建与具体的使用客户无关。 策略模式是对象行为型模式 ,它关注行为和算法的封装 。再举个例子,还是我们出去旅游,对于策略模式 我们只需要选择其中一种出行方法就好,但是工厂模式不同,工厂模式是你决定哪种旅行方案后,由工厂代替你 去构建具体方案(工厂代替你去买火车票)。

以上

Guess you like

Origin blog.csdn.net/qq_35731570/article/details/109465038