减少if else 的使用 之 策略模式

策略模式的UML

在这里插入图片描述
个人理解 :首先可以将策略算法抽象为一个接口或者是一个抽象类,根据上下文的环境实现具体的策略实现类

实例一

超市消费:
用户根据自己的等级,可以享受不同的打折优惠
normal用户: 不打折
vip用户:打九折

普通的实现方法

@Test
    public void initPlan() {
    	// 可以理解为上下文中传来的值
        String level = "normal";
        switch (level) {
            case "normal":
                // todo 不打折
                break;
            case "vip":
                // todo  打九折
                break;
            case "super_vip":
                // todo  打八折
                break;
        }
    }

使用策略模式+spring的实现

  1. 定义会员的枚举

    public enum MemLevel {
        NORMAl(0, "普通会员"),
    	VIP(1, "普通会员");
    
    	private Integer level;
    	private String desc;
    
    	MemLevel(Integer level, String desc) {
        	this.level = level;
      	 	this.desc = desc;
    	}
    
    	public Integer getLevel() {
       		return level;
    	 }
    	}
    
    
  2. 首先抽象出会员计算的策略接口

    public interface IDisCount {
    /**
     * 获取当前策略的类型
     * @return
     */
    	MemLevel getType();
    
    /**
     * 具体的打折操作
     */
    	void operation();
    }
    
  3. 完成具体的策略类

    /**
     * 普通顾客
     */
    @Component
    public class NormalDisCount implements IDisCount {
    
        @Override
        public MemLevel getType() {
            return MemLevel.NORMAl;
        }
    
        @Override
        public void operation() {
            // 逻辑操作
            System.out.println("原价");
        }
    }
    
    /**
     * VIP 顾客
     */
    @Component
    public class VipDiscount implements IDisCount {
    
        @Override
        public MemLevel getType() {
            return MemLevel.VIP;
        }
    
        @Override
        public void operation() {
            // 逻辑操作
            System.out.println("九折");
        }
    }
    
  4. 编写服务层

    @Service
    public class ShoppingService implements ApplicationContextAware {
    
        Map<Integer, IDisCount> strategy;
    
        /**
         * 根据总共消费的金额来判断打折的方式
         * @param
         */
        public void shopping(Integer level) {
            // 获取相应的策略
            IDisCount iDisCount = strategy.get(level);
            // 执行策略操作
            iDisCount.operation();
    
        }
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    
            // 获取ioc容器中的所有IDisCount组件, 加入到 Map<MemLevel, IDisCount> strategy;
            Map<String, IDisCount> iDisCountMap = applicationContext.getBeansOfType(IDisCount.class);
            HashMap<Integer, IDisCount> map = new HashMap<>();
            iDisCountMap.forEach((k, v) -> map.put(v.getType().getLevel(), v));
            this.strategy = map;
        }
    }
    
  5. 测试

    	public class StrategyTest {
        AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(Config.class);
        @Test
        public void test() {
    
            // 会员等级
            Integer memLevel = 1;
            // 从ioc容器中获取ShoppingService 
            ShoppingService shoppingService = ioc.getBean(ShoppingService.class);
            shoppingService.shopping(1);
        }
        }
    

从以上两种方案可以看出,使用设计模式明显的加大了代码量,那为什么又要使用

如果我们要增加一个SuperVip用户打八折的方案
如果是第一种方案则需要加一个case语句,看似没有多大的问题
但是随着需求的增加,代码量会异常的臃肿,你在修改的过程中不可能保证不误修改原来的代码
所以我们选择策略模式的方案
我们不需要修改以前的代码只需要添加一个具体的策略类和修改一下枚举类型

	public enum MemLevel {
    NORMAl(0, "普通会员"),
    VIP(1, "普通会员"),
    SUPER_VIP(2, "超级会员");

    private Integer level;
    private String desc;

    MemLevel(Integer level, String desc) {
        this.level = level;
        this.desc = desc;
    }

    public Integer getLevel() {
        return level;
    }
}
/**
 * VIP 顾客
 */
@Component
public class SuperVipDiscount implements IDisCount {

    @Override
    public MemLevel getType() {
        return MemLevel.SUPER_VIP;
    }

    @Override
    public void operation() {
        // 逻辑操作
        System.out.println("八折");
    }
}

其他的情况

如果出现在某个范围使用的一个策略的方式该怎么处理

现在将消费的等级修改为消费的总金额范围
我们只需要将接口中的getType() (获取类型) 改为一个具体的范围即可

/**
 * 打折接口
 */
public interface IDisCount {

    /**
     * 用来判断范围
     * @param value
     * @return
     */
    Boolean isMatch(int value);

    /**
     * 具体的打着操作
     */
    void operation();
}

具体的实现

/**
 * 普通顾客
 */
@Component
public class NormalDisCount implements IDisCount{

    @Override
    public Boolean isMatch(int value) {
        return value > 0 && value < 100;
    }

    @Override
    public void operation() {
        System.out.println("原价");
    }
}

Service层

@Service
public class ShoppingService implements ApplicationContextAware {

    List<IDisCount> strategy;

    /**
     * 根据总共消费的金额来判断打折的方式
     * @param price
     */
    public void shopping(int price) {
        // 获取相应的策略
        IDisCount iDisCount = strategy.stream().
                filter(strategy -> strategy.isMatch(price))
                .findFirst()
                .get();

        // 执行策略操作
        iDisCount.operation();
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

        // 获取ioc容器中的所有IDisCount组件, 加入到 List<IDisCount> strategy;
        Map<String, IDisCount> beansOfType = applicationContext.getBeansOfType(IDisCount.class);
        List<IDisCount> iDisCounts = new ArrayList<>();

        beansOfType.forEach((k, v) -> {
            iDisCounts.add(v);
        });
        this.strategy = iDisCounts;
    }
}

可以看出使用java8 的新特性可以轻松的解决

如果 策略处理的参数类型和返回值类型不同

可以使用模板方法设计模式
使用抽象类替代接口,继承该抽象类, 各个具体的实现只需要重写抽象类中某个方法即可


版权声明:本文为qq_46054356原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。