策略模式:在我们实际开发场景中, 经常遇到多种情况、多种类型对应不同的解决方案,很多开发者都是采用if else if 这种解决方案,实际情况是, 过多的if else 导致业务类过于庞大,如果新增类型,要对业务类进行修改,不利于维护,也违反开闭原则。策略模式正是为了解决这一问题。
下面我们假设一种简单的业务场景:一个会员有normal、vip、svip 三种等级,每种等级对应不同的价格优惠策略(不打折、八折、五折);我们根据一个会员的信息去计算他所要支付的最终的价格。
下面是会员类, 只有会员名称和会员类型
package com.ceair;
public class Member {
// 会员名称
public String name;
// 会员类型 --- normal VIP SVIP
public String type;
public Member(String name, String type) {
super();
this.name = name;
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
在没有用策略模式之前, 我们一般会写出下面这样的方法,用if else 去分辨不同的处理策略,上面也说了,这种写法不太好,我见过超过十个if else的代码
/**
* 根据会员级别, 算出价格
* @date 2020年4月9日
* @param type
* @param originalPrice
* @return
*/
public static BigDecimal getPrice (String type, BigDecimal originalPrice) {
BigDecimal price = BigDecimal.ZERO;
if ("normal".equals(type)) {
price = originalPrice.multiply(BigDecimal.ONE);
} else if ("vip".equals(type)) {
price = originalPrice.multiply(BigDecimal.valueOf(0.8));
} else if ("svip".equals(type)) {
price = originalPrice.multiply(BigDecimal.valueOf(0.5));
}
return price;
}
我们初始化两个会员,看下价格
Member m = new Member("lisi", "vip");
BigDecimal price = Price.getPrice(m.getType(), BigDecimal.valueOf(100));
System.out.println("会员名称:" + m.getName() + "###会员等级:" + m.getType() + "###实际价格:" + price.intValue());
System.out.println("--------");
Member m2 = new Member("zhangsan", "svip");
BigDecimal price2 = Price.getPrice(m2.getType(), BigDecimal.valueOf(100));
System.out.println("会员名称:" + m2.getName() + "###会员等级:" + m2.getType() + "###实际价格:" + price2.intValue());
返回结果:
会员名称:lisi###会员等级:vip###实际价格:80
--------
会员名称:zhangsan###会员等级:svip###实际价格:50
------------------------分割线-----------------------------
接下来,我们用策略模式来实现
一、首先抽象出价格策略接口
package com.ceair;
import java.math.BigDecimal;
public interface PriceStrategy {
BigDecimal getPrice (BigDecimal orignalPrice);
}
二、然后是具体的vip和svip实现类
package com.ceair;
import java.math.BigDecimal;
public class VipPriceStrategy implements PriceStrategy {
@Override
public BigDecimal getPrice(BigDecimal orignalPrice) {
return orignalPrice.multiply(BigDecimal.valueOf(0.8));
}
}
package com.ceair;
import java.math.BigDecimal;
public class SvipPriceStrategy implements PriceStrategy {
@Override
public BigDecimal getPrice(BigDecimal orignalPrice) {
return orignalPrice.multiply(BigDecimal.valueOf(0.5));
}
}
三、接着我们把会员等级和其对应的策略实现类的具体地址放在配置环境中
四、我们根据会员类型,取出对应策略的实现类地址,通过反射,获取到实例,调用获取价格方法, 得到价格。
Member m = new Member("lisi", "vip");
Class<?> clazz = Class.forName(CacheUtil.getI18nVal(m.getType()));
PriceStrategy s = (PriceStrategy)clazz.newInstance();
BigDecimal price = s.getPrice(BigDecimal.valueOf(100));
System.out.println("会员名称:" + m.getName() + "###会员等级:" + m.getType() + "###实际价格:" + price.intValue());
System.out.println("--------");
Member m2 = new Member("zhangsan", "svip");
Class<?> clazz2 = Class.forName(CacheUtil.getI18nVal(m2.getType()));
PriceStrategy s2 = (PriceStrategy)clazz2.newInstance();
BigDecimal price2 = s2.getPrice(BigDecimal.valueOf(100));
System.out.println("会员名称:" + m2.getName() + "###会员等级:" + m2.getType() + "###实际价格:" + price2.intValue());
五、返回结果
会员名称:lisi###会员等级:vip###实际价格:80
--------
会员名称:zhangsan###会员等级:svip###实际价格:50
总计: 用策略模式后, 业务类代码更明确了,如果有新的会员等级加入, 我们也不需要修改任何之前的代码,只需要再写一个实现类,加上配置即可, 符合开闭原则。