行为设计模式
观察者模式
定义
观察者是一种行为设计模式,观察者模式通常由两个对象组成:观察者(事件和发布器)和被观察者(监听器)。当被观察者状态发生改变时,它会通知所有的观察者对象,使他们能够及时做出响应,所以也被称作“发布-订阅模式”。
特点
优点:
降耦:观察者和被观察者之间不需要知道对方的具体实现,只需要知道对方实现的接口,降低了耦合度
增加灵活度:程序的运行中可以动态的进行添加或者删除观察者对象,增加了灵活度
符合开闭原则:对新增开放,而不需要修改原来被观察的代码
缺点:
- 当观察者未被正确移除时候,会导致内存泄漏的问题
- 会产生循环依赖的问题
组成
- 抽象被观察者(Subject):定义了一个接口,包含了注册观察者、删除观察者、通知观察者等方法。
- 具体被观察者(ConcreteSubject):实现了抽象被观察者接口,维护了一个观察者列表,并在状态发生改变时通知所有注册的观察者。
- 抽象观察者(Observer):定义了一个接口,包含了更新状态的方法。
- 具体观察者(ConcreteObserver):实现了抽象观察者接口,存储了需要观察的被观察者对象,并在被观察者状态发生改变时进行相应的处理。
应用场景
报纸订阅例子
被观察者接口
/**
* @author Created by njy on 2023/6/1
* 报纸接口,即被观察者接口
*/
public interface Newspaper {
/**
* 添加订阅者
* @param subscriber
*/
void addSubscriber(Subscriber subscriber);
/**
* 移除订阅者
* @param subscriber
*/
void removeSubscriber(Subscriber subscriber);
/**
* 通知订阅者
* @param message
*/
void notifySubscribers(String message);
}
观察者接口
/**
* @author Created by njy on 2023/6/1
* 订阅者(即观察者)接口
*/
public interface Subscriber {
void update(String message);
}
被观察者实现
import java.util.ArrayList;
import java.util.List;
/**
* @author Created by njy on 2023/6/1
* 报纸实现类
*/
public class NewspaperImpl implements Newspaper{
//订阅者集合
List<Subscriber> subscribers = new ArrayList<>();
//添加订阅者
@Override
public void addSubscriber(Subscriber subscriber) {
subscribers.add(subscriber);
}
//移除订阅者
@Override
public void removeSubscriber(Subscriber subscriber) {
subscribers.remove(subscriber);
}
//通知订阅者
@Override
public void notifySubscribers(String message) {
for (Subscriber s : subscribers) {
s.update(message);
}
}
}
观察者实现
import lombok.AllArgsConstructor;
import lombok.Data;
/**
* @author Created by njy on 2023/6/1
* 具体订阅者
*/
@Data
@AllArgsConstructor
public class SubscriberImpl implements Subscriber{
private String name;
@Override
public void update(String message) {
System.out.println(name + "---接到消息: " + message);
}
}
Spring中的事件编程
Spring中的事件编程模型就是观察者模式的实现,具体通过一个类:ApplicationEvent
(事件),和两个接口进行实现:ApplicationListener
(监听器), ApplicationEventPublisherAware
(发布器)。Application其实就是ApplicationContext,ApplicationContext内置了几个事件,其中比较容易理解的是:ContextRefreshedEvent、ContextStartedEvent、ContextStoppedEvent、ContextClosedEvent,从名称上来看,就知道这几个事件是什么时候被触发的了。
底层实现:
事件publish发布以后调用**multicastEvent
方法查找所有Listener
以后过滤出对应的监听器,然后调用监听器的doInvokeListener
方法,方法中执行onApplicationEvent
**方法执行监听到的逻辑。
自定义事件
// 自定义的事件
public class MyEvent extends ApplicationEvent {
public MyEvent(Object source) {
super(source);
}
}
自定义监听器
@Component //需要注册到spring容器中去
public class MyListener implements ApplicationListener<MyEvent> {
@Override
public void onApplicationEvent(MyEvent event) {
System.out.println("我订阅的事件已经到达");
}
}
事件发布者
发布者,需要实现ApplicationEventPublisherAware 接口,重写publish方法,方法的参数obj就是用来存放发布事件数据。setApplicationEventPublisher是Spring内部主动调用的,可以简单的理解为初始化发布者。
@Component
public class MyEventPublish implements ApplicationEventPublisherAware {
private ApplicationEventPublisher publisher;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.publisher = applicationEventPublisher;
}
public void publish(Object obj) {
this.publisher.publishEvent(obj);
}
}
测试
@SpringBootTest
class MyApplicationTests {
@Autowired
MyEventPublish publish;
@Test
void contextLoads() {
publish.publish(new MyEvent(this));
}
}
发布订阅模式
定义
发布-订阅模式(Publish-Subscribe Pattern)是一种行为型设计模式,用于解耦生产者(发布者)和消费者(订阅者)之间的关系。在这种模式中,发布者负责发布消息,而订阅者则可以选择订阅他们感兴趣的消息类型。当有新消息发布时,订阅者将收到通知并执行相应的操作。
特点
优点:
- 解耦性:实现了消费者和生产者之间的解耦,双方通过消息队列进行通信,彼此不直接依赖或知晓对方的存在
- 灵活性:可以任意的增添发布者或者订阅者,并且支持多对多的通信。发布订阅者可以根据需求动态的添加,删除或修改。
- 扩展性:发布订阅者之间的解耦,系统可以动态的进行扩展。
- 异步通信:发布订阅是通过消息队列或者总线的形式进行,所以支持异步通信
缺点:
- 消息的传递顺序难以保证:消息是异步发送的,所以顺序不能保证
- 消息处理延迟:因为是异步的,所以消息的传递和处理存在一定的延迟
组成
- 发布者(Publisher):负责发布消息的组件。它们通常不知道谁会接收到消息,只是将消息发送给与之连接的消息队列或主题。
- 订阅者(Subscriber):订阅特定类型的消息,并在该类型的消息被发布时接收到通知。订阅者可以根据自己的需求选择订阅的消息类型。
- 消息(Message):由发布者发布并由订阅者接收的信息单元。消息可以是任何形式的数据,例如文本、JSON、XML等。
- 主题(Topic):定义消息类型的逻辑通道或分类。发布者将消息发布到特定的主题,而订阅者则根据需要订阅特定的主题。
- 消息队列(Message Queue):用于在发布者和订阅者之间传递消息的中介服务。它可以确保消息的异步传输,并提供缓冲和路由消息的功能。
- 事件总线(Event Bus):类似于消息队列,用于在组件之间传递消息,但通常更为轻量级,通常在单个应用程序内部使用。
应用场景
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
// 定义事件类
class Event {
private String message;
public Event(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
}
// 定义发布者类
class Publisher {
private Map<String, List<Subscriber>> subscribers = new HashMap<>();
// 订阅
public void subscribe(String eventType, Subscriber subscriber) {
subscribers.computeIfAbsent(eventType, k -> new ArrayList<>()).add(subscriber);
}
// 发布消息
public void publish(String eventType, Event event) {
List<Subscriber> subscribersList = subscribers.getOrDefault(eventType, new ArrayList<>());
for (Subscriber subscriber : subscribersList) {
subscriber.notify(event);
}
}
}
// 定义订阅者接口
interface Subscriber {
void notify(Event event);
}
// 定义具体的订阅者类
class ConcreteSubscriber implements Subscriber {
private String name;
public ConcreteSubscriber(String name) {
this.name = name;
}
@Override
public void notify(Event event) {
System.out.println(name + " received message: " + event.getMessage());
}
}
public class PublishSubscribeExample {
public static void main(String[] args) {
Publisher publisher = new Publisher();
// 创建两个订阅者
Subscriber subscriber1 = new ConcreteSubscriber("Subscriber 1");
Subscriber subscriber2 = new ConcreteSubscriber("Subscriber 2");
// 订阅事件类型为 "news"
publisher.subscribe("news", subscriber1);
publisher.subscribe("news", subscriber2);
// 发布事件类型为 "news" 的消息
publisher.publish("news", new Event("Breaking news: COVID restrictions lifted!"));
}
}
常见面试题
发布-订阅模式的基本概念是什么?
答案:发布-订阅模式是一种软件架构模式,用于解耦生产者(发布者)和消费者(订阅者)之间的关系。在这种模式中,发布者负责发布消息,而订阅者则可以选择订阅他们感兴趣的消息类型。
发布-订阅模式与观察者模式有何区别?
答案:发布-订阅模式和观察者模式都用于处理对象之间的通信,但它们之间有一些区别。观察者模式中,主题(被观察者)维护了一组观察者对象,并在状态发生变化时通知它们。而在发布-订阅模式中,发布者和订阅者之间没有直接的关联,发布者将消息发布到特定的主题,而订阅者可以选择订阅他们感兴趣的主题,从而解耦了生产者和消费者。
责任链模式
定义
是一种行为设计模式,通常使用一条链来处理请求,该请求沿着链的顺序传递,直到有对象处理该请求为止,从而达到解耦请求发送者和请求处理者的目的。
特点
优点:
解耦:将请求发送者和处理者解耦
灵活度:可以灵活的调整顺序
简化连接:责任链中,只需要保持一个后继引用,不需要大量的if_else
缺点:
可能死循环:如果两个处理类之间有循环引用的问题会造成死循环
组成
1. BaseHandler
首先我们创建一个BaseHandler类,定义了一个大致的框架,包括了下一个处理器的指针,本处理器的执行逻辑
package com.example.proxy.chain;
/**
* BaseHandler class
*
* @author TransientBa
* @date 2018/3/10
*/
public abstract class BaseHandler {
/**
* 用来存储下一个Handler
*/
private BaseHandler nextHandler;
public BaseHandler getNextHandler() {
return nextHandler;
}
public void setNextHandler(BaseHandler nextHandler) {
this.nextHandler = nextHandler;
}
/** 如果nextHandler不为空 则执行下一个Handler**/
public void execute(){
handlerProcess();
if( nextHandler != null){
nextHandler.execute();
}
}
/**
* Handler
* @return void
*/
protected abstract void handlerProcess();
}
2. Client测试
在这个类中我们首先创建三个Handler来继承上面的BaseHandler类,重写其中的handlerProcess方法
,然后在实例化三个雷,并将它们按照顺序赋值给各自的NextHandler,然后我们执行一下
package com.example.proxy.chain;
/**
* Client class
*
* @author TransientBa
* @date 2018/3/12
*/
public class Client {
/**创建ABC三个Handler来继承BaseHandler**/
static class HandlerA extends BaseHandler{
@Override
protected void handlerProcess() {
System.out.println("handle by a");
}
}
static class HandlerB extends BaseHandler{
@Override
protected void handlerProcess() {
System.out.println("handle by b");
}
}
static class HandlerC extends BaseHandler{
@Override
protected void handlerProcess() {
System.out.println("handle by c");
}
}
public static void main(String[] args) {
BaseHandler handlerA = new HandlerA();
BaseHandler handlerB = new HandlerB();
BaseHandler handlerC = new HandlerC();
/**
* 将下个Handler赋值给当前Handler中的NextHandler属性
* 有点像HashMap中的Node 每个Node存储着下一个Node
*/
handlerA.setNextHandler(handlerB);
handlerB.setNextHandler(handlerC);
handlerA.execute();
}
}
应用场景
aop的责任链模式
aop的责任链模式是在上面的基础上进行的优化部分,让我们创建的时候可以不用挨个添加责任链
底层原理
第一步:AOP的责任链是先通过**CglibAopProxy类
中的intercept
**方法拦截
第二步:**CglibAopProxy
中的内部类CglibMethodInvocation
是ReflectiveMethodInvocation
类的子类,然后调用父类的proceed
**方法来开始链式的执行
第三步:按照**interceptorsAndDynamicMethodMatchers
**集合中保存的链式处理方法进行处理
1.Chain
这里创建一个chain类,这个类用来做什么呢?我们将上面的BaseHandler类拆分为ChainBaseHandler类和Chain类两个类来实现链式调用
package com.example.proxy.chain;
import java.util.List;
/**
* Chain class
*
* @author TransientBa
* @date 2018/3/12
*/
public class Chain {
private List<ChainBaseHandler> handlers;
/**
* 用来表示handlers的游标
*/
private int index = -1;
public Chain(List<ChainBaseHandler> handlers){
this.handlers = handlers;
}
/**
* 通过index自增 调用excute实现递归 遍历整个handlers
*/
public void proceed(){
if(index == handlers.size() - 1){
return;
}
handlers.get(++index).execute(this);
}
}
在chain中我们取消了NextHandler属性,而是改用了一个List,类型是ChainBaseHandler,
还多了一个index,初始值为-1,
然后是一个构造,在实例化时要传入一个List进来,并赋值给该类的handlers属性,
最后是proceed方法,在这个方法中我们首先判断了index是否超过List的size,如果超过了则返回,如果没操作,就得到当索引的Handler,然后执行ChainBaseHandler中的execute方法
2. ChainBaseHandler
在这个类中我们只有两个方法,execute和handlerProcess,其中handlerProcess依然用来被继承后写执行过程。
execute方法有一个参数Chain,这个方法首先进来会执行当前Handler的HandlerProcess执行过程,然后会调用传入Chain的proceed方法,这样调用就又回到了proceed中,继续判断index并开始下一次循环
package com.example.proxy.chain;
/**
* ChainBaseHandler class
*
* @author TransientBa
* @date 2018/3/12
*/
public abstract class ChainBaseHandler {
/**同样先执行自己的动作 再调用chain的proceed去遍历下一个Handler**/
public void execute(Chain chain){
handlerProcess();
chain.proceed();
};
protected abstract void handlerProcess();
}
3. 测试
package com.example.proxy.chain;
import java.util.Arrays;
import java.util.List;
/**
* ChainClient class
*
* @author TransientBa
* @date 2018/3/12
*/
public class ChainClient {
static class ChainHandlerA extends ChainBaseHandler{
@Override
protected void handlerProcess() {
System.out.println("handler by chain a");
}
}
static class ChainHandlerB extends ChainBaseHandler{
@Override
protected void handlerProcess() {
System.out.println("handler by chain b");
}
}
static class ChainHandlerC extends ChainBaseHandler{
@Override
protected void handlerProcess() {
System.out.println("handler by chain c");
}
}
public static void main(String[] args) {
/**
* 声明HandlerList关系链 通过数组顺序排序
* 不再像之前那样手动给当前Handler中的NextHandler设置值
* 同样每个chain中间没有相互的依赖 实现解耦
*/
List<ChainBaseHandler> handlerList = Arrays.asList(
new ChainHandlerA(),
new ChainHandlerB(),
new ChainHandlerC()
);
Chain chain = new Chain(handlerList);
chain.proceed();
}
}
责任链模式和观察者模式的区别
Java8新特性
函数式编程
在数学中,函数就是有输入量、输出量的一套计算方案,也就是“拿数据做操作”
面向对象思想强调“必须通过对象的形式来做事情”
函数式思想强调则金量忽略面向对象的复杂语句:“强调做什么,而不是以什么形式去做”
而我们要学习的Lambda表达式就是函数式思想的体现
标签:订阅,Java,chain,void,观察者,class,设计模式,public From: https://blog.csdn.net/asdfasaa/article/details/139584522