当前位置: 首页> 房产> 家装 > Java设计模式

Java设计模式

时间:2025/7/14 17:45:43来源:https://blog.csdn.net/asdfasaa/article/details/139584522 浏览次数:0次

行为设计模式

观察者模式

定义

观察者是一种行为设计模式,观察者模式通常由两个对象组成:观察者(事件和发布器)和被观察者(监听器)。当被观察者状态发生改变时,它会通知所有的观察者对象,使他们能够及时做出响应,所以也被称作“发布-订阅模式”。

特点

优点:

降耦:观察者和被观察者之间不需要知道对方的具体实现,只需要知道对方实现的接口,降低了耦合度

增加灵活度:程序的运行中可以动态的进行添加或者删除观察者对象,增加了灵活度

符合开闭原则:对新增开放,而不需要修改原来被观察的代码

缺点:

  • 当观察者未被正确移除时候,会导致内存泄漏的问题
  • 会产生循环依赖的问题
组成
  • 抽象被观察者(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<>();//添加订阅者@Overridepublic void addSubscriber(Subscriber subscriber) {subscribers.add(subscriber);}//移除订阅者@Overridepublic void removeSubscriber(Subscriber subscriber) {subscribers.remove(subscriber);}//通知订阅者@Overridepublic 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;@Overridepublic 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> {@Overridepublic void onApplicationEvent(MyEvent event) {System.out.println("我订阅的事件已经到达");}
}

事件发布者

发布者,需要实现ApplicationEventPublisherAware 接口,重写publish方法,方法的参数obj就是用来存放发布事件数据。setApplicationEventPublisher是Spring内部主动调用的,可以简单的理解为初始化发布者。

@Component
public class MyEventPublish implements ApplicationEventPublisherAware {private ApplicationEventPublisher publisher;@Overridepublic void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {this.publisher = applicationEventPublisher;}public void publish(Object obj) {this.publisher.publishEvent(obj);}
}

测试

@SpringBootTest
class MyApplicationTests {@AutowiredMyEventPublish publish;@Testvoid contextLoads() {publish.publish(new MyEvent(this));}}

发布订阅模式

定义

发布-订阅模式(Publish-Subscribe Pattern)是一种行为型设计模式,用于解耦生产者(发布者)和消费者(订阅者)之间的关系。在这种模式中,发布者负责发布消息,而订阅者则可以选择订阅他们感兴趣的消息类型。当有新消息发布时,订阅者将收到通知并执行相应的操作。

特点

优点:

  1. 解耦性:实现了消费者和生产者之间的解耦,双方通过消息队列进行通信,彼此不直接依赖或知晓对方的存在
  2. 灵活性:可以任意的增添发布者或者订阅者,并且支持多对多的通信。发布订阅者可以根据需求动态的添加,删除或修改。
  3. 扩展性:发布订阅者之间的解耦,系统可以动态的进行扩展。
  4. 异步通信:发布订阅是通过消息队列或者总线的形式进行,所以支持异步通信

缺点:

  1. 消息的传递顺序难以保证:消息是异步发送的,所以顺序不能保证
  2. 消息处理延迟:因为是异步的,所以消息的传递和处理存在一定的延迟
组成
  • 发布者(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;}@Overridepublic 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!"));}
}
常见面试题

发布-订阅模式的基本概念是什么?

答案:发布-订阅模式是一种软件架构模式,用于解耦生产者(发布者)和消费者(订阅者)之间的关系。在这种模式中,发布者负责发布消息,而订阅者则可以选择订阅他们感兴趣的消息类型。

发布-订阅模式与观察者模式有何区别?

答案:发布-订阅模式和观察者模式都用于处理对象之间的通信,但它们之间有一些区别。观察者模式中,主题(被观察者)维护了一组观察者对象,并在状态发生变化时通知它们。而在发布-订阅模式中,发布者和订阅者之间没有直接的关联,发布者将消息发布到特定的主题,而订阅者可以选择订阅他们感兴趣的主题,从而解耦了生产者和消费者。

责任链模式

定义

是一种行为设计模式,通常使用一条链来处理请求,该请求沿着链的顺序传递,直到有对象处理该请求为止,从而达到解耦请求发送者和请求处理者的目的。

image-20240407113804192
特点

优点:

解耦:将请求发送者和处理者解耦

灵活度:可以灵活的调整顺序

简化连接:责任链中,只需要保持一个后继引用,不需要大量的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{@Overrideprotected void handlerProcess() {System.out.println("handle by a");}}static class HandlerB extends BaseHandler{@Overrideprotected void handlerProcess() {System.out.println("handle by b");}}static class HandlerC extends BaseHandler{@Overrideprotected 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中的内部类CglibMethodInvocationReflectiveMethodInvocation类的子类,然后调用父类的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{@Overrideprotected void handlerProcess() {System.out.println("handler by chain a");}}static class ChainHandlerB extends ChainBaseHandler{@Overrideprotected void handlerProcess() {System.out.println("handler by chain b");}}static class ChainHandlerC extends ChainBaseHandler{@Overrideprotected 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设计模式

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: