假设现在有一个业务:一个用户去购物消费
- 如果他是普通用户,那么说就是原价
- 如果他是普通会员,那么就是打9折
- 如果他是高级会员,那么就是打8折
- 如果他是超级会员,那么就是打7折
用户实体类User
import java.math.BigDecimal;
@Data
public class User {
// 普通会员/高级会员/超级会员
private String lev;
// 对应折扣
private BigDecimal discount;
public User() {
}
public User(String lev, BigDecimal discount) {
this.lev = lev;
this.discount = discount;
}
}
执行业务逻辑
public static void main(String[] args) {
BigDecimal total = new BigDecimal("100.00");
BigDecimal nowTotal = new BigDecimal("100.00");
User user = new User("高级会员", new BigDecimal("0.8"));
if ("普通用户".equals(user.getLev())){
nowTotal = total.multiply(user.getDiscount());
}else if ("普通会员".equals(user.getLev())){
nowTotal = total.multiply(user.getDiscount());
}else if ("高级会员".equals(user.getLev())){
nowTotal = total.multiply(user.getDiscount());
}else if ("超级会员".equals(user.getLev())){
nowTotal = total.multiply(user.getDiscount());
}
System.out.println(String.format("原价:%s,因为你是%s,现价%s元",total,user.getLev(),nowTotal));
}
此时要是业务修改了 需要添加黄金会员、铂金会员、砖石会员...此时将会产生一堆的if else
语句,所以需要改造这个方法
策略模式
首先我们定义一个策略模式的接口,把共同行为抽象成一个接口
/**
* 策略模式接口
*/
public interface Handler {
// 处理对应折扣
public String handler(BigDecimal total,User user);
}
将每一个用户对应的业务封装成一个类
/**
* 普通用户
*/
public class GeneralUserHandler implements Handler {
@Override
public String handler(BigDecimal total,User user) {
return String.format("原价:%s,因为你是%s,现价%s元",total,user.getLev(),total);
}
}
/**
* 普通会员
*/
public class GeneralVIPUserHandler implements Handler {
@Override
public String handler(BigDecimal total,User user) {
BigDecimal general = new BigDecimal("0.9");
return String.format("原价:%s,因为你是%s,现价%s元",total,user.getLev(),total.multiply(general));
}
}
/**
* 高级会员
*/
public class SeniorVIPUserHandler implements Handler {
@Override
public String handler(BigDecimal total,User user) {
BigDecimal senior = new BigDecimal("0.8");
return String.format("原价:%s,因为你是%s,现价%s元",total,user.getLev(),total.multiply(senior));
}
}
/**
* 超级会员
*/
public class SuperVIPUserHandler implements Handler {
@Override
public String handler(BigDecimal total,User user) {
BigDecimal superVip = new BigDecimal("0.7");
return String.format("原价:%s,因为你是%s,现价%s元",total,user.getLev(),total.multiply(superVip));
}
}
现在执行方式变成为,现在看起来好像没有什么卵用,没有吧if else
去掉反而感觉代码越来越多了
public static void main(String[] args) {
BigDecimal total = new BigDecimal("100.00");
BigDecimal nowTotal = new BigDecimal("100.00");
User user = new User("高级会员", new BigDecimal("0.8"));
String res = "" ;
if ("普通用户".equals(user.getLev())){
Handler handler = new GeneralUserHandler();
res = handler.handler(total,user);
}else if ("普通会员".equals(user.getLev())){
Handler handler = new GeneralVIPUserHandler();
res = handler.handler(total,user);
}else if ("高级会员".equals(user.getLev())){
Handler handler = new SeniorVIPUserHandler();
res = handler.handler(total,user);
}else if ("超级会员".equals(user.getLev())){
Handler handler = new SuperVIPUserHandler();
res = handler.handler(total,user);
}
System.out.println(res);
}
工厂模式
那现在再利用工厂模式进行改造
定义一个工厂类
/**
* 工厂设计模式
*/
public class Factory {
// 定义一个全局变量,用于保存每一个策略类
private static Map<String,Handler> map = new HashMap<>();
// 生成获取方法
public static Handler getStrategy(String name){
return map.get(name);
}
// 生成注册方法
public static void register(String name,Handler handler){
if (StringUtils.isEmpty(name) || null == handler) {
return;
}
map.put(name,handler);
}
}
此时我们的策略接口需要修改一下
/**
* 策略模式接口
*/
public interface Handler extends InitializingBean {
public String handler(BigDecimal total,User user);
}
InitializingBean
:凡是继承该接口的类,在初始化bean的时候都会执行该方法
我们的策略类同时也需要修改,需要注意@Component
/**
* 普通用户
*/
@Component
public class GeneralUserHandler implements Handler {
@Override
public String handler(BigDecimal total,User user) {
return String.format("原价:%s,因为你是%s,现价%s元",total,user.getLev(),total);
}
@Override
public void afterPropertiesSet() throws Exception {
Factory.register("普通用户",this);
}
}
/**
* 普通会员
*/
@Component
public class GeneralVIPUserHandler implements Handler {
@Override
public String handler(BigDecimal total,User user) {
BigDecimal general = new BigDecimal("0.9");
return String.format("原价:%s,因为你是%s,现价%s元",total,user.getLev(),total.multiply(general));
}
@Override
public void afterPropertiesSet() throws Exception {
Factory.register("普通会员",this);
}
}
/**
* 高级会员
*/
@Component
public class SeniorVIPUserHandler implements Handler {
@Override
public String handler(BigDecimal total,User user) {
BigDecimal senior = new BigDecimal("0.8");
return String.format("原价:%s,因为你是%s,现价%s元",total,user.getLev(),total.multiply(senior));
}
@Override
public void afterPropertiesSet() throws Exception {
Factory.register("高级会员",this);
}
}
/**
* 超级会员
*/
@Component
public class SuperVIPUserHandler implements Handler {
@Override
public String handler(BigDecimal total,User user) {
BigDecimal superVip = new BigDecimal("0.7");
return String.format("原价:%s,因为你是%s,现价%s元",total,user.getLev(),total.multiply(superVip));
}
@Override
public void afterPropertiesSet() throws Exception {
Factory.register("超级会员",this);
}
}
最终效果:
public static void main(String[] args) {
BigDecimal total = new BigDecimal("100.00");
User user = new User("高级会员", new BigDecimal("0.8"));
Handler strategy = Factory.getStrategy("高级会员");
String handler = strategy.handler(total,user);
System.out.println(handler);
}
我们可以看到实际上看起来代码变少了,但是我们需要创建的策略类会逐渐变多,这也是策略模式的缺点
模板方法
模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。通俗的说的就是有很多相同的步骤的,在某一些地方可能有一些差别适合于这种模式,例如想要泡一杯茶或者一杯咖啡,第一步都是将水煮沸,第二部是加入咖啡或者茶,第三部就是搅拌匀中,第四部就是加入各种调味料。其中第一步和第三部都是一样的,这个就可以定义在基类,而第二步和第四步就是他们之间的差异就可以在具体的子类中去实现。
创建模板方法抽象类
/**
* 模板方法
*/
public abstract class MakingDrinks {
// 1.将水煮沸,由于这个方法是所以制作饮料的基础操作/共同方法
private void boiling(){
System.out.println("将水煮沸");
}
// 2.加入咖啡粉、茶叶或者炼奶
public abstract void addDrink();
// 3.搅拌融化
private void mixing(){
System.out.println("搅拌快速融化");
}
// 4.加入自己喜欢配料
public abstract void addBatching();
/**
* 制备饮料的模板方法
* 封装了所有子类共同遵守的算法骨架
*/
public final void preTemplage(){
//步骤一 将水煮沸
boiling();
//步骤二 炮制饮料
addDrink();
//步骤三 搅拌融化
mixing();
//步骤四 加入调味料
addBatching();
}
}
分别实现抽象类
public class Coffee extends MakingDrinks {
@Override
public void addDrink() {
System.out.println("加入雀巢的咖啡");
}
@Override
public void addBatching() {
System.out.println("加入初恋甜");
}
}
public class Tea extends MakingDrinks{
@Override
public void addDrink() {
System.out.println("加入上古龙井");
}
@Override
public void addBatching() {
System.out.println("加点其他装饰");
}
}
测试
public static void main(String[] args) {
MakingDrinks coffee = new Coffee();
coffee.preTemplage();
System.out.println("*****************");
MakingDrinks tea = new Tea();
tea.preTemplage();
}
整合
实际上策略模式和工厂模式一起使用能解决我们if else
的问题,但是如果我们需要给超级会员更多优惠,比如超级会员获取精美礼品
这个时候需要再接口上加上一个获取礼物的动作
/**
* 策略模式接口
*/
public interface Handler extends InitializingBean {
public String handler(BigDecimal total,User user);
public void getGift();
}
但是这个动作只有我们超级会员才能执行的,其他等级的用户是没有权限的,所以其他类要是重写这个方法就不合适,所以我们将其改模板方法
public abstract class Handler implements InitializingBean{
public String handler(BigDecimal total,User user){throw new UnsupportedOperationException();};
public void getGift(){throw new UnsupportedOperationException();};
}
如果没有重写该方法(没有特权)则会抛出异常。
public static void main(String[] args) {
BigDecimal total = new BigDecimal("100.00");
User user = new User("高级会员", new BigDecimal("0.8"));
Handler strategy = Factory.getStrategy("高级会员");
String handler = strategy.handler(total,user);
// 如果该用户没有获取礼物特权则抛出异常
strategy.getGift();
System.out.println(handler);
}
经过上面改造,后面如果新增成员或者新功能实现,新增策略实现即可。这种改造符合开闭原则,适用较为复杂的逻辑判断。