《设计模式面试小炒》策略和工厂模式替代业务场景中复杂的ifelse

我是肥哥,一名不专业的面试官!

我是囧囧,一名积极找工作的小菜鸟!

囧囧表示:小白面试最怕的就是面试官问的知识点太笼统,自己无法快速定位到关键问题点!!!


本期主要面试考点

面试官考点之如何用设计模式替换业务场景中复杂的ifelse?

VIP类型

import java.util.Objects;

/**
* @author: 欢迎关注喂信公猪号:囧么肥事
* @date: 2021/12/16
* @email: jiongmefeishi@163.com
*
* 会员类型
*/
public enum VIPEnums { GOLD(1, "黄金会员"),
STAR(2, "星钻会员"),
SPORTS(3, "体育会员"),
FUN_VIP(4, "FUN会员"); private final int code;
private final String desc; VIPEnums(int code, String desc) {
this.code = code;
this.desc = desc;
} public int getCode() {
return code;
} public String getDesc() {
return desc;
} public static VIPEnums getByCode(Integer code) {
for (VIPEnums s : VIPEnums.values()) {
if (Objects.equals(s.getCode(), code)) {
return s;
}
}
return null;
}
}

VIP实体

/**
* @author: 欢迎关注喂信公猪号:囧么肥事
* @date: 2021/12/16
* @email: jiongmeifeishi@163.com
*
* vip
*/
public class VIP { private VIPEnums vipType; // TODO VIP 其他属性 id, name ... public VIP() {
} public VIP(VIPEnums vipType) {
this.vipType = vipType;
} public VIPEnums getVipType() {
return vipType;
} public void setVipType(VIPEnums vipType) {
this.vipType = vipType;
}
}

if-else 模式

// if-else 模式
public class App {
public static void main( String[] args ) { // 黄金会员
VIP vip = new VIP(VIPEnums.GOLD); if (vip.getVipType().getCode() == VIPEnums.GOLD.getCode()) {
// TODO 黄金会员权益
} else if (vip.getVipType().getCode() == VIPEnums.STAR.getCode()) {
// TODO 星钻会员权益
} else if (vip.getVipType().getCode() == VIPEnums.SPORTS.getCode()) {
// TODO 体育会员权益
} else if (vip.getVipType().getCode() == VIPEnums.FUN_VIP.getCode()) {
// TODO FUN会员权益
} else {
// TODO 其他会员...
} }
}

策略模式

VIP策略接口

/**
* @author: 欢迎关注喂信公猪号:囧么肥事
* @date: 2021/12/16
* @email: jiongmefeishi@163.com
* VIP 策略接口
*/
public interface VIPStrategy { // VIP 具备的权益
void equity();
}

策略接口具体实现类-黄金会员

/**
* @author: 欢迎关注喂信公猪号:囧么肥事
* @date: 2021/12/16
* @email: jiongmefeishi@163.com
*
* 策略接口具体实现类-黄金会员
*/
public class GoldVIPStrategyImpl implements VIPStrategy { @Override
public void equity() {
// TODO 黄金会员具备的具体权益
}
}

策略接口具体实现类-星钻会员

/**
* @author: 欢迎关注喂信公猪号:囧么肥事
* @date: 2021/12/16
* @email: jiongmefeishi@163.com
*
* 策略接口具体实现类-星钻会员
*/
public class StarVIPStrategyImpl implements VIPStrategy { @Override
public void equity() {
// TODO 星钻会员具备的具体权益
}
}

策略接口具体实现类-体育会员

/**
* @author: 欢迎关注喂信公猪号:囧么肥事
* @date: 2021/12/16
* @email: jiongmefeishi@163.com
*
* 策略接口具体实现类-体育会员
*/
public class SportsVIPStrategyImpl implements VIPStrategy { @Override
public void equity() {
// TODO 体育会员具备的具体权益
}
}

策略接口具体实现类-FUN会员

/**
* @author: 欢迎关注喂信公猪号:囧么肥事
* @date: 2021/12/16
* @email: jiongmefeishi@163.com
*
* 策略接口具体实现类-FUN会员
*/
public class FunVIPStrategyImpl implements VIPStrategy { @Override
public void equity() {
// TODO FUN会员具备的具体权益
}
}

策略上下文类

/**
* @author: 欢迎关注喂信公猪号:囧么肥事
* @date: 2021/12/16
* @email: jiongmefeishi@163.com
*
* 策略上下文类( vip 策略接口的持有者)
*/
public class VIPStrategyContext { private VIPStrategy vipStrategy; // 设置VIP策略
public void setVipStrategy(VIPStrategy vipStrategy) {
this.vipStrategy = vipStrategy;
} // 执行 VIP 权益
public void handle() {
if (vipStrategy != null) {
vipStrategy.equity();
}
}
}

策略工厂

/**
* @author: 欢迎关注喂信公猪号:囧么肥事
* @date: 2021/12/16
* @email: jiongmefeishi@163.com
*
* VIP策略工厂
*/
public class VIPStrategyFactory { private VIPStrategyFactory() {
} public static VIPStrategy getVipStrategy(VIP vip) {
VIPStrategy vipStrategy = null; if (vip.getVipType().getCode() == VIPEnums.GOLD.getCode()) {
// 黄金会员策略实现类
vipStrategy = new GoldVIPStrategyImpl();
} else if (vip.getVipType().getCode() == VIPEnums.STAR.getCode()) {
// 星钻会员策略实现类
vipStrategy = new StarVIPStrategyImpl();
} else if (vip.getVipType().getCode() == VIPEnums.SPORTS.getCode()) {
// 体育会员策略实现类
vipStrategy = new SportsVIPStrategyImpl();
} else if (vip.getVipType().getCode() == VIPEnums.FUN_VIP.getCode()) {
// FUN会员策略实现类
vipStrategy = new FunVIPStrategyImpl();
} else {
// 其他会员...
} return vipStrategy;
}
}

模拟会员登录获取权益

/**
* @author: 欢迎关注喂信公猪号:囧么肥事
* @date: 2021/12/16
* @email: jiongmefeishi@163.com
*
* 模拟会员登录获取权益
*/
public class TestStrategy { public static void main(String[] args) { // 黄金会员
VIP vip = new VIP(VIPEnums.GOLD); // 策略上下文,执行者
VIPStrategyContext context = new VIPStrategyContext(); // 根据会员类型,获取会员具体策略,获取黄金会员策略
VIPStrategy strategy = VIPStrategyFactory.getVipStrategy(vip); // 绑定给执行者
context.setVipStrategy(strategy); // 执行黄金会员的策略,黄金权益
context.handle();
}
}

我们知道, 策略模式的本身设计出来的目的是封装一系列的算法,这些算法都具有共性,可以相互替换,算法独立于使用它的客户端独立变化,客户端不需要了解关注算法的具体实现,客户端仅仅依赖于策略接口 。

通过使用策略模式和工厂模式结合,是不是感觉变得高大上起来了呢?

当然了,最主要的是程序的扩展来说更方便了一些,更符合开闭原则,开放扩展,关闭修改。无论新增多少种新类型的会员,每个人只需要去继承策略接口,实现新会员应有的权益即可。

注意,虽然利于扩展,但是策略模式的缺点也很明显,策略工厂在创建具体的策略实现类的时候,还是书写大量的 if-else 去进行判断,如图

有小伙伴就说了这和不使用策略模式和工厂模式似乎差不多???

抽出一个方法或者封装成一个对象去调用岂不是更简单???

接下来,我们就说说如何优化策略工厂

首先,我们的工厂,是根据当前传入的用户的会员类型,判断后,返回相应的策略实现类,那么可以借助集合来存储实现类,会员类型作为 key,将所有的会员策略都注册到 map 中。需要注意的是,日常开发基于Spring进行bean管理,上面需要创建的策略类,当然都是希望被 Spring 动态托管,而不是我们自己去一个个的new 出实例。

问题是,如何去实现策略类通过spring进行托管注册?

Spring种提供的InitializingBean接口,这个接口为Bean提供了属性初始化后的处理方法,它只包括afterPropertiesSet方法,凡是继承该接口的类,在bean的属性初始化后都会执行该方法。我们利用此方法把Spring通过IOC创建出来的Bean注册Map 中。

改造策略工厂


import org.example.model.VIP;
import org.example.strategy.VIPStrategy; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; /**
* @author: 欢迎关注喂信公猪号:囧么肥事
* @date: 2021/12/16
* @email: jiongmefeishi@163.com
* <p>
* VIP策略工厂
*/
public class VIPStrategyFactory { // 存储策略类实例
public static Map<Integer, VIPStrategy> strategyMap = new ConcurrentHashMap<>(); private VIPStrategyFactory() {
} public static VIPStrategy getVipStrategy(VIP vip) {
if (vip == null) {
return null;
}
return strategyMap.get(vip.getVipType().getCode());
}
}

改造策略类,在bean属性初始化后,将实例对象注册到工厂类中的 map

以黄金会员为例:

import org.example.factory.VIPStrategyFactory;
import org.example.model.VIPEnums;
import org.example.strategy.VIPStrategy;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Service; /**
* @author: 欢迎关注喂信公猪号:囧么肥事
* @date: 2021/12/16
* @email: jiongmefeishi@163.com
*
* 策略接口具体实现类-黄金会员
*/
@Service
public class GoldVIPStrategyImpl implements VIPStrategy, InitializingBean { @Override
public void equity() {
// TODO 黄金会员具备的具体权益
System.out.println("黄金会员具备的具体权益");
} @Override
public void afterPropertiesSet() throws Exception {
VIPStrategyFactory.strategyMap.put(VIPEnums.GOLD.getCode(), new GoldVIPStrategyImpl());
}
}

通过策略模式、工厂模式以及Spring的InitializingBean接口,算是解决了大量的if else,后续新VIP出现也更容易扩展,当然了,这里只是对于设计模式思想的一个简单的示例,实际应用开发中,还是要根据具体的业务场景灵活变通。有需要的小伙伴也可以自己手动模拟一些场景,比如奶茶店各种奶茶新品等等。如果想用囧囧的示例,可公猪号上回复220110 自行导入示例运行即可。

注意:学习软件设计原则,千万不能形成强迫症。当碰到业务复杂的场景时,需要随机应变。

学习设计原则是学习设计模式的基础。在实际开发过程中,并不是一定要求所有代码都遵循设计原则,而是要综合考虑人力、时间、成本、质量,不刻意追求完美,要在适当的场景遵循设计原则。这体现的是一种平衡取舍,可以帮助我们设计出更加优雅的代码结构。

设计模式其实也是一门艺术。设计模式源于生活,不要为了套用设计模式而使用设计模式。

喜欢的小伙伴,欢迎点赞收藏关注

《设计模式面试小炒》策略和工厂模式替代业务场景中复杂的ifelse的更多相关文章

  1. Java设计模式(三) 抽象工厂模式

    原创文章,同步发自作者个人博客,转载请注明出处 http://www.jasongj.com/design_pattern/abstract_factory/ 抽象工厂模式解决的问题 上文<工厂 ...

  2. Java设计模式(一) 简单工厂模式不简单

    摘要:本文介绍了简单工厂模式的概念,优缺点,实现方式,以及结合Annotation和反射的改良方案(让简单工厂模式不简单).同时介绍了简单工厂模式(未)遵循的OOP原则.最后给出了简单工厂模式在JDB ...

  3. 设计模式(3)抽象工厂模式(Abstract Factory)

    设计模式(0)简单工厂模式 设计模式(1)单例模式(Singleton) 设计模式(2)工厂方法模式(Factory Method) 源码地址 0 抽象工厂模式简介 0.0 抽象工厂模式定义 抽象工厂 ...

  4. 设计模式(二 & 三)工厂模式:概述

    工厂 从 coding 的角度来说,在需要创建对象的时候,直接在方法内部使用 new 关键字来创建,是非常方便的. 然而从全局的角度考虑,这样会使对象变得难以管理和控制,代码会变得非常脆弱,缺乏弹性. ...

  5. PHP设计模式(三)抽象工厂模式(Abstract Factory)

    一.什么是抽象工厂模式 抽象工厂模式的用意为:给客户端提供一个接口,可以创建多个产品族中的产品对象 ,而且使用抽象工厂模式还要满足以下条件: 系统中有多个产品族,而系统一次只可能消费其中一族产品. 同 ...

  6. Java设计模式学习笔记(二) 简单工厂模式

    前言 本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址 正文开始... 1. 简介 简单工厂模式不属于GoF23中设计模式之一,但在软件开发中应用也较为 ...

  7. Java设计模式学习笔记(四) 抽象工厂模式

    前言 本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址 1. 抽象工厂模式概述 工厂方法模式通过引入工厂等级结构,解决了简单工厂模式中工厂类职责太重的问 ...

  8. C#设计模式学习笔记:简单工厂模式(工厂方法模式前奏篇)

    本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/7551373.html,记录一下学习过程以备后续查用. 一.引言 简单工厂模式并不属于GoF23里面的设计模式 ...

  9. Java设计模式之(二)——工厂模式

    1.什么是工厂模式 Define an interface for creating an object,but let subclasses decide which class toinstant ...

随机推荐

  1. 如何在 GitHub 上高效阅读源码?

    原文链接: 如何在 GitHub 上高效阅读源码? 之前听说过一个故事,一个领导为了提高团队战斗力,把团队成员集中起来,搞封闭开发,重点还是在没有网的条件下. 结果就是一个月过去了,产出基本为零. 我 ...

  2. java web 404错误页面配置

    java web 404错误页面配置:注意红框的地方,在工程的web.xml文件里的最开头加入如下的内容便可,但是也有问题,针对以.action后缀名和.jsp后缀名不起作用, 因为后面配置了一些拦截 ...

  3. React-Router(一)

    React-Router基础知识 import React from "react"; import { BrowserRouter as Router, Switch, Rout ...

  4. 【LeetCode】857. Minimum Cost to Hire K Workers 解题报告(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 题目地址: https://leetcode.com/problems/minimum- ...

  5. ORA-14450: 试图访问已经在使用的事务处理临时表

    需要对临时表动态添加列,经常碰到表在事务中被使用的情况,如果可以的话,可以现在只用临时表的时候先truncate,这样可以终止事务对当前临时表的占用. execute immediate('trunc ...

  6. 响应式网页设计(Bootstrap)

    Bootstrap官网 AOS官网 Chrome官方教程 Chrome教程 Bootstrap官网中有许多Bootstrap网站示例,大家可以参考

  7. MySQL8.0的下载与安装

    下载 进入官网的下载页面 点击下图中的链接 可以选择上边的 Community Server ,那样会下载压缩包,这里我选择下边的 Installer for Windows ,下载的是安装包 点击下 ...

  8. 使用.NET 6开发TodoList应用(14)——实现查询过滤

    系列导航及源代码 使用.NET 6开发TodoList应用文章索引 需求 在查询请求中,还有一类常见的场景是过滤查询,也就是有限制条件的查询,落在数据库层面就是常用的Where查询子句.实现起来也很简 ...

  9. windows下的Python的下载与安装

    Python的下载 Python下载要去官网下载,xdm,这里是网址 www.python.org 因为是外网所以打开下载会慢一些(不要着急的说) 这是python官网界面,跟着图片去下载(因为我这会 ...

  10. rabbimq集群搭建报错:Error: unable TO perform an operation ON node 'rabbit@test3'. Please see diagnostics information AND suggestions below.

    在搭建rabbitmq集群的时候,添加内存节点时,抛出异常:Error: unable TO perform an operation ON node 'rabbit@test3'. Please s ...