观察者模式(Observer Pattern)
文章目录
一. 概念
一对多依赖关系,当该对象改变状态时,它的所有依赖对象都会被通知并自动更新
拥有一些值得关注的状态的对象通常被称为目标, 由于它要将自身的状态改变通知给其他对象, 我们也将其称为
发布者
(publisher)。 所有希望关注发布者状态变化的其他对象被称为订阅者
(subscribers)。观察者模式建议你为发布者类添加订阅机制, 让每个对象都能订阅或取消订阅发布者事件流。 不要害怕! 这并不像听上去那么复杂。
实际上, 该机制包括:
1) 一个用于存储订阅者对象引用的列表成员变量。
2) 几个用于添加或删除该列表中订阅者的公有方法。
二. 使用场合
- 当一个对象状态的改变需要改变其他对象,或实际对象是事先未知的或动态变化的时
- 当应用中的一些对象必须观察其他对象
ps : java
中的使用
三. 优点及缺点
- 优点:发布者和订阅者低耦合,引入新的订阅者很轻松
- 缺点:订阅者的通知顺序随机
四. 结构图
五. 小试牛刀
5.1 问题
在这个世界上,每秒钟就会诞生4.5个婴儿,那婴儿的诞生,意味着丈夫妻子同时各多了个身份,孩子的爸爸/妈妈。
问题抛出:
孩子刚出生时,是急需让人照顾的,孩子的一举一动,都会影响着父母的动作及状态,那么我们就以这个案例为由,用代码实现把
5.2 uml
5.3 代码实现
⭐️ 依赖
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>5.7.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
⭐️Child 发布者
/**
* @author zhanghp
* @date 2022-08-26 14:03
*/
@Slf4j
public class Child {
private ActionType actionType;
private final List<ChildObserver> OBSERVERS;
public Child() {
actionType = ActionType.SLEEPING;
OBSERVERS = new ArrayList<>();
}
public void add(ChildObserver obs){
OBSERVERS.add(obs);
}
public void remove(ChildObserver obs){
OBSERVERS.remove(obs);
}
public void changeAction(){
var actionTypes = ActionType.values();
actionType = actionTypes[(actionType.ordinal() + 1) % actionTypes.length];
log.info("the child is {}", actionType);
notifyObservers();
}
public void notifyObservers(){
for (var observer : OBSERVERS) {
observer.update(actionType);
}
}
}
⭐️ChildObserver
订阅者接口类:孩子状态改变时,调用该接口的update
/**
* @author zhanghp
* @date 2022-08-26 14:04
*/
public interface ChildObserver {
void update(ActionType type);
}
⭐️ActionType
状态类:发布者的所有状态枚举
/**
* @author zhanghp
* @date 2022-08-26 13:59
*/
public enum ActionType {
SLEEPING("Sleeping"),
CRYING("Crying"),
SMILING("Smiling");
private final String description;
ActionType(String description) {
this.description = description;
}
public String getDescription(){
return this.description;
}
@Override
public String toString() {
return this.name().toLowerCase();
}
}
⭐️Father
And Mother
订阅者类:根据孩子状态,更新自己
/**
* @author zhanghp
* @date 2022-08-26 14:11
*/
@Slf4j
public class Father implements ChildObserver{
@Override
public void update(ActionType type) {
log.info("Father -> my baby is {}", type.getDescription());
}
}
/**
* @author zhanghp
* @date 2022-08-26 14:11
*/
@Slf4j
public class Father implements ChildObserver{
@Override
public void update(ActionType type) {
log.info("Father -> my baby is {}", type.getDescription());
}
}
5.4 UT测试用例
-
代码
/** * @author zhanghp * @date 2022-08-26 14:19 */ public class ObserverTest { @Test void testObserver(){ assertDoesNotThrow(() -> App.main(new String[]{})); } } /** * @author zhanghp * @date 2022-08-26 14:16 */ public class App { public static void main(String[] args) { Child child = new Child(); child.add(new Father()); child.add(new Mother()); child.changeAction(); child.changeAction(); child.changeAction(); } }
-
结果输出
- the child is crying - Father -> my baby is Crying - Mother -> my baby is Crying - the child is smiling - Father -> my baby is Smiling - Mother -> my baby is Smiling - the child is sleeping - Father -> my baby is Sleeping - Mother -> my baby is Sleeping
六. 再试牛刀
6.1 问题
在先进的电子时代,手机是必不可少的物品,那么手机品牌更是种类众多,每个人喜爱的品牌也大不相同。对自己喜爱的手机,手机一旦上新/断货等,都是十分关注的。
问题抛出:
现有张三,李四两人
- 张三喜欢苹果手机,李四喜欢华为手机
- 张三只想收到苹果手机的通知,李四同理
6.2 uml图
6.3 代码实现
⭐️EventManager 发布者
/**
* @author zhanghp
* @date 2022-08-26 16:32
*/
public class EventManager {
private Map<String, List<EventListener>> listeners;
public EventManager(PhoneType ... phoneTypes) {
this.listeners = new HashMap<>();
for (PhoneType phone : phoneTypes) {
this.listeners.put(phone.getTYPE(), new ArrayList<>());
}
}
public void subscribe(PhoneType phoneType, EventListener listener){
List<EventListener> eventListeners = listeners.get(phoneType.getTYPE());
eventListeners.add(listener);
}
public void unSubscribe(PhoneType phoneType, EventListener listener){
List<EventListener> eventListeners = listeners.get(phoneType.getTYPE());
eventListeners.remove(listener);
}
public void notifyListener(PhoneType phoneType, PhoneStatus phoneStatus){
List<EventListener> eventListeners = listeners.get(phoneType.getTYPE());
for (EventListener listener : eventListeners) {
listener.update(phoneType, phoneStatus);
}
}
}
⭐️EventListener 订阅者
/**
* @author zhanghp
* @date 2022-08-26 16:32
*/
public interface EventListener {
void update(PhoneType type, PhoneStatus status);
}
⭐️Edit 编辑器
/**
* @author zhanghp
* @date 2022-08-26 17:01
*/
public class Edit{
public EventManager eventManager;
public Edit(PhoneType ... phoneTypes) {
this.eventManager = new EventManager(phoneTypes);
}
public void applePhone(PhoneStatus status){
eventManager.notifyListener(PhoneType.APPLE, status);
}
public void huaweiPhone(PhoneStatus status){
eventManager.notifyListener(PhoneType.HUAWEI, status);
}
}
⭐️ PhoneType
And PhoneStatus
类型和状态枚举
/**
* @author zhanghp
* @date 2022-08-26 16:21
*/
public enum PhoneType {
APPLE("苹果手机"),
HUAWEI("华为手机");
private final String TYPE;
PhoneType(String name) {
this.TYPE = name;
}
public String getTYPE() {
return this.TYPE;
}
@Override
public String toString() {
return this.name().toLowerCase();
}
}
/**
* @author zhanghp
* @date 2022-08-26 16:24
*/
public enum PhoneStatus {
SUPPLY_SHORTAGE("供给短缺"),
FRESH_SUPPLIES_COME("新货上市");
private final String STATUS;
PhoneStatus(String status) {
this.STATUS = status;
}
public String getSTATUS() {
return this.STATUS;
}
@Override
public String toString() {
return this.name().toLowerCase();
}
}
⭐️ People 具体订阅者
@Slf4j
public class People implements EventListener {
private final String NAME;
public People(String name) {
this.NAME = name;
}
@Override
public void update(PhoneType type, PhoneStatus status) {
log.info("People : {} , {} -> {}", this.NAME, type.getTYPE(), status.getSTATUS());
}
}
6.4 UT测试用例
-
代码
/** * @author zhanghp * @date 2022-08-26 17:13 */ public class PhoneTest { @Test void eventTest(){ assertDoesNotThrow(() -> { App2.main(new String[]{});}); } } /** * @author zhanghp * @date 2022-08-26 17:04 */ public class App2 { public static void main(String[] args) { Edit edit = new Edit(PhoneType.APPLE, PhoneType.HUAWEI); edit.eventManager.subscribe(PhoneType.APPLE, new People("张三")); edit.eventManager.subscribe(PhoneType.HUAWEI, new People("李四")); edit.applePhone(PhoneStatus.FRESH_SUPPLIES_COME); edit.applePhone(PhoneStatus.SUPPLY_SHORTAGE); edit.huaweiPhone(PhoneStatus.FRESH_SUPPLIES_COME); edit.huaweiPhone(PhoneStatus.SUPPLY_SHORTAGE); } }
-
输出结果
- People : 张三 , 苹果手机 -> 新货上市 - People : 张三 , 苹果手机 -> 供给短缺 - People : 李四 , 华为手机 -> 新货上市 - People : 李四 , 华为手机 -> 供给短缺
七. 参考资料及源码地址
- iluwater:https://github.com/iluwatar/java-design-patterns/blob/master/observer/README.md
- refactoring:https://refactoring.guru/design-patterns/observer
🎃源码:https://gitee.com/zhp1221/design_pattern/tree/master/lab_04_observer