八股文之设计模式
策略模式
业务场景
现在有这样的一个场景,关联系统送过来一批文件,根据不同类型采取不同解析方式,很多小伙伴肯定会写出一下代码
if(type=="A"){
// 按照A格式解析
}else if("B".equals(type)){
// 按照B格式解析
}else if("C".equals(type)){
// 按照C格式解析
}
。。。
后面还有多种格式
这个代码会出现什么问题?
如果分支变多,这里代码变得臃肿,难以维护,可读性低
如果需要接入新的解析类型,只能在原来代码上修改
专业一点就是以上代码违背面向对象编程开闭原则和单一原则
- 开闭原则:对扩展开放,对修改关闭,增加或者删除某个逻辑,需要修改原来代码
- 单一原则:规定一个类只有一个发生变化原因,修改任何类型的分支逻辑代码,都需要改动当前类代码
这时候我们的利器来了 “策略模式”
定义和理解
定义一组算法,将每个算法都封装起来,并且使它们之间可以互换。
是不是有点抽象,举个通俗易懂例子
孙权看刘备有雄起之意,杀是不能杀了,那会惹天下人唾弃,就想个招儿收拾他一下,那有什么办法呢?孙权有个妹妹——孙尚香,准备招刘备做女婿,然后孙权想办法把刘备软禁起来,孙权的想法还是很单纯的嘛,就是不让你刘备回西川,然后我东吴想干啥就干啥,夺荆州,吞西川也不是不可能的。东吴的想法是好的,无奈中间多了智谋无敌的诸葛亮,他早就预测了东吴有此招数,于是在刘备去东吴招亲之前,特授以伴郎赵云三个锦囊,说是按天机拆开解决棘手问题。
这三个妙计分别是:找乔国老帮忙(也就是走后门了),求吴国太放行(诉苦)以及孙夫人断后,对这三个妙计不熟悉的读者可以去温习一下《三国演义》,这里就不多说了。想想看,这三个计谋有什么相似之处,他们都是告诉赵云要怎么执行,也就是说这三个计谋都有一个方法是执行,具体执行什么内容,每个计谋当然不同了,分析到这里,我们是不是就有这样一个设计思路:三个妙计应该实现的是同一个接口?
策略模式针对一组算法,将每一个算法封装到共同的接口独立的类中,从而使得他们可以相互转换。
应用场景
● 多个类只有在算法或行为上稍有不同的场景。
● 算法需要自由切换的场景。
● 需要屏蔽算法规则的场景。
UML
策略模式使用
- 一个接口或者抽象类,里面包含两个方法,一个方法匹配类型,一个是可以替换的逻辑实现方法
- 不同策略差异化实现(不同实现类)
- 使用策略模式
妙计接口
public interface IStrategy {
//每个锦囊妙计都是一个可执行的算法
public void operate();
}
第一个妙计:乔国老开后门
@Component
public class BackDoor implements IStrategy {
@Override
public void operate() {
System.out.println("找乔国老帮忙,让吴国太给孙权施加压力");
}
}
第二个妙计:是找吴国太哭诉,企图给自己开绿灯
@Component
public class GivenGreenLight implements IStrategy {
@Override
public void operate() {
System.out.println("求吴国太开绿灯,放行!");
}
}
第三个妙计是在逃跑的时候,让新娘子孙夫人断后,谁来砍谁,这是非常好的主意
@Component
public class BlockEnemy implements IStrategy {
@Override
public void operate() {
System.out.println("孙夫人断后,挡住追兵");
}
}
在这个场景中,三个妙计都有了,那还缺少两个配角:第一,妙计肯定要放到一个地方吧,这么重要的东西要保管呀,也就是承装妙计的锦囊,所以俗称锦囊妙计嘛;第二,这些妙计都要有一个执行人吧,是谁?当然是赵云了,妙计是小亮给的,执行者是赵云。赵云就是一个干活的人,从锦囊中取出妙计,执行,然后获胜。过程非常清晰,我们把完整的过程设计出来
锦囊
public class Context {
//构造函数,你要使用哪个妙计
private IStrategy straegy;
public Context(IStrategy strategy){
this.straegy = strategy;
}
//使用计谋了,看我出招了
public void operate(){
this.straegy.operate();
}
}
通过构造函数把策略传递进来,然后用operate()方法来执行相关的策略方法。三个妙计有了,锦囊也有了,然后就是赵云雄赳赳地揣着三个锦囊,拉着已步入老年行列的、还想着娶纯情少女的刘老爷子去入赘了。嗨,还别说,小亮同志的三个妙计还真是不错
public class ZhaoYun {
//赵云出场了,他根据诸葛亮给他的交代,依次拆开妙计
public static void main(String[] args) {
Context context;
//刚刚到吴国的时候拆第一个
System.out.println("---刚刚到吴国的时候拆第一个---");
context = new Context(new BackDoor()); //拿到妙计
context.operate(); //拆开执行
System.out.println("\n\n\n\n\n\n\n\n");
//刘备乐不思蜀了,拆第二个了
System.out.println("---刘备乐不思蜀了,拆第二个了---");
context = new Context(new GivenGreenLight());
context.operate(); //执行了第二个锦囊
System.out.println("\n\n\n\n\n\n\n\n");
//孙权的小兵追来了,咋办?拆第三个
System.out.println("---孙权的小兵追来了,咋办?拆第三个---");
context = new Context(new BlockEnemy());
context.operate(); //孙夫人退兵
System.out.println("\n\n\n\n\n\n\n\n");
}
}
单例模式
业务场景
定义和理解
实践应用
UML
工厂模式
业务场景
定义和理解
实践应用
UML
责任链模式
业务场景
定义和理解
实践应用
UML
观察者模式
业务场景
登录注册是最常见的业务场景了,就拿注册来说是,我们经常遇到类似情况,用户注册成功之后,给用户发送短信或者发送邮件,有如下代码
void register(User user){
insertRegister(user);
sendMsm();
sendMobileMsg();
}
这部分代码有什么问题呢,如果产品添加新的需求,现在注册成功的用户,再给发送一条短信通知,于是乎我们又要修改register方法代码,是不是违反开闭原则
并且调用发短信的接口失败了,是不是影响用户注册,这时候是不是得加个异步方法给通知才可以
实际上我们可以使用观察者模式优化
定义和理解
定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新
举例说明:韩非子大家都应该记得吧,法家的代表人物,主张建立法制社会,实施重罚制度,真是非常有远见呀!看看现在社会在呼吁什么,建立法制化的社会,这在2000多年前就已经提出了。大家可能还不知道,法家还有一个非常重要的代表人物——李斯。李斯是秦国的丞相,最终被残忍车裂的那位,李斯和韩非子都是荀子的学生,李斯是师兄,韩非子是师弟,若干年后,李斯成为最强诸侯国秦国的上尉,致力于统一全国,于是安插了间谍到各个国家的重要人物的身边,以获取必要的信息,韩非子作为韩国的重量级人物,身边自然有不少间谍,韩非子做的事,李斯都了如指掌,那可是相隔千里!怎么做到的呢?间谍呀!我们先通过程序把这个过程展现一下,看看李斯是怎么监控韩非子的
实践应用
完成某件事之后,异步通知场景比如办理业务成功发送短信,邮件
订阅发布
UML
我们先来解释一下观察者模式的几个角色名称:
● Subject被观察者
定义被观察者必须实现的职责,它必须能够动态地增加、取消观察者。它一般是抽象类或者是实现类,仅仅完成作为被观察者必须实现的职责:管理观察者并通知观察者。
● Observer观察者
观察者接收到消息后,即进行update(更新方法)操作,对接收到的信息进行处理。
● ConcreteSubject具体的被观察者
定义被观察者自己的业务逻辑,同时定义对哪些事件进行通知。
● ConcreteObserver具体的观察者
每个观察在接收到消息后的处理反应是不同,各个观察者有自己的处理逻辑。
观察者模式使用
观察者
/**
* @description 观察者
*/
public interface Observer {
void doEvent();
}
被观察者
package com.geekmice.onetomany.eventbus;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
public class Subject {
private Vector<Observer> observers = new Vector<>();
private Integer state;
public Integer getState() {
return state;
}
public void setState(Integer state) {
notifyAllObservers(state);
}
/**
* 添加观察者
*/
public void addServer(Observer observer) {
observers.add(observer);
}
/**
* @description 删除观察者
*/
public void removeServer(Observer observer) {
observers.remove(observer);
}
/**
* @param state
* @description 通知所有观察者
* */
private void notifyAllObservers(Integer state) {
if (state != 1) {
System.out.println("不是通知的状态");
return;
}
for (Observer obj : observers) {
obj.doEvent();
}
}
}
发送邮件被观察者
package com.geekmice.onetomany.eventbus;
public class EmailObserver implements Observer{
@Override
public void doEvent() {
System.out.println("发送email");
}
}
发送短信被观察者
package com.geekmice.onetomany.eventbus;
public class MSMessageObserver implements Observer {
@Override
public void doEvent() {
System.out.println("发送MSM短信");
}
}
场景类
package com.geekmice.onetomany.eventbus;
public class Context {
public static void main(String[] args) {
Subject observerAble = new Subject();
observerAble.setState(1);
Observer observer = new EmailObserver();
observerAble.addServer(observer);
observer.doEvent();
Observer msMessageObserver = new MSMessageObserver();
observerAble.addServer(observer);
msMessageObserver.doEvent();
}
}
eventbus优化
Guava EventBus封装好观察者模式,提供了一套基于注解的事件总线,api可以灵活的使用,美滋滋
被观察者
package com.geekmice.onetomany.eventbus;
import com.google.common.eventbus.EventBus;
public class EventBusCenter {
private static EventBus eventBus = new EventBus();
private EventBusCenter() {
}
public static EventBus getInstance() {
return eventBus;
}
/**
* @description 添加观察者
* @param obj
*/
public static void register(Object obj) {
eventBus.register(obj);
}
/**
* @description 删除观察者
* @param object
*/
public static void unregister(Object object) {
eventBus.unregister(object);
}
/**
* @description 把消息推送给观察者
* @param object
*/
public static void post(Object object) {
eventBus.post(object);
}
}
观察者
package com.geekmice.onetomany.eventbus;
import com.google.common.eventbus.Subscribe;
// 声明观察者
public class EventListener {
// 加了订阅,这里标记这个方法是事件处理方法
@Subscribe
public void handle(NotifyEvent notifyEvent) {
System.out.println("发送IM消息" + notifyEvent.getImNo());
System.out.println("发送短信消息" + notifyEvent.getMobileNo());
System.out.println("发送mail消息" + notifyEvent.getEmailNo());
}
}
事件通知类
package com.geekmice.onetomany.eventbus;
// 通知事件类
public class NotifyEvent {
private String mobileNo;
private String emailNo;
private String imNo;
public NotifyEvent(String mobileNo, String emailNo, String imNo) {
this.mobileNo = mobileNo;
this.emailNo = emailNo;
this.imNo = imNo;
}
public String getMobileNo() {
return mobileNo;
}
public void setMobileNo(String mobileNo) {
this.mobileNo = mobileNo;
}
public String getEmailNo() {
return emailNo;
}
public void setEmailNo(String emailNo) {
this.emailNo = emailNo;
}
public String getImNo() {
return imNo;
}
public void setImNo(String imNo) {
this.imNo = imNo;
}
}
demo测试
package com.geekmice.onetomany.eventbus;
public class Client {
public static void main(String[] args) {
EventListener eventListener = new EventListener();
EventBusCenter.register(eventListener);
EventBusCenter.post(new NotifyEvent("1660702****", "1233445@163.com", "666"));
}
}
发送IM消息666
发送短信消息1660702****
发送mail消息1233445@163.com
策略模式