双向耦合的代码
案例描述:员工上班炒股,老板来的时候,前台秘书通知炒股的员工。
/**
* 看股票同事类
*/
public class StockObserver {
private String name;
// 耦合秘书
private Secretary sub;
public StockObserver(String name,Secretary sub) {
this.name = name;
this.sub = sub;
}
public void update() {
// 炒股的同事需要获取秘书的状态
System.out.println(String.format("%s %s关闭股票行情,继续工作", sub.getSecretaryAction(),name));
}
}
import java.util.ArrayList;
import java.util.List;
/**
* 前台秘书类
*/
public class Secretary {
// 同事列表
private List<StockObserver> observers = new ArrayList<StockObserver>();
// 前台状态
private String secretaryAction;
/**
* 增加:有几个同事请前台帮忙,于是就给集合增加几个对象
*/
public void attach(StockObserver observer) {
observers.add(observer);
}
/**
* 通知:老板来时,前台就给所有登记的同事们发通知
*/
public void notifyObservers() {
for (StockObserver stockObserver : observers) {
stockObserver.update();
}
}
public String getSecretaryAction() {
return secretaryAction;
}
public void setSecretaryAction(String secretaryAction) {
this.secretaryAction = secretaryAction;
}
}
客户端
/**
* 双向耦合代码
*/
public class Client {
public static void main(String[] args) {
// 前台小姐
Secretary secretary = new Secretary();
// 看股票的同事
StockObserver observer1 = new StockObserver("张无忌", secretary);
StockObserver observer2 = new StockObserver("白眉鹰王", secretary);
// 前台记下两位同事
secretary.attach(observer1);
secretary.attach(observer2);
// 发现老板回来
secretary.setSecretaryAction("老板来了!");
// 通知两个同事
secretary.notifyObservers();
}
}
解耦实践一
增加抽象的观察者
/**
* 抽象的观察者
*/
public abstract class Observer {
protected String name;
protected Secretary sub;
public Observer(String name,Secretary sub) {
this.name = name;
this.sub = sub;
}
public abstract void update();
}
增加了两个具体观察者
/**
* 看股票的同事
*/
public class StockObserver extends Observer {
public StockObserver(String name,Secretary sub) {
super(name, sub);
}
public void update() {
System.out.println(String.format("%s %s关闭股票行情,继续工作", sub.getSecretaryAction(),name));
}
}
/**
* 看NBA的同事
*/
public class NBAObserver extends Observer {
public NBAObserver(String name,Secretary sub) {
super(name, sub);
}
public void update() {
System.out.println(String.format("%s %s关闭NBA直播,继续工作!", sub.getSecretaryAction(),name));
}
}
减少前台秘书与具体观察者的耦合
import java.util.ArrayList;
import java.util.List;
/**
* 前台秘书类
*/
public class Secretary {
// 同事列表
private List<Observer> observers = new ArrayList<Observer>();
// 前台状态
private String secretaryAction;
/**
* 增加:针对抽象编程,减少了与具体类的耦合
*/
public void attach(Observer observer) {
observers.add(observer);
}
/**
* 减少:针对抽象编程,减少了与具体类的耦合
*/
public void detach(Observer observer) {
observers.remove(observer);
}
/**
* 通知:针对抽象编程,减少了与具体类的耦合
*/
public void notifyObservers() {
for (Observer observer : observers) {
observer.update();
}
}
public String getSecretaryAction() {
return secretaryAction;
}
public void setSecretaryAction(String secretaryAction) {
this.secretaryAction = secretaryAction;
}
}
客户端同上。
解耦实践二
增加抽象通知者
通知者接口,具体的通知者可能是前台,可能是老板,它们也许有各自的一些方法,但对于通知者来说,它们是一样的,所以它们都去实现这个接口。
public abstract class Subject {
public abstract void attach(Observer observer);
public abstract void detach(Observer observer);
public abstract void notifyObserver();
private String subjectState;
public String getSubjectState() {
return subjectState;
}
public void setSubjectState(String subjectState) {
this.subjectState = subjectState;
}
}
具体通知者
import java.util.ArrayList;
import java.util.List;
/**
* 老板
*/
public class Boss extends Subject {
private List<Observer> observers = new ArrayList<Observer>();
// 增加
@Override
public void attach(Observer observer) {
observers.add(observer);
}
// 减少
@Override
public void detach(Observer observer) {
observers.remove(observer);
}
// 通知
@Override
public void notifyObserver() {
for (Observer observer : observers) {
observer.update();
}
}
}
import java.util.ArrayList;
import java.util.List;
/**
* 前台秘书
*/
public class Secretary extends Subject {
private List<Observer> observers = new ArrayList<Observer>();
// 增加
@Override
public void attach(Observer observer) {
observers.add(observer);
}
// 减少
@Override
public void detach(Observer observer) {
observers.remove(observer);
}
// 通知
@Override
public void notifyObserver() {
for (Observer observer : observers) {
observer.update();
}
}
}
抽象观察者
/**
* 抽象观察者
*/
public abstract class Observer {
protected String name;
protected Subject sub;
// 原来是“前台”,现改成“抽象通知者”
public Observer(String name,Subject sub) {
this.name = name;
this.sub = sub;
}
public abstract void update();
}
具体观察者
/**
* 看股票的同事
*/
public class StockObserver extends Observer {
// 原来是“前台”,现改成“抽象通知者”
public StockObserver(String name, Subject sub) {
super(name, sub);
}
@Override
public void update() {
// 原来是“前台状态”,先改成“抽象通知者状态”
System.out.println(String.format("%s %s关闭股票行情,继续工作", sub.getSubjectState(), name));
}
}
/**
* 看NBA的同事
*/
public class NBAObserver extends Observer {
// 原来是“前台”,现改成“抽象通知者”
public NBAObserver(String name, Subject sub) {
super(name, sub);
}
@Override
public void update() {
// 原来是“前台状态”,先改成“抽象通知者状态”
System.out.println(String.format("%s %s关闭NBA直播,继续工作", sub.getSubjectState(), name));
}
}
客户端代码
public class Client {
public static void main(String[] args) {
Boss boss = new Boss();
// 看股票的同事
StockObserver stockObserver = new StockObserver("张无忌", boss);
// 看NBA的同事
NBAObserver nbaObserver = new NBAObserver("殷天正", boss);
boss.attach(stockObserver);
boss.attach(nbaObserver);
boss.detach(nbaObserver);
// 老板回来
boss.setSubjectState("老板回来了!");
// 发出通知
boss.notifyObserver();
}
}
观察者模式
观察者模式,又叫做发布-订阅(Publish/Subscribe)模式。
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
Subject类
Subject类,可翻译为主题或抽象通知者,一般用一个抽象类或者一个接口实现。它把所有对观察者对象的引用保存在一个聚集里,每个主题都可以有任何数量的观察者。 抽象主题提供一个接口,可以增加和删除观察者对象。
public abstract class Subject {
private List<Observer> observers = new ArrayList<Observer>();
// 增加观察者
public void attach(Observer observer) {
observers.add(observer);
}
// 移除观察者
public void detach(Observer observer) {
observers.remove(observer);
}
// 通知
public void notifyObservers() {
for (Observer observer : observers) {
observer.update();
}
}
}
Observer类
Observer类,抽象观察者,为所有的具体观察者定义一个接口,在得到主题的通知时更新自己。这个接口叫做更新接口。抽象观察者一般用一个抽象类或者一个接口实现。 更新接口通常包含一个update()方法,这个方法叫做更新方法。
public abstract class Observer {
public abstract void update();
}
ConcreteObserver类
ConcreteObserver类,具体观察者,实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。具体观察者角色可以保存一个指向具体主题对象的引用。具体观察者角色通常用一个具体子类实现。
public class ConcreteObserver extends Observer {
private String name;
private String observerState;
private ConcreteSubject subject;
public ConcreteObserver(ConcreteSubject subject, String name) {
this.subject = subject;
this.name = name;
}
@Override
public void update() {
observerState = subject.getSubjectState();
System.out.println(String.format("观察者%s的新状态是%s", name, observerState));
}
public ConcreteSubject getSubject() {
return subject;
}
public void setSubject(ConcreteSubject subject) {
this.subject = subject;
}
}
ConcreteSubject类
ConcreteSubject类,叫做具体主题或具体通知者,将有关状态存入具体观察者对象; 在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色通常用一个具体子类实现。
public class ConcreteSubject extends Subject {
// 具体被观察者状态
private String subjectState;
public String getSubjectState() {
return subjectState;
}
public void setSubjectState(String subjectState) {
this.subjectState = subjectState;
}
}
客户端代码
public class Client {
public static void main(String[] args) {
ConcreteSubject subject = new ConcreteSubject();
subject.attach(new ConcreteObserver(subject, "X"));
subject.attach(new ConcreteObserver(subject, "Y"));
subject.attach(new ConcreteObserver(subject, "Z"));
subject.setSubjectState("ABC");
subject.notifyObservers();
}
}
观察者模式特点
使用观察者模式的动机是什么?
将一个系统分割成一系列相互协作的类有一个很不好的副作用,那就是需要维护相关对象间的一致性。我们不希望为了维护一致性而使各类紧密耦合,这样会给维护、扩展和重用都带来不便。而观察者模式的关键对象是主题Subject和观察者Observer,一个Subject可以有任意数目的依赖它的Observer,一旦Subject的状态发生了改变,所有的Observer都可以得到通知。Subject发出通知时并不需要知道谁是它的观察者,也就是说,具体观察者是谁,它根本不需要知道。而任何一个具体观察者不知道也不需要知道其他观察者的存在。
什么时候应该使用观察者模式呢?
当一个对象的改变需要同时改变其他对象的时候。而且它不知道具体有多少对象有待改变时,应该考虑使用观察者模式。
一个抽象模型有两个方面,其中一方面依赖于另一方面,这时用观察者模式可以将这两者封装在独立的对象中使它们各自独立地改变和复用。
总的来说,观察者模式所做的工作其实就在解除耦合。让耦合的双方都依赖于抽象,而不是依赖于具体。从而使得各自的变化都不会影响另一边的变化。
注:本文内容源自程杰的《大话设计模式》