if-else还能用设计模式解决?试试工厂模式、策略模式和模板模式

假设现在有一个业务:一个用户去购物消费

  • 如果他是普通用户,那么说就是原价
  • 如果他是普通会员,那么就是打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();
}
image-20210302160920003.png

整合

实际上策略模式和工厂模式一起使用能解决我们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);
}

经过上面改造,后面如果新增成员或者新功能实现,新增策略实现即可。这种改造符合开闭原则,适用较为复杂的逻辑判断。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342