zoukankan      html  css  js  c++  java
  • [design pattern](2) Observer

    前言

    在上一个博客中我们介绍了Strategy模式,它是行为型模式麾下的一员大将。那么本博客我们来学习一下行为型模式麾下的另一员大将Observer模式。

    思考题

    老套路,先来思考下面的问题:

    问题:思考报纸订阅的问题,有一个体育报社每天都会有新的体育报道,关注体育新闻的用户会获取每天的报道

    首先,我们还是来看一看不使用设计模式的代码:

    SportNews.java:

    public class SportNews {
        private String context;
        //用户获取新闻
        public String getContext() {
            return context;
        }
        //产生新的新闻
        public void newContext(String context) {
            this.context = context;
        }
    }

    People.java:

    public class People {
        private final String name;
        private SportNews sportNews;
        public People(String name, SportNews sportNews) {
            this.sportNews = sportNews;
            this.name = name;
        }
        //读新闻
        public void readNews() {
            System.out.println(String.format("I am %s,I am looking %s", name, sportNews.getContext()));
        }
    }

    Robot.java:

    public class Robot {
        private static int num = 0;
        private final int id = num++;
        private SportNews sportNews;
        
        public Robot(SportNews sportNews) {
            this.sportNews = sportNews;
        }
        
        public void readNews() {
            System.out.println(String.format("Robot:%s,Context:%s", id, sportNews.getContext()));
        }
    }

    测试程序:

    TestMain.java:

    public class TestMain {
        public static void main(String... ars) {
            SportNews sportNews = new SportNews();
            People xiaoLi = new People("xiao li", sportNews);
            People xiaoMin = new People("xiao min", sportNews);
            Robot robot = new Robot(sportNews);
            
            sportNews.newContext("H:F 1:1");
            xiaoLi.readNews();
            xiaoMin.readNews();
            robot.readNews();
            sportNews.newContext("湖人:火箭 102:103");
            xiaoLi.readNews();
            xiaoMin.readNews();
            robot.readNews();
        }
    }

    print:

    I am xiao li,I am looking H:F 1:1
    I am xiao min,I am looking H:F 1:1
    Robot:0,Context:H:F 1:1
    I am xiao li,I am looking 湖人:火箭 102:103
    I am xiao min,I am looking 湖人:火箭 102:103
    Robot:0,Context:湖人:火箭 102:103

    上面的代码展示了xiaoli、xiaomin、Robot是怎样获取新的新闻内容的,每个人都需要调用 readNews 才可以获取新的新闻内容。如果再增加一个人,那么这个人也需要调用相应的方法才可以获取。这就相当于每个人都知道报社的地址,每天都要去报社自己拿新的报纸。那么怎样才能改变这样的关系,让人不需要去报社拿新的报纸,那就是使用观察这模式,下面就让我们来具体的介绍这个模式。

    介绍Observser

    •  定义: 定义对象之间一对多的关系,当一个对象的状态发生改变时,该对象所有依赖对象都会得到通知。 
    • 类图:

    从上面的类图中可以总结一下几点:

    1. 抽象主题类(Observable):持有一个 Observer 的集合List,通过 registerObserver 方法可以将 Observer 注册到集合中。通过 notifyServers 方法可以让所有在集合里面的 Observer 都获得通知
    2. 抽象观察者类(Observer):持有一个 Observable ,其目的是为了在 Observable 的集合中取消订阅。而注册行为是发生在构造函数中
    3. 具体主题类(ConcreteObservable):继承了 Observable ,并且实现了父类的抽象方法 changeState ,在该方法中调用了父类的方法 notifyObservers  

    4. 具体观察者类(ConcreteObserver):继承了 Observer ,并且实现了父类的方法 update 方法,该方法是具体观察者要做的动作

    重构思考题

    通过上面的学习,我们已经基本上了解了观察者模式了,下面我们针对上面提到的问题,使用观察者模式来实现它:

    首先,我们定义一个抽象主题类:

    Subject.java:

    import java.util.List;
    import java.util.ArrayList;
    
    public abstract class Subject {
        protected List<Observer> observers;
        
        public Subject() {
            observers = new ArrayList<Observer>();
        }
        
        public boolean registerObserver(Observer observer) {
            return observers.add(observer);
        }
        
        public boolean removeObserver(Observer observer) {
            return observers.remove(observer);
        }
        
        public void notifyObservers(String context) {
            for(Observer observer: observers) {
                observer.update(context);
            }
        }
        
        public abstract void newContext(String context);
    }

    然后,我们来实现抽象观察者:

    Observer.java:

    public abstract class Observer {
        private Subject subject;
        public Observer(Subject subject) {
            this.subject = subject;
            subject.registerObserver(this);
        }
        
        public abstract void update(String context);
    }

    之后,实现一个具体主题类:

    SportNews.java:

    public class SportNews extends Subject {
        @Override
        public void newContext(String context) {
            notifyObservers(String.format("sport news:%s", context));
        }
    }

    最后,实现具体观察者类:

    People.java:

    public class People extends Observer {
        private final String name;
        
        public People(String name, Subject subject) {
            super(subject);
            this.name = name;
        }
        
        @Override
        public void update(String context) {
            System.out.println(String.format("I am %s,I am looking %s", name, context));
        }
    }

    Robot.java:

    public class Robot extends Observer {
        private static int num = 0;
        private final int id = num++;
        
        public Robot(Subject subject) {
            super(subject);
        }
        
        @Override
        public void update(String context) {
            System.out.println(String.format("Robot:%s,Context:%s", id, context));
        }
    }

    测试用例:

    TestMain.java:

    public class TestMain {
        public static void main(String... ars) {
            Subject subject = new SportNews();
            People xiaoLi = new People("xiao li", subject);
            People xiaoMin = new People("xiao min", subject);
            Robot robot = new Robot(subject);
            
            subject.newContext("H:F 1:1");
            subject.newContext("湖人:火箭 102:103");
        }
    }

    以上就是观察者模式的全部代码,通过使用观察者模式,可以看出我们只需要将观察者注册到主题中。不需要调用观察者的任何方法,就可以自动的获得通知。极大的减少了代码量和维护成本。这就相当于你只要订阅了报纸,每天都会有人给你送过去,而不需要自己去报社拿。

    扩展

    在我们的java中也定义了观察者模式的接口,并且Java中的事件监听机制就是使用观察者模式实现的,它们分别是 java.util.Observer 和 java.util.Observable 。通过实现这两个接口我们可以实现自己的观察者模式,下面就让我们来看看这两个类。

    类图:

    简单介绍下上面的类图:

    1. 抽象主题类(Observable): addObserver 是用来注册观察者的。 notifyObservers 是用来通知所有的观察者的。 setChanged 是用来告知 notifyObservers 状态已经改变,可以通知了。其他的就不做介绍了。

    下面让我们使用java提供的观察者模式接口来实现上面的问题:

    People.java:

    import java.util.Observer;
    import java.util.Observable;
    public class People implements Observer {
        private final String name;
        private Observable observable;
        public People(String name, Observable o) {
            this.name = name;
            observable = o;
            observable.addObserver(this);
        }
        
        @Override
        public void update(Observable o, Object arg) {
            System.out.println(String.format("I am %s,I am looking %s", name, arg.toString()));
        }
    }

    Robot.java:

    import java.util.Observer;
    import java.util.Observable;
    
    public class Robot implements Observer {
        private static int num = 0;
        private final int id = num++;
        private Observable observable;
        
        public Robot(Observable observable) {
            this.observable = observable;
            observable.addObserver(this);
        }
        
        @Override
        public void update(Observable o, Object arg) {
            System.out.println(String.format("Robot:%s,Context:%s", id, arg.toString()));
        }
    }

    SportNews.java:

    import java.util.Observable;
    
    public class SportNews extends Observable {
        public void newContext(String context) {
            setChanged();
            notifyObservers(context);
        }
    }

    测试用例:

    TestMain.java:

    public class TestMain {
        public static void main(String... ars) {
            SportNews sportNews = new SportNews();
            People xiaoLi = new People("xiao li", sportNews);
            People xiaoMin = new People("xiao min", sportNews);
            Robot robot = new Robot(sportNews);
            
            sportNews.newContext("H:F 1:1");
            sportNews.newContext("湖人:火箭 102:103");
        }
    }

    以上是本博客的全部内容,希望看完会对你有一定的启发。

  • 相关阅读:
    HDU4507 吉哥系列故事――恨7不成妻(数位dp)
    UCF Local Programming Contest 2017 G题(dp)
    ICPC Latin American Regional Contests 2019 I题
    UCF Local Programming Contest 2017 H题(区间dp)
    HDU2089 不要62
    AcWing1084 数字游戏II(数位dp)
    UCF Local Programming Contest 2017 F题(最短路)
    Google Code Jam 2019 Round 1A Pylons(爆搜+贪心)
    AcWing1083 Windy数(数位dp)
    Vue
  • 原文地址:https://www.cnblogs.com/cafebabe-yun/p/9016921.html
Copyright © 2011-2022 走看看