现在有这么一个需求让你设计一个程序,有观察对象和被观察对象,要求是被观察者属性值发生变化后,观察者能感知到这个变化,你会怎么设计该程序?
其实该问题有很多种实现方式,比如把被观察者的值放入一个缓存中,然后不断进行循环将被观察者属性最新的值和缓存里的旧值进行比较,当存在不一致情况,那么就说明被观察对象发生了变化。
但这种方式存在哪些问题呢。
-
不断循环遍历,浪费CPU资源
-
如果被观察者有些私有属性值发生变化,只能通过反射获取。
那么有更好的方式吗?不要着急,为了让大家更好的理解,我举一个生活的例子来说明。
一个家长在监督孩子写作业,请问家长如何做最省力高效,是一直看着孩子做作业,还是等家长过一会问一下作业的完成情况?都不是,最省力高效的做法是孩子完成作业的那一刻,主动的告知家长,让家长进行作业的检查。同样应用在程序中亦是如此,这也是观察者模式的精髓所在。
观察者模式包含以下几个核心角色:
- 主题(Subject):也称为被观察者或可观察者,它是具有状态的对象,并维护着一个观察者列表。主题提供了添加、删除和通知观察者的方法。
- 观察者(Observer):观察者是接收主题通知的对象。观察者需要实现一个更新方法,当收到主题的通知时,调用该方法进行更新操作。
- 具体主题(Concrete Subject):具体主题是主题的具体实现类。它维护着观察者列表,并在状态发生改变时通知观察者。
- 具体观察者(Concrete Observer):具体观察者是观察者的具体实现类。它实现了更新方法,定义了在收到主题通知时需要执行的具体操作。
观察者模式的关键代码是在被观察对象下有一个 ArrayList 存放观察者们,这样在自己发生变化后,就能通知到所有观察者(循环)。
参考代码如下:
csharp
public class ObserverMode {
public static void main(String[] args) {
Subject subject = new Subject();
new HexaObserver(subject);
new OctalObserver(subject);
new BinaryObserver(subject);
System.out.println("First state change: 15");
subject.setState(15);
System.out.println("Second state change: 10");
subject.setState(10);
}
}
class Subject {
private List<Observer> observers
= new ArrayList<Observer>();
private int state;
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
notifyAllObservers();
}
public void attach(Observer observer){
observers.add(observer);
}
//注销方法,用于在观察者集合中删除一个观察者
public void detach(Observer observer) {
observers.remove(observer);
}
public void notifyAllObservers(){
for (Observer observer : observers) {
observer.update();
}
}
}
abstract class Observer {
protected Subject subject;
public abstract void update();
}
class BinaryObserver extends Observer{
public BinaryObserver(Subject subject){
this.subject = subject;
this.subject.attach(this);
}
@Override
public void update() {
System.out.println( "Binary String: "
+ Integer.toBinaryString( subject.getState() ) );
}
}
class OctalObserver extends Observer{
public OctalObserver(Subject subject){
this.subject = subject;
this.subject.attach(this);
}
@Override
public void update() {
System.out.println( "Octal String: "
+ Integer.toOctalString( subject.getState() ) );
}
}
class HexaObserver extends Observer{
public HexaObserver(Subject subject){
this.subject = subject;
this.subject.attach(this);
}
@Override
public void update() {
System.out.println( "Hex String: "
+ Integer.toHexString( subject.getState() ).toUpperCase() );
}
}
通过代码可以看到,被观察者分别给到了3个观察者对象,观察者对象通过attach方法将自身注册到了被观察者维护的Arraylist列表中。被观察者的set方法中有个通知观察者的方法,可以在属性赋值后通知给所有观察者,这是一套标准的观察者模式用法。
但这种模式就没有缺点了吗?它最大的问题就是违反了单一职责原则(被观察者还要维护一个观察者列表),观察者和被观察者耦合度高。为了解决此问题,可以采用事件驱动的方式,被观察发生的变化可以当做一个事件。每当被观察者发生变化后,就在事件列表里存放该事件即可。这样观察者只需要循环遍历事件列表有无事件就能知道被观察对象是否发生了变化。
程序的设计源于生活,我们要善于发现生活的细节,并应用在程序中。
结束!