美文网首页
设计模式三十六计之观察者模式(Observer)

设计模式三十六计之观察者模式(Observer)

作者: ramostear | 来源:发表于2019-01-06 14:48 被阅读0次

    设计模式三十六计之观察者模式(Observer)

    1. 设计意图

    定义对象之间的一对多依赖关系,以便当一个对象更改状态时,将自动通知和更新其所有依赖项。

    观察者模式

    简而言之

    你别来找我,给我你的联系方式,有事我会主动联系你

    2.案例演示

    以当前最火热的吃鸡游戏作为一个简单的案例来演示观察者模式,当玩家进入游戏时,会收到游戏服务器推送的提示消息,随着游戏的进行,如果某个玩家被Kill掉了,游戏服务器会把此消息推送给房间里的其他玩家。在本案例中,“游戏” 是一个抽象的被观察者,"吃鸡游戏" 是具体的被观察者;“游戏玩家” 是一个抽象的观察者(接口),而玩家A、玩家B等是具体的观察者。案例的UML关系如下图:

    image

    3. 示例代码

    3.1 抽象的被观察者类(Subject)

    AbstractGame.java

    package com.ramostear.pattern.observer;
    import java.util.ArrayList;
    /**
     * @author ramostear
     * @create-time 2019/1/5 0005-23:27
     * @modify by :
     * @info:[抽象的被观测者类]
     * @since:
     */
    public abstract class AbstractGame {
        /**
         * 定义一个存放观察者的容器
         */
        public final ArrayList<Observer> obsList = new ArrayList<>();
        /**
         * 注册观察者
         * @param obs   观察者
         * @param <T>
         */
        public <T> void attach(Observer obs){
            if (obs == null){
                throw new NullPointerException("Observer is null.");
            }else{
                this.attachObs(obs);
            }
        }
        /**
         * 注册观察者
         * @param obs
         */
        private void attachObs(Observer obs){
            if (obs == null){
                throw new NullPointerException("class is null");
            }else {
                synchronized (obsList){
                    if(!obsList.contains(obs)){
                        obsList.add(obs);
                    }
                }
            }
        }
        /**
         * 注销观察者
         * @param obs   观察者
         * @param <T>
         */
        public <T> void detach(Observer obs){
            if(obs == null){
                throw new NullPointerException("Observer is null");
            }else {
                this.detachObs(obs);
            }
        }
        /**
         * 注销观察者
         * @param obs
         */
        private void detachObs(Observer obs){
            if(obs == null){
                throw new NullPointerException("Class is null");
            }else{
                synchronized (obsList){
                   obsList.remove(obs);
                }
            }
        }
        /**
         * 通知所有的观察者
         * @param messages
         */
        public abstract void notifyAllObs(String...messages);
        /**
         * 通知某个观察者
         * @param obs
         * @param messages
         */
        public abstract void notifyObs(Observer obs,String...messages);
    }
    

    AbstractGame类中定义了添加、删除和通知观察者的方法,同时有一个List类型的容器,用于保存已注册的观察者,当需要通知观察者时,从容器中取出观察者信息。

    说明:抽象的被观察者可以定义成一个抽象类或者接口,本案例中采用的是抽象类

    3.2 抽象的观察者接口(Observer)

    Observer.java

    package com.ramostear.pattern.observer;
    
    /**
     * @author ramostear
     * @create-time 2019/1/5 0005-23:26
     * @modify by :
     * @info:[观察者接口]
     * @since:
     */
    public interface Observer {
        /**
         * 更新状态
         * @param messages
         */
        void update(String... messages);
    }
    

    在该接口中定义了一个update() 方法,当被观察者发出通知时,此方法会被调用。

    3.3 具体被观察者(ConcreteSubject)

    ChikenGame继承了AbstractGame类,并对通知方法进行了具体的实现。
    ChikenGame.java

    package com.ramostear.pattern.observer;
    
    /**
     * @author ramostear
     * @create-time 2019/1/5 0005-23:55
     * @modify by :
     * @info:[吃鸡游戏类]
     * @since:
     */
    public class ChickenGame extends AbstractGame {
    
        private String roomName;
    
        public ChickenGame(String roomName) {
            this.roomName = roomName;
        }
    
    
        public String getRoomName() {
            return roomName;
        }
    
        public void setRoomName(String roomName) {
            this.roomName = roomName;
        }
    
        @Override
        public void notifyAllObs(String... messages) {
            obsList.forEach(obs->{
                this.notifyObs(obs,messages);
            });
        }
    
        @Override
        public void notifyObs(Observer obs, String... messages) {
           if (obs == null){
               throw new NullPointerException("Observer is null");
           }else{
              obs.update(messages);
           }
        }
    }
    

    3.4 具体观察者(ConcreteObserver)

    Gamer类实现了Observer接口,并对Observer的update方法进行了具体的实现;这里为了演示,只是简单的对消息进行输出。
    Gamer.java

    package com.ramostear.pattern.observer;
    /**
     * @author ramostear
     * @create-time 2019/1/6 0006-0:06
     * @modify by :
     * @info:[游戏玩家]
     * @since:
     */
    public class Gamer implements Observer{
    
        private String name;
    
        public Gamer(String name) {
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public void update(String... messages) {
            System.out.println("玩家:"+name);
          for (String message:messages){
              System.out.println("消息->"+message+"\n");
          }
        }
    }
    

    3.5 测试本次案例

    创建一个吃鸡游戏叫“三国吃鸡演义” ,将刘、关、张三个玩家注册到吃鸡游戏中。游戏发布消息给三个玩家,刘、关、张同时收到游戏发出的消息,当关羽挂掉后,只有刘、张两个玩家收到消息;当张飞再挂掉后,只有刘备收到消息。为了演示观察者模式,最后我们让关羽满血复活,此时刘、关二人收到游戏发出的消息。
    App.java

    package com.ramostear.pattern.observer;
    /**
     * @author ramostear
     * @create-time 2019/1/6 0006-0:08
     * @modify by :
     * @info:[测试类]
     * @since:
     */
    public class App {
        public static void main(String[] args){
            ChickenGame game = new ChickenGame("三国吃鸡演义");
            Gamer gamerLiu = new Gamer("刘备");
            Gamer gamerZhang = new Gamer("张飞");
            Gamer gamerGuan = new Gamer("关羽");
    
            game.attach(gamerLiu);
            game.attach(gamerGuan);
            game.attach(gamerZhang);
            game.notifyAllObs("欢迎进入"+game.getRoomName());
            game.notifyAllObs(new String[]{"刘关张桃园三结义,开始三国吃鸡演义..."});
    
            game.detach(gamerGuan);
            game.notifyAllObs("#关羽:\"我去!被98K爆了,快来扶我一下!\"");
            game.notifyAllObs("#刘备:\"我去,这货肥得一批!\"");
    
            game.detach(gamerZhang);
            game.notifyAllObs("#张飞:\"我去,这比是挂!\"");
            game.notifyAllObs("#刘备:\"我去!咋这么多人,我凉了!\"");
    
            game.attach(gamerGuan);
            game.notifyAllObs("关羽满血复活");
            game.notifyAllObs("#刘备:\"苟住,苟住就能赢!\"");
    
        }
    }
    

    测试结果:


    image

    4. 适用性

    当满足以下情况中的一种时使用观察者模式

    • 当抽象有两个Aspect时,一个依赖于另一个。 将这些Aspact封装在单独的对象中可让您独立地改变和重用它们.
    • 当一个对象的更改需要更改其他对象时,你不知道到底需要更改多少个关联的对象
    • 当不希望多个对象之前发生紧耦合时

    5. 真实案例

    原文链接:https://www.ramostear.com/post/details/observer-design-pattern.html

    相关文章

      网友评论

          本文标题:设计模式三十六计之观察者模式(Observer)

          本文链接:https://www.haomeiwen.com/subject/hnssrqtx.html