手记

设计模式 | 4分钟搞懂10种设计模式

虽然你觉得大炮肯定是个标题党,但你终究还是点进来了(别打我,手动狗头保命),毕竟这性价比也太高了,4分钟10种,如果是真的就赚大发了。

但是大炮可以肯定的告诉你,只要正儿八经的参与过几个项目,有个一两年的开发经验。你肯定已经用过并且可能已经理解了一些设计模式了,只是并不自知而已。今天大炮就来给你好好缕一缕。

单例、工厂模式

这两个设计模式大家都不陌生,很多读者也都已经自学过了。比如饿汉、懒汉单例模式,简单、抽象工厂模式。而且这两个模式,就像兄弟一样,经常结对出现。所以大炮放到一起来讲,我们先来看一行代码:

private static final Logger logger = LoggerFactory.getLogger(UserService.class);

非常眼熟吧,这是我们平时用来获取日志对象的一段代码,但是重点并不是这个 logger 而是 LoggerFactory,顾名思义用来专门用来造 logger 的工厂 ,是一个工厂模式很好的体现。同时作为工厂呢,它又是单例的,毕竟new一个工厂就够了嘛,自己实例化那么多个干嘛呢,单例杜绝了反复创建和销毁对象的开销

同时它又用到了门面模式,这行代码是不是很牛,用到了三个设计模式,我们接着往下看。


门面模式

在《阿里巴巴Java开发手册》中,关于日志章节中专门有提到:

【强制】应用中不可直接使用日志系统(Log4j、Logback)中的 API,而应依赖使用日志框架 SLF4J 中的 API,使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一。

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

private static final Logger logger = LoggerFactory.getLogger(Abc.class);

有点感觉没,门面模式其实就是把自己作为一个接口,自己并不提供真正的实现。什么,还是没到位?那我们看看下面这段代码:

是不是又很眼熟了,下单要干挺多事情,保存订单,保存订单项,减库存…或者还有其他业务,如果所有这些方法的实现都放到 createOrder 这个方法里面,那代码肯定是又长又臭,洋洋洒洒就奔着几百上千行去了。所以有经验的你们肯定会抽到好几个子方法里面,就像上面一样,然后挨个调用下就行了。(PS,真正的下单代码肯定不是这么玩的,高并发下肯定挂了,看个意思就好)

没错,这也是门面模式,createOrder 方法并没有给出具体的实现,直接调用了几个子方法而已。可能有些读者心里已经发出了嘲讽:**就这?就这?**这也算设计模式?我自己都会写啦。

没错,就这~设计模式就是对经验的总结,然后提出了一些建议和规范。其实并不是多么高深的东西。现在想想,如果给优秀的我们早生几年,有些设计模式的创造者还指不定是谁呢。回到上面的代码,虽然没多少技术含量,但对调用者而言,我管你怎么实现,我轻轻调一下 createOrder 方法就完事了。这正是门面模式的精髓所在。

嗯,一行代码 get 到了三个设计模式。咱们继续。


代理模式

代理模式,是一个非常重要的设计模式,不理解它可能会让你直接无缘大厂。大家一定要学会并理解这个东西。我们先来看一段代码:

How old are you ? 怎么老是你 ? 没错,还是上面的图。这次的关注点大家放在 @Transaction 上。注解本身没啥用,只是作为一个标记,有时候会携带一些配置信息。老开发已经知道我要讲啥了,这里用到了Spring 中一个非常的重要特性:AOP编程。而AOP的实现原理就是代理模式。

**代理模式的作用就是职责(或者说功能)增强。**那Spring帮我们做了哪些增强呢?上图的部分其实只有CRUD的业务代码,事务方面一行都没有。而没Spring之前,我们是要这样写的:

try {    
    //加载驱动
    Class.forName("com.mysql.jdbc.Driver");    
    //建立连接     
    Connection con = DriverManager.getConnection()   
    //开启事务    
    con.setAutoCommit(true/false);    
    //真正的、我们每天都要写的:)    
    C R U D    
    //提交事务    
    con.commit()
} catch (Exception e){   
    //失败了还要自己手动回滚    
    con.rollback();
} finally {    
    //关闭连接    
    conn.close();
}

写的伪代码,不用在意细节。重要的是如果每个需要用到事务的方法都要这么重复写,我们估计早疯了。这里Spring就将这些重复的代码使用动态代理给我们抽到代理类中去了,所以我们只用关注CRUD就成——从这个角度看,一定程度上造成了程序员的懒惰,不是我们不学习,只怪Spring太好用。

代理模式就不继续发散了,后面会精讲。


桥接模式

让我猜猜看,是不是有些同学第一次听到这个模式?猜对了待会下面给我留言点赞统计喔,但其实,我们每天都在用。咱们来看一段代码:

    //加载驱动    
    Class.forName("com.mysql.jdbc.Driver");    
    //建立连接     
    Connection con = DriverManager.getConnection()

没错,大炮又套娃了,用的就是上面的连接数据库的代码。大家每天都要连接数据库的吧。

先铺垫一个知识:Java 没有真正能操作数据库的方法,只是提供了一些抽象的接口,真正的实现在各个数据库厂商提供的相应jar包内。mysql就由mysql的开发者提供,oracle的就由oracle的提供。

为什么我们这个DriverManager能直接get到Connection呢,就是使用了桥接模式。关键就在于这个Class.forName(“com.mysql.jdbc.Driver”),Class.forName 只是加载了一个类而已,怎么做到的呢。我们来看这个Driver类到底干啥了:

其实就是一个静态块里面调用了java.sql.DriverManager 的 registeDriver方法,然后把mysql 的Driver对象传了过去,起到了一个搭桥的作用。

多的就不扩展了,桥接模式的作用就是连接了一个抽象维度和一个实现维度。这里的抽象维度就是指 java,实现维度就是指 mysql。其实也可以说桥接的精髓在于“约定”,java 和 各数据库厂商约定:你们底层实现我不管,但是你们得按照我的规矩来,调用我DriverManager的registerDriver方法才行。


中介者模式

聊完桥接模式,不得不聊下中介者模式了。桥接是关注两个维度的连接,而中介者是关注多个多个维度。作用是统一管理多个多个维度的网状资源。没错,可能你已经想到了,我们天天都在用的注册中心就是一个中介者,比如 eureka、zookeeper。


如图,所有的 service 都只和 Service Registry 耦合,而不是直接去依赖其他service ,中介者完美地解决了网状依赖的复杂关系带来的代码混乱。总体来说中介者还是个比较容易理解的设计模式,这里就不做太多展开了。


解释器模式

解释器模式是个非常简单的模式,有种自娱自乐的味道,如果你知道它是怎么自娱自乐的,那你也能跟着“娱乐一下”。来看一个熟悉的东东:

#每隔五秒执行一次
times = */5 * * * * ?

你的配置文件是不是也有corn表达式呢,这就是一个解释器模式的体现。可能现在你的内心又:

**解释器模式定义了一种表达式(或者说是语言),并且也提供了解析表达式(语言)的方法。**类似的还有正则表达式。Spring中的ExpressionParser也用到了解释器模式,有兴趣的同学可以去看看源码。


策略模式

策略模式也比较容易理解,大家在开发和日常生活中也经常用到,也是一个需要重点掌握的设计模式。每天买东西,使用手机支付,就是策略模式的一种体现。**策略模式只注重结果,不管使用哪种策略(策略模式要求策略的数量是有限的),最后的结果都是一样的。**比如手机支付,不管使用哪种支付方式,该付多少还是得付多少。


原型模式

原型模式也是一种非常简单,唉,凌晨一点多了。明天继续写吧。困了困了。

诶大炮又复活了。原型模式也是一种非常简单的设计模式:给定一个对象实例,通过拷贝的技术创建新的对象,达到对象复用的效果。来看一段代码:

UserVo userVO = new UserVO();
User user = mapper.selectByPrimaryKey(id);
if(user == null){
    throw new Exception("未获取到指定用户");
}
/* 拷贝数据,传给前端大佬渲染页面 */
BeanUtils.copyProperties(user,userVO);
return userVO;

是不是非常眼熟了,可能有些读者内心纷纷表示“是我本人没错了”。这里的BeanUtils.copyProperties 方法就用到了原型模式里面的 浅拷贝。

稍微提一句,不要使用Apache的 BeanUtils 来copy。性能问题比较严重,在《阿里巴巴Java开发手册》中也有专门提到。如果需要切到Spring的copy方法,也有个坑需要注意下,这两家的提供的copy,类名相同、方法名也相同。但参数的位置相反,切换时请多加注意并做好测试。


迭代器模式

迭代器模式大家也天天都在用,直接上代码:

这是一段生成签名时,拼接参数的算法。里面的 Iterator 就是迭代器模式的体现,给集合提供了一个全局访问点,通过hasNext()判断没有下一个节点,next()方法获取下个节点,直到所有节点都被访问过。我们常用的 HashMap ,ArrayList 顶层都是 Collection,而 Collection又继承了 Iterator 接口。来张ArrayList 的类图感受下:


小结

大炮稍微给大家缕了一遍,有些同学是不是觉得自己又行了,原来不知不觉间,自己竟然用了这么多设计模式,却不自知。其实还有很多,比如:

  • 责任链模式,Spring security、Shiro这些权限框架都有用到。
  • 观察者模式,Spring 的 ContextLoaderListener 就有用到。
  • 模板模式,Spring 中的 JdbcTemplate 有用到。

设计模式无处不在,平时开发我们也无时无刻在享受着设计模式带来的好处,只是有些同学缺乏一个系统的认知。由于篇幅原因就不再过多介绍了。

好吧,标题党不算太过分,超了三分钟嘻嘻。

下期预告

今天讲的还是比较片面和粗犷的,很多细节都没有体现出来。这一期相当于是个设计模式的面试尝鲜版,下一期会对设计模式做一个正式的介绍:

  • 为什么要用设计模式
  • 设计模式是什么
  • 设计模式有哪些
  • 设计模式的分类

大概就是上面这些了。然后就开始做具体某个模式的详细分析和代码实现(想先看什么模式记得私信我喔)。

今天就先到这了,下次去面试被问到用过哪些设计模式,心里是不是就有底了,直接给他一顿连招13种统统安排上。我们下期见。


0人推荐
随时随地看视频
慕课网APP