设计模式 - 观察者模式

观察者模式(Observer Pattern)

一. 概念

一对多依赖关系,当该对象改变状态时,它的所有依赖对象都会被通知并自动更新

拥有一些值得关注的状态的对象通常被称为目标, 由于它要将自身的状态改变通知给其他对象, 我们也将其称为发布者(pub­lish­er)。 所有希望关注发布者状态变化的其他对象被称为订阅者 (sub­scribers)。

观察者模式建议你为发布者类添加订阅机制, 让每个对象都能订阅或取消订阅发布者事件流。 不要害怕! 这并不像听上去那么复杂。

实际上, 该机制包括:

1) 一个用于存储订阅者对象引用的列表成员变量。

2) 几个用于添加或删除该列表中订阅者的公有方法。

二. 使用场合

  1. 当一个对象状态的改变需要改变其他对象,或实际对象是事先未知的或动态变化的时
  2. 当应用中的一些对象必须观察其他对象

ps : java中的使用

  1. java.util.Observer/java.util.Observerable

  2. java.util.EventListener

  3. javax.servlet.http.HttpSessionBindingListener

  4. javax.faces.event.PhaseListener

三. 优点及缺点

  • 优点:发布者和订阅者低耦合,引入新的订阅者很轻松
  • 缺点:订阅者的通知顺序随机

四. 结构图

在这里插入图片描述

五. 小试牛刀

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 问题

在先进的电子时代,手机是必不可少的物品,那么手机品牌更是种类众多,每个人喜爱的品牌也大不相同。对自己喜爱的手机,手机一旦上新/断货等,都是十分关注的。

问题抛出:

现有张三,李四两人

  1. 张三喜欢苹果手机,李四喜欢华为手机
  2. 张三只想收到苹果手机的通知,李四同理

6.2 uml图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xfgzxCRM-1661760847052)(lab_04_观察者模式.assets/Demo02_Phone_UML.png)]

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 : 李四 , 华为手机 -> 供给短缺
    

七. 参考资料及源码地址

  1. iluwater:https://github.com/iluwatar/java-design-patterns/blob/master/observer/README.md
  2. refactoring:https://refactoring.guru/design-patterns/observer

🎃源码https://gitee.com/zhp1221/design_pattern/tree/master/lab_04_observer


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值