实战!聊聊工作中使用了哪些设计模式 您所在的位置:网站首页 工作模式都有哪些 实战!聊聊工作中使用了哪些设计模式

实战!聊聊工作中使用了哪些设计模式

2024-02-22 14:55| 来源: 网络整理| 查看: 265

前言

大家好,我是捡田螺的小男孩。

平时我们写代码呢,多数情况都是流水线式写代码,基本就可以实现业务逻辑了。如何在写代码中找到乐趣呢,我觉得,最好的方式就是:使用设计模式优化自己的业务代码。今天跟大家聊聊日常工作中,我都使用过哪些设计模式。

工作中常用到哪些设计模式

干货公众号:捡田螺的小男孩 我的github地址,感谢给个star 1.策略模式 1.1 业务场景

假设有这样的业务场景,大数据系统把文件推送过来,根据不同类型采取不同的解析方式。多数的小伙伴就会写出以下的代码:

if(type=="A"){ //按照A格式解析 }else if(type=="B"){ //按B格式解析 }else{ //按照默认格式解析 }

这个代码可能会存在哪些问题呢?

如果分支变多,这里的代码就会变得臃肿,难以维护,可读性低。 如果你需要接入一种新的解析类型,那只能在原有代码上修改。

说得专业一点的话,就是以上代码,违背了面向对象编程的开闭原则以及单一原则。

开闭原则(对于扩展是开放的,但是对于修改是封闭的):增加或者删除某个逻辑,都需要修改到原来代码 单一原则(规定一个类应该只有一个发生变化的原因):修改任何类型的分支逻辑代码,都需要改动当前类的代码。

如果你的代码就是酱紫:有多个if...else等条件分支,并且每个条件分支,可以封装起来替换的,我们就可以使用策略模式来优化。

1.2 策略模式定义

策略模式定义了算法族,分别封装起来,让它们之间可以相互替换,此模式让算法的变化独立于使用算法的的客户。这个策略模式的定义是不是有点抽象呢?那我们来看点通俗易懂的比喻:

假设你跟不同性格类型的小姐姐约会,要用不同的策略,有的请电影比较好,有的则去吃小吃效果不错,有的去逛街买买买最合适。当然,目的都是为了得到小姐姐的芳心,请看电影、吃小吃、逛街就是不同的策略。

策略模式针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。

1.3 策略模式使用

策略模式怎么使用呢?酱紫实现的:

一个接口或者抽象类,里面两个方法(一个方法匹配类型,一个可替换的逻辑实现方法) 不同策略的差异化实现(就是说,不同策略的实现类) 使用策略模式 1.3.1 一个接口,两个方法 public interface IFileStrategy { //属于哪种文件解析类型 FileTypeResolveEnum gainFileType(); //封装的公用算法(具体的解析方法) void resolve(Object objectparam); } 1.3.2 不同策略的差异化实现

A 类型策略具体实现

@Component public class AFileResolve implements IFileStrategy { @Override public FileTypeResolveEnum gainFileType() { return FileTypeResolveEnum.File_A_RESOLVE; } @Override public void resolve(Object objectparam) { logger.info("A 类型解析文件,参数:{}",objectparam); //A类型解析具体逻辑 } }

B 类型策略具体实现

@Component public class BFileResolve implements IFileStrategy { @Override public FileTypeResolveEnum gainFileType() { return FileTypeResolveEnum.File_B_RESOLVE; } @Override public void resolve(Object objectparam) { logger.info("B 类型解析文件,参数:{}",objectparam); //B类型解析具体逻辑 } }

默认类型策略具体实现

@Component public class DefaultFileResolve implements IFileStrategy { @Override public FileTypeResolveEnum gainFileType() { return FileTypeResolveEnum.File_DEFAULT_RESOLVE; } @Override public void resolve(Object objectparam) { logger.info("默认类型解析文件,参数:{}",objectparam); //默认类型解析具体逻辑 } } 1.3.3 使用策略模式

如何使用呢?我们借助spring的生命周期,使用ApplicationContextAware接口,把对用的策略,初始化到map里面。然后对外提供resolveFile方法即可。

/** * @author 公众号:捡田螺的小男孩 */ @Component public class StrategyUseService implements ApplicationContextAware{ private Map iFileStrategyMap = new ConcurrentHashMap(); public void resolveFile(FileTypeResolveEnum fileTypeResolveEnum, Object objectParam) { IFileStrategy iFileStrategy = iFileStrategyMap.get(fileTypeResolveEnum); if (iFileStrategy != null) { iFileStrategy.resolve(objectParam); } } //把不同策略放到map @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { Map tmepMap = applicationContext.getBeansOfType(IFileStrategy.class); tmepMap.values().forEach(strategyService -> iFileStrategyMap.put(strategyService.gainFileType(), strategyService)); } } 2. 责任链模式 2.1 业务场景

我们来看一个常见的业务场景,下订单。下订单接口,基本的逻辑,一般有参数非空校验、安全校验、黑名单校验、规则拦截等等。很多伙伴会使用异常来实现:

public class Order { public void checkNullParam(Object param){ //参数非空校验 throw new RuntimeException(); } public void checkSecurity(){ //安全校验 throw new RuntimeException(); } public void checkBackList(){ //黑名单校验 throw new RuntimeException(); } public void checkRule(){ //规则拦截 throw new RuntimeException(); } public static void main(String[] args) { Order order= new Order(); try{ order.checkNullParam(); order.checkSecurity (); order.checkBackList(); order2.checkRule(); System.out.println("order success"); }catch (RuntimeException e){ System.out.println("order fail"); } } }

这段代码使用了异常来做逻辑条件判断,如果后续逻辑越来越复杂的话,会出现一些问题:如异常只能返回异常信息,不能返回更多的字段,这时候需要自定义异常类。

并且,阿里开发手册规定:禁止用异常做逻辑判断。

【强制】 异常不要用来做流程控制,条件控制。 说明:异常设计的初衷是解决程序运行中的各种意外情况,且异常的处理效率比条件判断方式要低很多。

如何优化这段代码呢?可以考虑责任链模式

2.2 责任链模式定义

当你想要让一个以上的对象有机会能够处理某个请求的时候,就使用责任链模式。

责任链模式为请求创建了一个接收者对象的链。执行链上有多个对象节点,每个对象节点都有机会(条件匹配)处理请求事务,如果某个对象节点处理完了,就可以根据实际业务需求传递给下一个节点继续处理或者返回处理完毕。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。

责任链模式实际上是一种处理请求的模式,它让多个处理器(对象节点)都有机会处理该请求,直到其中某个处理成功为止。责任链模式把多个处理器串成链,然后让请求在链上传递:

责任链模式

打个比喻:

假设你晚上去上选修课,为了可以走点走,坐到了最后一排。来到教室,发现前面坐了好几个漂亮的小姐姐,于是你找张纸条,写上:“你好, 可以做我的女朋友吗?如果不愿意请向前传”。纸条就一个接一个的传上去了,后来传到第一排的那个妹子手上,她把纸条交给老师,听说老师40多岁未婚...

2.3 责任链模式使用

责任链模式怎么使用呢?

一个接口或者抽象类 每个对象差异化处理 对象链(数组)初始化(连起来) 2.3.1 一个接口或者抽象类

这个接口或者抽象类,需要:

有一个指向责任下一个对象的属性 一个设置下一个对象的set方法 给子类对象差异化实现的方法(如以下代码的doFilter方法) /** * 关注公众号:捡田螺的小男孩 */ public abstract class AbstractHandler { //责任链中的下一个对象 private AbstractHandler nextHandler; /** * 责任链的下一个对象 */ public void setNextHandler(AbstractHandler nextHandler){ this.nextHandler = nextHandler; } /** * 具体参数拦截逻辑,给子类去实现 */ public void filter(Request request, Response response) { doFilter(request, response); if (getNextHandler() != null) { getNextHandler().filter(request, response); } } public AbstractHandler getNextHandler() { return nextHandler; } abstract void doFilter(Request filterRequest, Response response); } 2.3.2 每个对象差异化处理

责任链上,每个对象的差异化处理,如本小节的业务场景,就有参数校验对象、安全校验对象、黑名单校验对象、规则拦截对象

/** * 参数校验对象 **/ @Component @Order(1) //顺序排第1,最先校验 public class CheckParamFilterObject extends AbstractHandler { @Override public void doFilter(Request request, Response response) { System.out.println("非空参数检查"); } } /** * 安全校验对象 */ @Component @Order(2) //校验顺序排第2 public class CheckSecurityFilterObject extends AbstractHandler { @Override public void doFilter(Request request, Response response) { //invoke Security check System.out.println("安全调用校验"); } } /** * 黑名单校验对象 */ @Component @Order(3) //校验顺序排第3 public class CheckBlackFilterObject extends AbstractHandler { @Override public void doFilter(Request request, Response response) { //invoke black list check System.out.println("校验黑名单"); } } /** * 规则拦截对象 */ @Component @Order(4) //校验顺序排第4 public class CheckRuleFilterObject extends AbstractHandler { @Override public void doFilter(Request request, Response response) { //check rule System.out.println("check rule"); } } 2.3.3 对象链连起来(初始化)&& 使用 @Component("ChainPatternDemo") public class ChainPatternDemo { //自动注入各个责任链的对象 @Autowired private List abstractHandleList; private AbstractHandler abstractHandler; //spring注入后自动执行,责任链的对象连接起来 @PostConstruct public void initializeChainFilter(){ for(int i = 0;i iFileStrategyMap.put(strategyService.gainFileType(), strategyService)); } } 5.2 使用工厂模式

定义工厂模式也是比较简单的:

一个工厂接口,提供一个创建不同对象的方法。 其子类实现工厂接口,构造不同对象 使用工厂模式 5.3.1 一个工厂接口 interface IFileResolveFactory{ void resolve(); } 5.3.2 不同子类实现工厂接口 class AFileResolve implements IFileResolveFactory{ void resolve(){ System.out.println("文件A类型解析"); } } class BFileResolve implements IFileResolveFactory{ void resolve(){ System.out.println("文件B类型解析"); } } class DefaultFileResolve implements IFileResolveFactory{ void resolve(){ System.out.println("默认文件类型解析"); } } 5.3.3 使用工厂模式 //构造不同的工厂对象 IFileResolveFactory fileResolveFactory; if(fileType=“A”){ fileResolveFactory = new AFileResolve(); }else if(fileType=“B”){ fileResolveFactory = new BFileResolve(); }else{ fileResolveFactory = new DefaultFileResolve(); } fileResolveFactory.resolve();

一般情况下,对于工厂模式,你不会看到以上的代码。工厂模式会跟配合其他设计模式如策略模式一起出现的。

6. 单例模式 6.1 业务场景

单例模式,保证一个类仅有一个实例,并提供一个访问它的全局访问点。 I/O与数据库的连接,一般就用单例模式实现de的。Windows里面的Task Manager(任务管理器)也是很典型的单例模式。

来看一个单例模式的例子

/** * 公众号:捡田螺的小男孩 */ public class LanHanSingleton { private static LanHanSingleton instance; private LanHanSingleton(){ } public static LanHanSingleton getInstance(){ if (instance == null) { instance = new LanHanSingleton(); } return instance; } }

以上的例子,就是懒汉式的单例实现。实例在需要用到的时候,才去创建,就比较懒。如果有则返回,没有则新建,需要加下 synchronized 关键字,要不然可能存在线性安全问题。

6.2 单例模式的经典写法

其实单例模式还有有好几种实现方式,如饿汉模式,双重校验锁,静态内部类,枚举等实现方式。

6.2.1 饿汉模式 public class EHanSingleton { private static EHanSingleton instance = new EHanSingleton(); private EHanSingleton(){ } public static EHanSingleton getInstance() { return instance; } }

饿汉模式,它比较饥饿、比较勤奋,实例在初始化的时候就已经建好了,不管你后面有没有用到,都先新建好实例再说。这个就没有线程安全的问题,但是呢,浪费内存空间呀。

6.2.2 双重校验锁 public class DoubleCheckSingleton { private static DoubleCheckSingleton instance; private DoubleCheckSingleton() { } public static DoubleCheckSingleton getInstance(){ if (instance == null) { synchronized (DoubleCheckSingleton.class) { if (instance == null) { instance = new DoubleCheckSingleton(); } } } return instance; } }

双重校验锁实现的单例模式,综合了懒汉式和饿汉式两者的优缺点。以上代码例子中,在synchronized关键字内外都加了一层 if 条件判断,这样既保证了线程安全,又比直接上锁提高了执行效率,还节省了内存空间。

6.2.3 静态内部类 public class InnerClassSingleton { private static class InnerClassSingletonHolder{ private static final InnerClassSingleton INSTANCE = new InnerClassSingleton(); } private InnerClassSingleton(){} public static final InnerClassSingleton getInstance(){ return InnerClassSingletonHolder.INSTANCE; } }

静态内部类的实现方式,效果有点类似双重校验锁。但这种方式只适用于静态域场景,双重校验锁方式可在实例域需要延迟初始化时使用。

6.2.4 枚举 public enum SingletonEnum { INSTANCE; public SingletonEnum getInstance(){ return INSTANCE; } }

枚举实现的单例,代码简洁清晰。并且它还自动支持序列化机制,绝对防止多次实例化。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有