需求:商品有三种折扣价,普通客户不享受任何优惠,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);
该方法的缺点是当要增加业务时,得去枚举中添加相应的数据。