虽然你觉得大炮肯定是个标题党,但你终究还是点进来了(别打我,手动狗头保命),毕竟这性价比也太高了,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种统统安排上。我们下期见。
往期推荐
-
蔡大炮准备正式开张了
-
家雄又写bug了
微信搜索 java-caidapao ,关注大炮~回复“面试题”,领取2020最新面试题