设计模式

设计模式

23个设计模式分类

总体来说设计模式分为三大类:

  • 创建型模式,共五种:

工厂方法模式、抽象工厂模式、单例模式、建造者(构造器)模式、原型模式。

  • 结构型模式,共七种:

适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

  • 行为型模式,共十一种:

策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

责任链模式

责任链模式在框架的引用极为广泛, 例如SpringSecurity的过滤器链, Sentinel的插槽(限流, 降级, 系统保护等),他们实现就像一条链子一样 一级一级的往下走。

在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。

建造者(构造器)模式

建造者模式:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示,建造者模式是一种对象创建型模式。:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示,建造者模式是一种对象创建型模式。

最常见的是Lombok的@Builder注解, 让我们使用流式创建对象。

: 每一个属性对应的方法都,返回Builder本身

  1. 通过一个public static class Builder的内部类,持有要构建对象的所有属性

  2. 设置属性的方法都返回Builder当前实例this,这样可以流式设置属性,用起来更加方便

  3. 提供一个static的Builder方法,方便用户获取Builder对象

  4. 用户通过builder对象自定义设置属性

  5. 提供build方法,创建最终对象

工厂方法模式

适用: 根据条件 判断生成什么对象

简单工厂模式

案例: 我们有一个生产课程对象的工厂(CourseFactory), 根据传入条件生产对象(JavaCourse,GoCourse…)

需求: 我们传入什么则生成什么对象

实现: 可以通过给工厂的create(自定义的创建生产的方法), 传入对象名、类名、字节码对象, 然后生产相对应的实例对象

此时的工厂模式有一个最大的特点就是工厂具体, 产品抽象, 工厂生产是根据条件生产, 只能生产一个类的课程

查看代码测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 生产的工厂
public class CourseFactory {
public ICourse create(Class<?> clazz){ // ICourse: 课程的抽象
try{
if(null != clazz){
return clazz.newInstance();
}
}catch(Exception e){
e.printStackTace();
}
}
}
// 测试
@Test
public void test(){
CourseFactory cf = new CourseFactory();
ICourse course = cf.create(JavaCourse.class);
}

工厂方法模式

产品抽象, 工厂也抽象。

Java课程对应一个Java工厂, Python课程对应一个Python工厂. 在某一个特定的工厂可以根据特定的条件生产特定的产品. 解耦合

例如: List的实现, 可以发现不同的具体实例实现有不同的遍历方法(指: 迭代器). 因为不同的实现, 它们底层的数据结构实现不同, 在LinkedList底层则是链表, ArrayList则是顺序表.

追踪ArrayList的源码中实现的迭代方法Iterator

1
2
3
4
// 这个就是工厂生产产品的方法, 生产了一个用于ArrayList的Itr迭代实例
public Iterator<E> iterator() {
return new Itr();
}
1
2
3
4
5
6
7
8
9
10
11
12
// 继续追踪Itr(), 可以发现next方法
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}

很容易知道, 其实就是一个顺序表的遍历

同理LinkedList遍历也是一样, 通过实现Iterator, 容易知道肯定是链表的遍历

1
2
3
4
5
6
7
8
9
10
public E next() {
checkForComodification();
if (!hasNext())
throw new NoSuchElementException();

lastReturned = next;
next = next.next;
nextIndex++;
return lastReturned.item;
}

所以有: List的各种实现类, 不同的数据结构实现所实现的迭代器(工厂方法–> 用于生产产品–> 迭代器产品).

在SpringCloud Gateway中, 我们可以看到关键组件有

  • 谓词工厂

用于定义路由规则。谓词工厂接收一个输入(通常是一个HTTP请求),并根据定义的规则返回一个布尔值,指示该请求是否与路由匹配。

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: food-service # 路由规则id,自定义,唯一
uri: lb://food-service # 路由的目标服务,lb代表负载均衡,会从注册中心拉取服务列表
predicates: # 路由断言,判断当前请求是否符合当前规则,符合则路由到目标服务
- Path=/resfood/** # 这里是以请求路径作为判断规则

predicates: 路由断言

现在使用该模式我们可以通过,根据条件创建一个对象, 在以往之前, 只能通过new一个具体对象

其他的案例还有: 1. Feign 2. JDK中的StringBuilder

  • 过滤器工厂

工厂模式和策略模式组合

例如我们现在有一个需求,对于传入的不同数据进行不同的处理(比如QQ发消息,他聊天框可以发送不同类型的消息,这个是不是就可以用策略模式+工厂模式进行推送处理,即我需要对不同的数据进行不同的处理)

那么就可以使用这个组合:

我们先定义一个抽象类模板:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/**
* Description: 消息处理器抽象类
*/
public abstract class AbstractMsgHandler<Req> {
@Autowired
private MessageDao messageDao;
private Class<Req> bodyClass;

@PostConstruct
private void init() {
ParameterizedType genericSuperclass = (ParameterizedType) this.getClass().getGenericSuperclass();
this.bodyClass = (Class<Req>) genericSuperclass.getActualTypeArguments()[0];
MsgHandlerFactory.register(getMsgTypeEnum().getType(), this);
}

/**
* 消息类型
*/
abstract MessageTypeEnum getMsgTypeEnum();

protected void checkMsg(Req body, Long roomId, Long uid) {

}

@Transactional
public Long checkAndSaveMsg(ChatMessageReq request, Long uid) {
Req body = this.toBean(request.getBody());
//统一校验
AssertUtil.allCheckValidateThrow(body);
//子类扩展校验
checkMsg(body, request.getRoomId(), uid);
Message insert = MessageAdapter.buildMsgSave(request, uid);
//统一保存
messageDao.save(insert);
//子类扩展保存
saveMsg(insert, body);
return insert.getId();
}
//
private Req toBean(Object body) {
if (bodyClass.isAssignableFrom(body.getClass())) {
return (Req) body;
}
return BeanUtil.toBean(body, bodyClass);
}
// 需要保存的逻辑
protected abstract void saveMsg(Message message, Req body);

/**
* 展示消息
*/
public abstract Object showMsg(Message msg);

/**
* 被回复时——展示的消息
*/
public abstract Object showReplyMsg(Message msg);

/**
* 会话列表——展示的消息
*/
public abstract String showContactMsg(Message msg);

}

提供一个实现类样例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@Component
public class EmojisMsgHandler extends AbstractMsgHandler<EmojisMsgDTO> {
@Autowired
private MessageDao messageDao;

@Override
MessageTypeEnum getMsgTypeEnum() {
return MessageTypeEnum.EMOJI;
}

@Override
public void saveMsg(Message msg, EmojisMsgDTO body) {
MessageExtra extra = Optional.ofNullable(msg.getExtra()).orElse(new MessageExtra());
Message update = new Message();
update.setId(msg.getId());
update.setExtra(extra);
extra.setEmojisMsgDTO(body);
messageDao.updateById(update);
}

@Override
public Object showMsg(Message msg) {
return msg.getExtra().getEmojisMsgDTO();
}

@Override
public Object showReplyMsg(Message msg) {
return "表情";
}

@Override
public String showContactMsg(Message msg) {
return "[表情包]";
}
}

然后我们需要提供一个工厂创建场景对象

1
2
3
4
5
6
7
8
9
10
11
12
13
public class MsgHandlerFactory {
private static final Map<Integer, AbstractMsgHandler> STRATEGY_MAP = new HashMap<>();

public static void register(Integer code, AbstractMsgHandler strategy) {
STRATEGY_MAP.put(code, strategy);
}

public static AbstractMsgHandler getStrategyNoNull(Integer code) {
AbstractMsgHandler strategy = STRATEGY_MAP.get(code);
AssertUtil.isNotEmpty(strategy, CommonErrorEnum.PARAM_INVALID);
return strategy;
}
}

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 发送消息
*/
@Override
@Transactional
public Long sendMsg(ChatMessageReq request, Long uid) {
check(request, uid);
AbstractMsgHandler<?> msgHandler = MsgHandlerFactory.getStrategyNoNull(request.getMsgType()); // 这里
Long msgId = msgHandler.checkAndSaveMsg(request, uid);
//发布消息发送事件
applicationEventPublisher.publishEvent(new MessageSendEvent(this, msgId));
return msgId;
}