装饰器模式
装饰者模式的应用场景
装饰者模式(Decorator Pattern)是指在不改变原有对象的基础之上,将功能附加到对象上,提供了比继承更有弹性的替代方案(扩展原有对象的功能),属于结构型模式。
装饰者在代码程序中适用于以下场景:
1、用于扩展一个类的功能或给一个类添加附加职责。
2、动态的给一个对象添加功能,这些功能可以再动态的撤销。
例如登录功能
定义操作接口:
public interface IMember {
public void login(String name,String password);
public void signUp(String name,String password);
}
默认登录类
public class DefaultMember implements IMember {
{
System.out.println("一些初始化动作,获取数据库操作对象等等");
}
@Override
public void login(String name, String password) {
System.out.println("执行登录逻辑");
}
@Override
public void signUp(String name, String password) {
System.out.println("执行注册逻辑");
}
}
QQ登录扩展
public class QQMember implements IMember {
private IMember member;
private QQMember() {
}
public QQMember(IMember member) {
this.member = member;
}
public void loginQQ(String auth) {
member.login("authPart", "authPart");
}
public void signInQQ(String auth) {
member.signUp("authPart", "authPart");
}
@Override
public void login(String name, String password) {
member.login(name, password);
}
@Override
public void signUp(String name, String password) {
member.signUp(name, password);
}
}
test
public static void main(String[] args) {
QQMember qqMember = new QQMember(new DefaultMember());
qqMember.loginQQ("auth");
}
原本只支持账号密码登录,扩展后,可以对QQ认证的信息进行处理,再作为账号密码调用原本的登录注册功能.扩展了功能的同时不改变原有对象
装饰者模式在源码中的应用
再例如原型模式中使用到的FileInputStream和ObjectInputStream
public FileInputStream(String name) throws FileNotFoundException {
this(name != null ? new File(name) : null);
}
public ObjectInputStream(InputStream in) throws IOException {
verifySubclass();
bin = new BlockDataInputStream(in);
handles = new HandleTable(10);
vlist = new ValidationList();
serialFilter = ObjectInputFilter.Config.getSerialFilter();
enableOverride = false;
readStreamHeader();
bin.setBlockDataMode(true);
}
public static void main(String[] args) throws Exception {
QQMember qqMember = new QQMember(new DefaultMember());
qqMember.loginQQ("auth");
FileOutputStream fileOutputStream = new FileOutputStream("test");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(qqMember);
FileInputStream fileInputStream = new FileInputStream("test");
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
QQMember o = ((QQMember) objectInputStream.readObject());
System.out.println(o==qqMember);
}
通过ObjectInputStream扩展了FileInputStream,使得可以直接将文件流转换为对象.
装饰者模式的优缺点
优点:
1、装饰者是继承的有力补充,比继承灵活,不改变原有对象的情况下动态地给一个对象 扩展功能,即插即用。
3、装饰者完全遵守开闭原则。
缺点:
1、会出现更多的代码,更多的类,增加程序复杂性。
2、动态装饰时,多层装饰时会更复杂
观察者模式
观察者模式的应用场景
观察者模式(Observer Pattern)定义了对象之间的一对多依赖,让多个观察者对象同 时监听一个主体对象,当主体对象发生变化时,它的所有依赖者(观察者)都会收到通 知并更新,属于行为型模式。观察者模式有时也叫做发布订阅模式。观察者模式主要用 于在关联行为之间建立一套触发机制的场景。
观察者模式在现实生活应用也非常广泛, 比如关注用户后的更新通知.
下面用代码实现一个关注后用户更新时,通知所有关注者的观察者模式
Event
public class Event {
int userId;
String type;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Event event = (Event) o;
return Objects.equals(userId, event.userId);
}
@Override
public int hashCode() {
return Objects.hash(userId);
}
public Event(int userId) {
this.userId = userId;
}
public Event setType(String type) {
this.type = type;
return this;
}
}
Listener
public class Listener {
int targetId;
int userId;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Listener listener = (Listener) o;
return Objects.equals(userId, listener.userId);
}
@Override
public int hashCode() {
return Objects.hash(userId);
}
public Listener(int targetId, int userId) {
this.targetId = targetId;
this.userId = userId;
}
public void notifies(Event event) {
System.out.println("通知" + userId + "," + event.userId + "更新了" + event.type);
}
}
Publisher
//发布中心
public class Publisher {
//<用户,监听者集合>
private Map<Event, LinkedHashSet<Listener>> listeners = new HashMap<>();
public void publish(Event event) {
for (Listener listener : listeners.get(event)) {
listener.notifies(event);
}
}
public void follow(Listener listener) {
Event event = new Event(listener.targetId);
if (this.listeners.get(event) != null) {
this.listeners.get(event).add(listener);
} else {
LinkedHashSet<Listener> objects = new LinkedHashSet<>();
objects.add(listener);
listeners.put(event, objects);
}
}
}
Test
static Publisher publisher = new Publisher();
public static void main(String[] args) {
int userId, userId1, targetId, targetId1;
userId = 11;
userId1 = 22;
targetId = 1;
targetId1 = 2;
publisher.follow(new Listener(targetId, userId));
publisher.follow(new Listener(targetId, userId1));
publisher.follow(new Listener(targetId1, userId1));
publisher.follow(new Listener(targetId1, userId));
update(new Event(targetId).setType("朋友圈"));
update(new Event(targetId1).setType("心情"));
}
public static void update(Event event) {
publisher.publish(event);
}
效果
通知11,1更新了朋友圈
通知22,1更新了朋友圈
通知22,2更新了心情
通知11,2更新了心情
观察者模式在源码中的应用
在spring的生命周期中,各个阶段都存在事件和对应的监听(处理)器
org.springframework.boot.context.config.ConfigFileApplicationListener#onApplicationEvent
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationEnvironmentPreparedEvent) {
onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
}
if (event instanceof ApplicationPreparedEvent) {
onApplicationPreparedEvent(event);
}
}
org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType)
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
Executor executor = getTaskExecutor();
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
测试代码
public static void main(String[] args) {
SpringApplicationBuilder builder = new SpringApplicationBuilder(PatternTemplateAdapterApplication.class);
builder.bannerMode(Banner.Mode.OFF);
builder.listeners(event -> { // 增加监听器
System.err.println("监听到事件 : " + event.getClass().getSimpleName());
})
.run(args)
.close();
}
效果
监听到事件 : ApplicationStartingEvent
监听到事件 : ApplicationEnvironmentPreparedEvent
监听到事件 : ApplicationContextInitializedEvent
2020-11-28 20:42:31.206 INFO 30788 --- [ main] c.e.p.PatternTemplateAdapterApplication : Starting PatternTemplateAdapterApplication on LAPTOP-D2VN2M00 with PID 30788 (D:\Learn2020\project1\pattern\pattern-template-adapter\target\classes started by 87990 in D:\Learn2020\project1\pattern\pattern-template-adapter)
2020-11-28 20:42:31.206 INFO 30788 --- [ main] c.e.p.PatternTemplateAdapterApplication : No active profile set, falling back to default profiles: default
监听到事件 : ApplicationPreparedEvent
2020-11-28 20:42:31.986 INFO 30788 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2020-11-28 20:42:31.996 INFO 30788 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2020-11-28 20:42:31.996 INFO 30788 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.39]
2020-11-28 20:42:32.086 INFO 30788 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2020-11-28 20:42:32.086 INFO 30788 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 830 ms
2020-11-28 20:42:32.206 INFO 30788 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
监听到事件 : ContextRefreshedEvent
2020-11-28 20:42:32.326 INFO 30788 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2020-11-28 20:42:32.326 INFO 30788 --- [ main] c.e.p.PatternTemplateAdapterApplication : Started PatternTemplateAdapterApplication in 1.453 seconds (JVM running for 2.425)
2020-11-28 20:42:32.326 INFO 30788 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'applicationTaskExecutor'
监听到事件 : ServletWebServerInitializedEvent
监听到事件 : ApplicationStartedEvent
监听到事件 : ApplicationReadyEvent
监听到事件 : ContextClosedEvent
Process finished with exit code 0
总结
1、观察者模式支持广播通信。
2、通过事件使得应用的生命周期更清晰