跟Spring学设计模式-让你的代码更优雅——总结
先把总结写在前头:利用Spring和设计模式搭配写出更优秀的代码。
先发点牢骚,公司里的代码有点年龄,有些类写的又臭又长,每天要面对这些代码进行开发,不仅消磨了对技术的热情也让人变得毫无斗志,反正都已经这样了,那我也就这么来吧。相信不少小伙伴也是跟我有着同样的遭遇与困惑。但唯一不能停下来的就是进步,即使面对恶龙还是不能放弃抵抗。
在做需求的时候,也不能去修改那些代码,太耗时太费劲,领导也不会同意,那自己起码也要思考一遍如何设计代码才能去避免以后出现同样的情况,让自己下次不要犯同样的错误。讲师推荐了一本书叫《重构 改善既有代码的设计》-Martin Fowler,里面讲述了22种代码的坏味道,如重复的代码,过长的参数列,散弹式修改,基本型偏执等等,不一一展开。
那如何设计代码,什么才算是好的代码?这里需要一把尺子,一把可以量出代码好坏的尺子。六大设计原则就是这样一把好尺子。六大设计原则应该不存在没听过这一说。
拿其中的“职责单一原则”说一说,职责单一是什么,一个类只负责一个功能领域中的相应职责。高内聚,低耦合。那什么时候会违背职责单一原则。因为 职责扩散 ,所谓 职责扩散 ,就是因为某种原因,本来单一的职责需要被分化成多个职责。举个例子,统计一个月的清单,这个简单,几行代码就可以搞定。那如果要统计多个子公司全部的清单呢? 数据量太大,拿一个简单的方法去跑,效率就太慢,这时就引入了大数据框架帮忙解决。一个职责统计清单,变成了两个职责大数据处理和统计清单。职责扩散就很容易出现,因为初期太不容易察觉了。
面对小范围的代码可以说是对粒度的把控。粒度太大,职责不单一,导致高复杂度的脆弱代码。粒度太小,导致高耦合,同样脆弱。如何把控粒度,真是一门艺术。如何把控这粒度的方法,从大神那里偷学了2招,
一:面向可控的设计,控制的是复杂度,把多个复杂的问题耦合到一个类甚至一个方法内,这可不是线性增长,是指数增长。三个复杂度为N的问题耦合到一起,复杂度变成了N^3,而不是3N。
提示: 一个类最多只实现一个复杂逻辑,把复杂度的指数增长变成线性增长。
二:面向扩展的设计
可扩展的重要性不言而喻。扩展是针对技术和业务趋势而言的,要有前瞻性。如果有类似“会有更好的框架”,“多种算法”等等的情况,一定要发挥你的设计才能,预留相应的扩展点。发现了扩展点,就要做成接口,剥离相关实现。这是设计模式擅长的事情,不展开。
提示: 针对技术和业务趋势,预留扩展点,并剥离扩展点的实现。
而且大神还告诉我:Java,一处编写,跨平台到处运行,把平台差异从业务代码中剥离了吧,是职责问题。突然就感觉这职责单一问题好像突然跳跃了一个维度,真有趣。
有了可以衡量代码好坏的尺子后,再选择合适的设计模式对代码进行设计。
废话不多说,直接上例子。
需求一:用户在完成事件A之后能够得到短信通知。
第一种写法:
/**
* 业务伪代码。忽略具体逻辑,参数封装、数据查询等
*/
public void underwrite() {
// 1. 业务操作
log.info("1. 事件A操作成功");
// 2. 短信通知
log.info("2. 短信通知,调用短信发送接口");
}
很简单,但也存在问题。当增加新的需求,比如增加APP通知时,只能再修改类去添加调用方法,这就违反了上一节讲的六大设计原则中的开闭原则了。
其实Spring当中就有现成的列子供我们学习使用。Spring启动后,会执行内部的一系列操作,我们也经常定义Spring容器的启动,执行一些特定的业务代码,如初始化字典表、启动为执行的调度任务,加载缓存等,而我们无需改动Spring的源码就能实现功能的扩展。这里Spring就使用了观察者模式的设计模式。
观察者模式:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
在Spring中观察者模式有4个角色:
事件(ApplicationEvent):事件对象通继承ApplicationEvent类
事件监听(ApplicationListener):ApplicationListener 事件监听器,也就是观察者。继承自 jdk 的 EventListener,该类中只有一个方法 onApplicationEvent。当监听的事件发生后该方法会被执行。
** 事件发布(ApplicationContext)**:ApplicationContext 是 Spring 中的核心容器,在事件监听中 ApplicationContext 可以作为事件的发布者,也就是事件源。因为 ApplicationContext 继承自 ApplicationEventPublisher。在 ApplicationEventPublisher 中定义了事件发布的方法 — publishEvent(Object event)
事件管理(ApplicationEventMulticaster):ApplicationEventMulticaster 用于事件监听器的注册和事件的广播。监听器的注册就是通过它来实现的,它的作用是把 Applicationcontext 发布的 Event 广播给它的监听器列表。
我们先定义一个业务操作成功事件,继承ApplicationEvent实现
public class BusinessAcceptanceEvent extends ApplicationEvent {
/**
* Create a new {@code ApplicationEvent}.
*
* @param source the object on which the event initially occurred or with
* which the event is associated (never {@code null})
*/
public BusinessAcceptanceEvent(Object source) {
super(source);
}
}
再定义业务部分代码,事件监听类
public class AppListener implements ApplicationListener<BusinessAcceptanceEvent> {
@Override
public void onApplicationEvent(BusinessAcceptanceEvent event) {
log.info("1. APP通知,调用APP消息推送接口");
}
}
public class MessageListener implements ApplicationListener<BusinessAcceptanceEvent> {
@Override
public void onApplicationEvent(BusinessAcceptanceEvent event) {
log.info("2. 短信通知,调用短信发送接口");
}
}
发布事件
public class InsuranceAcceptanceServiceDesign {
@Autowired
private ApplicationContext applicationContext;
/**
* 业务操作
* 业务伪代码。忽略具体逻辑,参数封装、数据查询等
*/
@Deprecated
public void underwrite() {
// 1. 事件A操作
log.info("1. 事件A操作成功");
// 2. 短信通知
log.info("2. 短信通知,调用短信发送接口");
}
/**
* 事件A。
* 利用Spring事件机制改造,利用观察者模式设计高扩展性的代码
*/
public void underwriteDesign() {
// 1. 业务操作
log.info("事件A操作成功");
// 2. 发布事件A操作成功事件
InsuranceAcceptanceEvent acceptanceEvent = new InsuranceAcceptanceEvent("事件A");
applicationContext.publishEvent(acceptanceEvent);
// 不需要其他业务操作。可以像扩展Spring一样,扩展我们的后续业务逻辑
}
}
这样我们就通过利用Spirng的事件机制轻松完成了一个需求。且符合六大设计原则,也便利于以后扩展。