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

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

原文地址:https://www.cnblogs.com/cafebabe-yun/p/9016921.html