不积跬步,无以至千里;不积小流,无以成江海
前些时间,在图书馆借阅了《重构-改善既有代码的设计》这本书,书中列举的代码中的坏味道,不就是自己平时写过的代码吗?
话不多说,先上一段伪代码,有什么问题吗?
public Result addMessage() throws Exception{
//此处省略n行代码
//发短信
smsUtil.sendSms();
//消息推送
jpushUtils.sendClient();
//发送微信
...
return ResultUtil.success();
}
确实没问题,是能够正常运行的。不过,如果现在要增加发送微信的功能,去掉消息推送呢?也很简单,添加一个发送微信的方法,然后再注释掉消息推送的方法。但是,这样真的好吗?
六大设计原则
-
单一职责原则
一个类只负责一个功能领域中的相应职责。高内聚、低耦合。
-
开闭原则
对扩展开放,对修改关闭。不修改原有代码的情况下进行扩展。
-
里氏代换原则
所有引用基类(父类)的地方必须能透明地使用其子类的对象。
-
依赖倒转原则
抽象不应该依赖于细节,细节应当依赖于抽象。
-
接口隔离原则
接口拆分,当一个接口太大时,我们需要将它分割成一些更细小的接口。
-
迪米特法则
减少依赖,一个软件实体应当尽可能少地与其他实体发生相互作用。
很显然,上面的修改方式已经违背了单一职责和开闭原则。有没有什么办法呢,其实Spring为我们提供了事件通知机制。
ApplicationEvent以及Listener是Spring为我们提供的一个事件监听、订阅的实现,内部实现原理是观察者设计模式,设计初衷也是为了系统业务逻辑之间的解耦,提高可扩展性以及可维护性。事件发布者并不需要考虑谁去监听,监听具体的实现内容是什么,发布者的工作只是为了发布事件而已。
观察者模式
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。又称:发布/订阅、消息通知机制 、事件监听、事件驱动编程。
接下来,利用观察者模式设计高拓展性的代码,利用spring事件机制改造消息处理功能:
事件监听类
/**
* 事件监听类
*/
public class ConfigEvent extends ApplicationEvent {
public ConfigEvent(Object source) {
//source:传入的参数
super(source);
}
}
监听ConfigEvent事件
/**
* 发送短信
*/
@Component
public class SmsListener implements ApplicationListener<ConfigEvent> {
@Override
public void onApplicationEvent(ConfigEvent configEvent) {
System.out.println("2:短信发送成功!" + configEvent.getSource());
}
}
@Component
public class WeChatListener implements ApplicationListener<ConfigEvent> {
@Override
public void onApplicationEvent(ConfigEvent configEvent) {
System.out.println("3:微信发送成功!" + configEvent.getSource());
}
}
消息处理类
/**
* 消息处理
*/
@Service
public class MessageService{
@Resource
private ApplicationContext applicationContext;
public void addMessage() {
//业务
System.out.println("1:消息添加成功");
ConfigEvent configEvent = new ConfigEvent("内容:857857");
//事件发布
applicationContext.publishEvent(configEvent);
}
}
测试方法
@Autowired
private MessageService messageService;
@Test
public void Test() {
messageService.addMessage();
}
打印结果:
1:消息添加成功
2:短信发送成功!内容:857857
3:微信发送成功!内容:857857
这样修改后,即使再增加发送邮件,也只需要增加一个类去监听原有事件,而不用去修改原有的代码,便于后期的扩展和维护。
Spring内置事件
-
ApplicationContextEvent
spring内置事件的父抽象类,构造方法传入spring的context容器,同时也有获取spring的context容器的方法。
-
ContextRefreshedEvent
当spring容器初始化或刷新时,会触发此事件。此事件在开发中常用,用于在spring容器启动时,导入自定义的bean实例到spring容器中。
-
ContextStartedEvent
当spring启动时,或者说是context调用start()方法时,会触发此事件。
-
ContextStoppedEvent
当spring停止时,或者说context调用stop()方法时,会触发此事件。
-
ContextClosedEvent
当spring关闭时,或者说context调用close()方法时,会触发此事件。