- 设计模式解析一 工厂模式的不同
- 设计模式解析二 结构模式三剑客
- 设计模式解析三 行为模式三剑客
- 设计模式解析四 模板方法模式和外观模式
- 设计模式解析五 观察者模式和桥接模式
- 设计模式解析六 单例模式
一. 前言
第五篇要讲的也是一个行为型模式,观察者模式,话不多说,直接开始吧。
二. 观察者模式
不知道大家有没有向出版社订阅过杂志的经历,我小时候定过,隐约记得拿本书叫《青年文摘》。
那么出版社有这么多杂志类型,有娱乐杂志、有经济杂志、有科技杂志,我们订阅的时候选择其中一种或几种来订阅,杂志出版的时候就会像所有订阅了杂志的人邮寄杂志,事实上这种出版+订阅的模式就是观察者模式。
我们来实现代码,首先我们定一个观察者,也就是我们这些要订书的订阅者们的接口:
public interface Observer {
void post(String book);
}
再定义个杂志社:
public enum MagazineType {
// 娱乐杂志
ENTERTAINMENT,
// 科技杂志
SCIENCE
}
public interface PeriodicalOffice {
/**
* 订阅杂志
*/
void subscribe(Observer observer,MagazineType type);
/**
* 取消订阅
*/
void unSubscribe(Observer observer,MagazineType type);
/**
* 出版杂志,出版时会向所有订阅者邮寄杂志
*/
void publish(MagazineType type, String book);
}
让我们开一家星星杂志社好了:
public class StarPeriodicalOffice implements PeriodicalOffice {
// 存储所有订阅者
private static final Map> observers = new ConcurrentHashMap<>();
@Override
public void subscribe(Observer observer, MagazineType type) {
List list = observers.getOrDefault(type, new ArrayList<>());
if (!list.contains(observer)) {
list.add(observer);
observers.put(type, list);
}
}
@Override
public void unSubscribe(Observer observer, MagazineType type) {
List list = observers.getOrDefault(type, new ArrayList<>());
list.remove(observer);
}
@Override
public void publish(MagazineType type, String book) {
List list = observers.getOrDefault(type, new ArrayList<>());
list.forEach(o -> o.post(book));
}
}
杰克和萝丝来订阅杂志了:
public class Jack implements Observer {
@Override
public void post(String book) {
System.out.println("杰克收到了杂志社邮寄来的书:" + book);
}
}
public class Rose implements Observer {
@Override
public void post(String book) {
System.out.println("萝丝收到了杂志社邮寄来的书:" + book);
}
}
接下来是杂志订阅和发布的过程:
public static void main(String[] args) {
PeriodicalOffice office = new StarPeriodicalOffice();
Observer jack = new Jack();
Observer rose = new Rose();
office.subscribe(jack, MagazineType.ENTERTAINMENT);
office.subscribe(jack, MagazineType.SCIENCE);
office.subscribe(rose, MagazineType.ENTERTAINMENT);
office.publish(MagazineType.SCIENCE, "科学故事");
office.publish(MagazineType.ENTERTAINMENT, "娱乐圈的故事");
}
执行结果:
杰克收到了杂志社邮寄来的书:科学故事
杰克收到了杂志社邮寄来的书:娱乐圈的故事
萝丝收到了杂志社邮寄来的书:娱乐圈的故事
这就是观察者模式,观察者模式在spring框架中也有运用。
Spring的事件监听机制就是观察者模式的实现:
@Configuration
public class AppEvent implements ApplicationListener {
private static final LoggerAdapter LOGGER = LoggerFactory.getLogger(AppEvent.class);
@Autowired
private Environment environment;
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationReadyEvent) {
LOGGER.info("应用已启动, 当前运行的端口是: {} ", environment.getProperty("server.port"));
return;
}
if (event instanceof ApplicationFailedEvent) {
LOGGER.info("应用启动失败");
}
}
}
这里的监听是对所有应用ApplicationEvent
事件监听,当然泛型也可以选择针对某个之间进行监听,比如只监听ApplicationReadyEvent
。
public interface ApplicationEventMulticaster {
/**
* Add a listener to be notified of all events.
* @param listener the listener to add
*/
void addApplicationListener(ApplicationListener> listener);
/**
* Add a listener bean to be notified of all events.
* @param listenerBeanName the name of the listener bean to add
*/
void addApplicationListenerBean(String listenerBeanName);
/**
* Remove a listener from the notification list.
* @param listener the listener to remove
*/
void removeApplicationListener(ApplicationListener> listener);
/**
* Remove a listener bean from the notification list.
* @param listenerBeanName the name of the listener bean to add
*/
void removeApplicationListenerBean(String listenerBeanName);
/**
* Remove all listeners registered with this multicaster.
* After a remove call, the multicaster will perform no action
* on event notification until new listeners are being registered.
*/
void removeAllListeners();
/**
* Multicast the given application event to appropriate listeners.
*
Consider using {@link #multicastEvent(ApplicationEvent, ResolvableType)}
* if possible as it provides a better support for generics-based events.
* @param event the event to multicast
*/
void multicastEvent(ApplicationEvent event);
/**
* Multicast the given application event to appropriate listeners.
*
If the {@code eventType} is {@code null}, a default type is built
* based on the {@code event} instance.
* @param event the event to multicast
* @param eventType the type of event (can be null)
* @since 4.2
*/
void multicastEvent(ApplicationEvent event, ResolvableType eventType);
}
ApplicationEventMulticaster
类就是所有的观察者管理类,所有监听器都会通过这个接口来进行注册,遇到应用的事件发生时也通过该接口来进行通知。至于是怎么注册进来的,就是在spring 容器加载bean时候判断我们的bean是否是ApplicationListener
的实现类,如果是就帮我们注册进来,有兴趣的可以去看看源码。
实际上Spring的源码可以说是集设计模式大成者,我了解的也是百不足一,其中很多源码都值得我们去研究学习,不能因为它为我们提供了简便的使用方式,我们就不去了解学习了。
定义
观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
三. 桥接模式
桥接模式是一个很难理解的模式,夜也深了,这个模式会用例子来解释,但就不写代码了。
小时候大家一定学过画画,我记得小时候有一种一套的画笔,里面会包含一二十种画笔,每种颜色不同,小时候没觉得什么,但是从程序猿的角度再来思考一下这个东西。
- 画笔可以有很多种类 软毛、硬毛
- 画笔还会有多种颜色 赤橙黄绿青蓝紫
现在让我们从程序的角度抽象一下画家的画笔:画笔的材质、画笔颜色
有这两种可变特性,设计接口的话,是不是接口就应该有获取这两个属性的方法,接下来,根据这两种特性进行组合,然后编写实现类的话,总共有14支笔,要写14个类,what??如果再加一种毛的材料,我是不是还得再加7个类,这绝对不可能。
那么怎么办?我们是不是可以把这两种抽象进行分离呢?
- 定义一个画笔毛刷种类的接口
- 定义一个颜色接口
然后再使用画笔的时候在对这两种属性进行画笔的组合,是不是我们就可以做到只有9个类就行了,而就算任意增加画笔材质,或者增加颜色,也只增加一个类。
所以现实中我们知道那些画画的人总是有画笔,还有额外的颜料。
而这种把抽象进行分离解耦的方式就是桥接模式。
定义
桥接模式即将抽象部分与它的实现部分分离开来,使他们都可以独立变化。
这句话以前一直很不容易理解,现在可以试着理解一下了,说回上面的画笔,其中画笔材质,还有画笔颜色都是画笔的抽象,通常来讲会将这两个抽象定义到一个接口里,但由于每一种抽象有可能会分开变化,所以我们将其中的部分抽象,比如颜色,分离出去,然后通过类的组合关联的方式让画笔具有颜色的属性,这就是抽象部分和实现部分进行分离开来的意思。
套用《大话设计模式》里面的就是实现系统可能有多个角度分类,每一种角度都可能变化,那么把这种多角度分类给分离出来让他们独立变化,减少他们之间耦合。
桥接模式中的所谓脱耦,就是指在一个软件系统的抽象化和实现化之间使用关联关系(组合或者聚合关系)而不是继承关系,从而使两者可以相对独立地变化,这就是桥接模式的用意。