• [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");
        }
    }

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

  • 相关阅读:
    java项目数据库从oracle迁移到mysql 中 java部分的一些修改
    mysql表名等大小写敏感问题、字段类型timestamp、批量修改表名、oracle查询历史操作记录等
    navicat premium相关应用(将oracle数据库迁移到mysql等)
    Java byte 类型的取值范围是-128~127
    idea中debug:
    chrome里面模拟手机上打开网页的场景方法
    Dealloc weak nil
    用七牛sdk传递图片到七牛服务器
    iOS block 本质研究
    UIWebView JSContext相关问题
  • 原文地址:https://www.cnblogs.com/cafebabe-yun/p/9016921.html
Copyright © 2020-2023  润新知