设计模式之策略模式(优雅)

不求流芳百世,只求口碑载道。

写在前面的话

从最简单系统最常用的开始,因为难的我也需要先学习然后再分享嘛。

一、策略模式介绍

策略模式就是将多个策略(对象行为)封装起来,根据实际需要选择不同的策略进行业务操作。简单地说,就是将一组对象行为封装到不同的策略类里面,作为一个策略接口的实现,然后通过业务需要去拿到不同的实现类进行后续的操作。
比如:
超市的商品会根据购买人的不同(消费者可能是员工可能是普通消费者)去调整商品的售价,员工会走员工通道拿到员工价,普通消费者对不起你只能按正常售价购买。员工可能又会按照年限制定不同的策略,二十年老员工,十年老员工,五年员工,一个月的牛犊子价格又会不同。这里只区分了价格,但是实际肯定不会这么简单,员工购物可能会走多个业务流程,比如优惠中心,积分中心等等。
在我的理解看来,策略模式就是优雅版的if else,不知道这个形容对不对。
策略模式的缺点在于:容易造成很多的策略类,增加维护难度。

二、策略模式的实现

1、结构说明

1)策略接口,比如:购物接口。
2)策略接口的实现类,也即以哪种身份进行购物,员工或普通消费者
3)策略标识枚举类,通过枚举值来唯一标识每一个策略。这个类根据情况选择也可以不要,如果策略较多比如上面的超时购物的例子,方便统一维护我觉得还是有必要的。
4)策略工厂,在这个类中根据标识来确定走哪种具体的策略。
5)业务类,这里是业务的具体操作,选择合适的策略进行业务流程。

2、具体实现

1)策略接口

public interface ShoppingStrategy {

    /**
     * 处理购物相关
     * @param request
     * @return
     */
    ShoppingResponse handleShopping(ShoppingRequest request);

}

2)策略接口的实现类

@Service("normalShoppingStrategy")
public class NormalShoppingStrategy implements ShoppingStrategy {

	/**
     * 普通顾客购物
     * @param request
     * @return
     */
    @Override
    public ShoppingResponse handleShopping(ShoppingRequest request) {
        return null;
    }
}
@Service("staffShoppingStrategy")
public class StaffShoppingStrategy implements ShoppingStrategy {
	/**
     * 员工购物
     * @param request
     * @return
     */
    @Override
    public ShoppingResponse handleShopping(ShoppingRequest request) {
        return null;
    }
}

3)策略标识枚举类
提供了一个方法getStrategyName,根据shoppingStrategyCode找到对应的策略类。

public enum ShoppingStrategyEnum {

    /**
     * 普通顾客
     */
    NORMAL_SHOPPING("SP012001", "normalShoppingStrategy"),

    /**
     * 员工
     */
    STAFF_SHOPPING("SP012002", "staffShoppingStrategy")
    ;

    private String shoppingStrategyCode;
    
    private String shoppingStrategyName;

    ShoppingStrategyEnum(String shoppingStrategyCode, String shoppingStrategyName) {
        this.shoppingStrategyCode = shoppingStrategyCode;
        this.shoppingStrategyName = shoppingStrategyName;
    }

    public String getShoppingStrategyCode() {
        return shoppingStrategyCode;
    }

    public String getShoppingStrategyName() {
        return shoppingStrategyName;
    }

    public static String getStrategyName(String shoppingTypeCode) {
        for (ShoppingStrategyEnum strategyEnum : ShoppingStrategyEnum.values()) {
            if (strategyEnum.getShoppingStrategyCode().equals(shoppingTypeCode)) {
                return strategyEnum.getShoppingStrategyName();
            }
        }
        return null;
    }
}

4)策略工厂
重点是这里,其实也就是一个map,key为顾客类型的枚举值,value即根据顾客类型确定的购物策略。
提供了一个方法,根据枚举类中的策略名称shoppingStrategyName找到对应的策略实现类。

@Component
public class ShoppingStrategyFactory {

    private Map<String, ShoppingStrategy> contextStrategy = new HashMap<>();

    public ShoppingStrategyFactory(Map<String, ShoppingStrategy> contextStrategy) {
        this.contextStrategy = contextStrategy;
    }

    public Map<String, ShoppingStrategy> getContextStrategy() {
        return contextStrategy;
    }

    public ShoppingStrategy doStrategy(String typeName) {
        return this.contextStrategy.get(typeName);
    }
}

5)业务类
开始实际业务。

@Slf4j
@Service
public class ShoppingServiceImpl implements ShoppingService {
    @Autowired
    private ShoppingStrategyFactory shoppingStrategyFactory;
    
    @Override
    public ShoppingResponse handleShoppingBusiness(ShoppingRequest request) {
        String strategyName = ShoppingStrategyEnum.getStrategyName(request.getShoppingTypeCode());
        if (StringUtils.isBlank(strategyName)) {
            return null;
        }
        ShoppingStrategy shoppingStrategy = null;
        try {
            shoppingStrategy = shoppingStrategyFactory.doStrategy(strategyName);
            ShoppingResponse shoppingResponse = shoppingStrategy.handleShopping(request);
        }catch (Exception e) {
            log.error("执行{}购物失败", strategyName);
        }
        return null;
    }
}

三、单元测试

// 写了个单元测试测了一下,传入SP012001	打印的全类名确实是
// class com.roshine.service.shopping.strategy.NormalShoppingStrategy
request.setShoppingTypeCode("SP012001");

至此,一个策略模式的demo就完成了。如果有更好的方案或更优雅的方式也请留言大家一起共同学习。
注意:我自己写的不是shopping,代码的类名临时修改过全部改成了shopping,如果有地方看走眼了没改完全还请见谅。

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 游动-白 设计师:上身试试 返回首页