策略模式+反射-解决多重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

2. 属性文件获取及反射得到对象

/**
 * @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;
    }
}

3.测试类

 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;
}

2.通过反射+枚举得到对象

@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;
    }
}

3.测试类

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);

该方法的缺点是当要增加业务时,得去枚举中添加相应的数据。

    原文作者:冲奶粉的奶爸
    原文地址: https://blog.csdn.net/qq_32258777/article/details/83177688
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞