1、什么是观察者模式?
观察者模式是一种关注对象之间责任分配的行为模式。观察者模式定义了对象之间的一对多依赖关系,这样当一个对象更改状态时,它的所有依赖关系都会被通知并自动更新。此模式中的关键对象是subject和observer。理解观察者模式的另一种方法是发布者-订阅者关系的工作方式。
观察者模式有四个参与者:
Subject:用于注册观察员。对象使用此接口注册为观察者,并将自己从观察者中移除。
Observer :对象的更新接口,该接口应在主题发生更改时通知对象。所有观察者都需要实现观察者接口。此接口有一个方法update(),当主题的状态发生更改时,将调用该方法。
ConcreteSubject :存储对ConcreteObserver对象感兴趣的状态。当状态发生变化时,它向观察者发送一个通知。具体的主题总是实现主题接口。notifyobserver()方法用于在状态发生更改时更新所有当前的观察者。
-
ConcreateObserver :维护对ConcreteSubject对象的引用,并实现Observer接口。每个观察者注册一个具体的主题来接收更新。
2、场景分析
体育大厅是一个了不起的体育爱好者的体育网站。它们涵盖了几乎所有的体育项目,提供最新的新闻、信息、比赛日期、特定球员或球队的信息。现在,他们正计划以短信服务的形式提供现场解说或数十场比赛,但仅限于高级用户。他们的目标是在短时间间隔后发送短信实时比分、比赛情况和重要事件。作为用户,您需要订阅包,当有现场比赛时,您将收到一条短信到现场解说。该站点还提供了一个选项,可以随时从包中取消订阅。作为开发人员,体育游说团要求您为他们提供这个新功能。观察者的设计模式最适合这种情况,让我们看看这个模式,然后为体育大厅创建功能。
3、代码实现
public interface Subject {
public void subscribeObserver(Observer observer);
public void unSubscribeObserver(Observer observer);
public void notifyObservers();
public String subjectDetails();
}
主题界面的三个关键方法是:
subscribeObserver,用于订阅观察员,或者我们可以说注册观察员,以便如果主题状态发生变化,应该通知所有这些观察员。
unSubscribeObserver,用于取消订阅观察者,以便如果主题状态发生更改,不应通知此未订阅的观察者。
-
notifyobserver,当主体状态发生更改时,此方法通知已注册的观察者。
另外还有一个方法subjectDetails(),这是一个简单的方法,可以根据需要使用。在这里,它的工作是返回主题的细节。现在,让我们看看观察者接口
public interface Observer { public void update(String desc); public void subscribe(); public void unSubscribe(); }
update()方法由主体在观察者上调用,以便在主体状态发生更改时通知它。
subscribe()方法用于用主题订阅自身。
-
unSubscribe()方法来取消订阅主题本身。
public interface Commentary { public void setDesc(String desc); }
记者使用上面的界面来更新评论对象上的实时评论,该接口只包含一个用于更改具体subject对象状态的方法
public class CommentaryObject implements Subject,Commentary { private final List<Observer> observers; private String desc; private final String subjectDetails; public CommentaryObject(List<Observer>observers,String subjectDetails){ this.observers = observers; this.subjectDetails = subjectDetails; } @Override public void subscribeObserver(Observer observer) { observers.add(observer); } @Override public void unSubscribeObserver(Observer observer) { int index = observers.indexOf(observer); observers.remove(index); } @Override public void notifyObservers() { System.out.println(); for(Observer observer : observers){ observer.update(desc); } } @Override public void setDesc(String desc) { this.desc = desc; notifyObservers(); } @Override public String subjectDetails() { return subjectDetails; } }
上面的类作为一个具体的主题工作,它实现了subject接口并提供了它的实现。它还存储对注册到它的观察者的引用。
public class SMSUsers implements Observer { private final Subject subject; private String desc; private String userInfo; public SMSUsers(Subject subject, String userInfo) { if (subject == null) { throw new IllegalArgumentException("No Publisher found."); } this.subject = subject; this.userInfo = userInfo; } @Override public void update(String desc) { this.desc = desc; display(); } private void display() { System.out.println("[" + userInfo + "]: " + desc); } @Override public void subscribe() { System.out.println("Subscribing " + userInfo + " to " + subject.subjectDetails() + " ..."); this.subject.subscribeObserver(this); System.out.println("Subscribed successfully."); } @Override public void unSubscribe() { System.out.println("Unsubscribing " + userInfo + " to " + subject.subjectDetails() + " ..."); this.subject.unSubscribeObserver(this); System.out.println("Unsubscribed successfully."); } }
上面的类是实现观察者接口的具体观察者类。它还存储对它订阅的主题的引用,并可选地存储用于显示用户信息的userInfo变量。现在,让我们测试这个例子。
public class TestObserver { public static void main(String[] args) { Subject subject = new CommentaryObject(new ArrayList<>(), "Soccer Match[2014AUG24]"); Observer observer = new SMSUsers(subject, "Adam Warner [New York]"); observer.subscribe(); System.out.println(); Observer observer2 = new SMSUsers(subject, "Tim Ronney [London]"); observer2.subscribe(); Commentary cObject = ((Commentary) subject); cObject.setDesc("Welcome to live Soccer match"); cObject.setDesc("Current score 0-0"); System.out.println(); observer2.unSubscribe(); System.out.println(); cObject.setDesc("It’s a goal!!"); cObject.setDesc("Current score 1-0"); System.out.println(); Observer observer3 = new SMSUsers(subject, "Marrie [Paris]"); observer3.subscribe(); System.out.println(); cObject.setDesc("It’s another goal!!"); cObject.setDesc("Half-time score 2-0"); } }
正如您所看到的,最初有两个用户订阅了足球比赛并开始接收评论。但是后来有一个用户取消了订阅,所以用户没有再收到评论。然后,另一个用户订阅并开始获得评论。所有这一切都在不改变现有代码的情况下动态发生,不仅如此,假设公司想要在电子邮件上播放评论,或者任何其他公司想要与该公司合作来播放评论。您所需要做的就是创建两个新类,如UserEmail和ColCompany。并通过实现observer接口使它们成为主题的观察者。只要主体知道它是一个观察者,它就会提供更新。
4、何时使用观察者模式?
- 当抽象有两个方面时,一个依赖于另一个。将这些方面封装在单独的对象中,可以独立地改变和重用它们。
- 当对一个对象的更改需要更改其他对象时,您不知道需要更改多少对象。
- 当一个对象应该能够通知其他对象而不需要假设这些对象是谁时。换句话说,您不希望这些对象紧密耦合。