(二)策略模式+反射-解决多重if-else if问题

  • 需求:商品有三种折扣价,普通客户不享受任何优惠,vip客户享受9折优惠,超级vip客户享受8折优惠

当没有用到设计模式时,我们一般会采用下面的方式处理业务

 int type = 1;
 if(type == 1){
      System.out.println("普通客户,商品原价出售");
 }else if(type == 2){
      System.out.println("vip客户,商品9折出售");
 }else if(type == 3){
      System.out.println("超级vip客户,商品8折出售");
 }

这样做的好处是直观,能以下看懂其中的逻辑关系,但是弊端更多,当要推出第四种优惠活动时,我们不得不改变源码,重新加else-if 判断语句,这样不符合开发开闭原则,耦合性太大,这时可以用策略模式来解决该问题。

  • 抽象策略类
public interface CustomerStrategy {
    //策略方法
    public double discountPrice(double orgnicPrice);
}
  • 具体策略类
/** 
 * 普通客户
 */
public class OrgnicCustomer implements CustomerStrategy {
 
    @Override
    public double discountPrice(double orgnicPrice) {
        return orgnicPrice;
    }
}
 
 
/**
 *  vip客户
 */
public class VipCustomer implements CustomerStrategy {
 
    @Override
    public double discountPrice(double orgnicPrice) {
        return orgnicPrice * 0.9;
    }
}
 
/**
 * 超级vip客户
 */
public class SuperVipCustomer implements CustomerStrategy {
 
    @Override
    public double discountPrice(double orgnicPrice) {
        return orgnicPrice * 0.8;
    }
}
  • 环境角色类
@Data
@NoArgsConstructor
public class ExcuteContext {
 
    //持有一个具体策略的对象
    private CustomerStrategy customerStrategy;
 
    private double money;
 
    /**
     * 构造函数,传入一个具体策略对象
     */
    public ExcuteContext(CustomerStrategy customerStrategy) {
        this.customerStrategy = customerStrategy;
    }
 
    /**
     * 策略方法
     */
    public double excute(){
        return customerStrategy.discountPrice(money);
    }
}

测试类

 int type = 1;
 ExcuteContext excuteContext = new ExcuteContext();
 if(type == 1){
     excuteContext.setCustomerStrategy(new OrgnicCustomer());
 }else if(type == 2){
     excuteContext.setCustomerStrategy(new VipCustomer());
 }else if(type == 3){
     excuteContext.setCustomerStrategy(new SuperVipCustomer());
 }
 
 excuteContext.setMoney(200);
 double price = excuteContext.excute();
 System.out.println("最终价格为:" + price);

策略模式将一个个具体的策略继承一个策略接口,具体的策略方法在子策略中实现,最后通过一个角色类来统一处理业务。

但是这并没有解决if-else问题,细看测试类,我们不难看出,如果我们能根据类型来自动地得到不用的对象,就可以摒弃if-else的问题。我们可以想想,我们能通过不同的值获取其对应的数据,在开发中应该是一种什么样的存储方式呢。对了,就是以key-value键值对存储的map,如果我们能将type和对象以map的方式存储,在需要得到对象时通过key值我们就能的得到对应的对象了。但是怎么存储map呢,在这里我介绍两种形式以作参考。

  • 方法一:属性文件 + 反射
  1. application.yml
customer:
  discount :
    1 : com.huace.thread.com.huace.pattern.strategy.OrgnicCustomer
 
    2 : com.huace.thread.com.huace.pattern.strategy.VipCustomer
 
    3 : com.huace.thread.com.huace.pattern.strategy.SuperVipCustomer
  1. 属性文件获取及反射得到对象
/**
 * @Description:
 * @Auther: chenmingjian
 * @Date: 18-10-17 16:19
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@Configuration
@ConfigurationProperties(prefix = "customer")
@PropertySource(value = "classpath:/application.yml",ignoreResourceNotFound = true)
public class PropertiesConfig {
    
    //获取属性文件
    public  Map<String,String> discount = new HashMap<>();
 
    //通过反射得到不同的对象
    public <T> T getBean(int type){
        if(!CollectionUtils.isEmpty(discount)){
            if (discount.containsKey(String.valueOf(type))) {
                try {
                    Class clazz = Class.forName(discount.get(String.valueOf(type)));
                    return (T) clazz.newInstance();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }
}
  1. 测试类
 int type = 2;
 ExcuteContext excuteContext = new ExcuteContext();
 CustomerStrategy customer = propertiesConfig.getBean(type);
 excuteContext.setCustomerStrategy(customer);
 excuteContext.setMoney(200);
 double price = excuteContext.excute();
 System.out.println("最终价格为:" + price);

这样我们就可以替代if-else语句了,但是当属性文件中的类名被修改时,则找不到对应的对象。

  • 方法二:枚举
  1. 定义枚举类
@Getter
@NoArgsConstructor
@AllArgsConstructor
public enum TypeEnum {
 
    ORGNIC(1,"com.huace.pattern.strategy.OrgnicCustomer","普通客户"),
    VIP(2,"com.huace.pattern.strategy.VipCustomer","vip客户"),
    SUPERVIP(3,"com.huace.pattern.strategy.SuperVipCustomer","超级vip客户"),;
 
    private int type;
    private String className;
    private String decs;
}
  1. 通过反射+枚举得到对象
@Data
@AllArgsConstructor
@Configuration
public class PropertiesConfig {
 
    //通过反射得到不同的对象
    public <T> T getBean(int type){
        TypeEnum[] values = TypeEnum.values();
        if(values != null && values.length > 0){
            for(TypeEnum value : values){
                try {
                    if(type == value.getType()){
                        Class clazz = Class.forName(value.getClassName());
                        return (T) clazz.newInstance();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }
}
  1. 测试类
CustomerStrategy customer = propertiesConfig.getBean(type);
int type = 2;
ExcuteContext excuteContext = new ExcuteContext(); 
propertiesConfig.getBean(type);
excuteContext.setCustomerStrategy(customer);
excuteContext.setMoney(200);
double price = excuteContext.excute();
System.out.println("最终价格为:" + price);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值