设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的;设计模式使代码编制真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。
设计模式分为三种类型,共23种。
创建型模式(5):单例模式、抽象工厂模式、建造者模式、工厂模式、原型模式。
结构型模式(7):适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。
行为型模式(11):(父子类)策略模式、模版方法模式,(两个类)观察者模式、迭代器模式、职责链模式、命令模式,(类的状态)状态模式、备忘录模式,(中间类) 访问者模式、中介者模式、解释器模式。
观察者模式(Observer Pattern):定义对象之间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。观察者模式的别名包括发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。观察者模式是一种对象行为型模式。
Observer Pattern:Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
观察者模式结构中通常包括观察目标和观察者两个继承层次结构,其结构如图3所示:
在观察者模式结构图中包含如下几个角色:
下面通过示意代码来对该模式进行进一步分析。首先我们定义一个抽象目标Subject,典型代码如下所示:
public abstract class Subject {
//定义一个观察者集合用于存储所有观察者对象
protected ArrayList observers = new ArrayList();
//注册方法,用于向观察者集合中增加一个观察者
public void attach(Observer observer) {
observers.add(observer);
}
//注销方法,用于在观察者集合中删除一个观察者
public void detach(Observer observer) {
observers.remove(observer);
}
//声明抽象通知方法
public abstract void notify();
}
具体目标类ConcreteSubject是实现了抽象目标类Subject的一个具体子类,其典型代码如下所示:
public class ConcreteSubject extends Subject {
//实现通知方法
public void notify() {
//遍历观察者集合,调用每一个观察者的响应方法
for(Object obs:observers) {
((Observer)obs).update();
}
}
}
抽象观察者角色一般定义为一个接口,通常只声明一个update()方法,为不同观察者的更新(响应)行为定义相同的接口,这个方法在其子类中实现,不同的观察者具有不同的响应方法。抽象观察者Observer典型代码如下所示:
public interface Observer {
//声明响应方法
public void update();
}
在具体观察者ConcreteObserver中实现了update()方法,其典型代码如下所示:
public class ConcreteObserver implements Observer {
//实现响应方法
public void update() {
//具体响应代码
}
}
在有些更加复杂的情况下,具体观察者类ConcreteObserver的update()方法在执行时需要使用到具体目标类ConcreteSubject中的状态(属性),因此在ConcreteObserver与ConcreteSubject之间有时候还存在关联或依赖关系,在ConcreteObserver中定义一个ConcreteSubject实例,通过该实例获取存储在ConcreteSubject中的状态。如果ConcreteObserver的update()方法不需要使用到ConcreteSubject中的状态属性,则可以对观察者模式的标准结构进行简化,在具体观察者ConcreteObserver和具体目标ConcreteSubject之间无须维持对象引用。如果在具体层具有关联关系,系统的扩展性将受到一定的影响,增加新的具体目标类有时候需要修改原有观察者的代码,在一定程度上违反了“开闭原则”,但是如果原有观察者类无须关联新增的具体目标,则系统扩展性不受影响。
JavaSE中已经提供了Observer接口和Observable类让你简单快速的实现观察者模式,因此有必要去了解Observer和Observable;
Observer为java.util包下的一个接口,源码如下:
public interface Observer {
void update(Observable o, Object arg);
}
update方法第一个参数为被观察者对象,可以调用它的public方法。为观察者提供了一种拉取数据的方式
第二个参数由调用者调用notifyObservers(Object obj)将一些信息推过来。
Observable类同样位于java.util包,该类的成员变量和方法(省略其具体实现)如下:
public class Observable {
private boolean changed = false;
private Vector obs;
public Observable(){};
protected synchronized void setChanged(){};
protected synchronized void clearChanged(){};
public synchronized void addObserver(Observer o){};
public synchronized void deleteObserver(Observer o) {};
public synchronized void deleteObservers(){};
public synchronized boolean hasChanged(){};
public synchronized int countObservers(){};
public void notifyObservers(){};
public void notifyObservers(Object arg){};
}
Vector相比于ArrayList来说,它是线程安全的。其次,在添加和删除观察者时对两个方法使用了synchronized关键字,这都是在为多线程考虑。
最关键的方法是notifyObservers(),来看下源码:
public void notifyObservers(Object arg) {
Object[] arrLocal;
synchronized (this) {
if (!changed)
return;
arrLocal = obs.toArray();
clearChanged();
}
for (int i = arrLocal.length-1; i>=0; i--)
((Observer)arrLocal[i]).update(this, arg);
}
这个方法相当的严谨,考虑了线程安全等问题。
从这个方法可以看出,只有changed为true,才会执行各个观察者的update方法。对changed状态的改变的唯一途径是protected synchronized void setChanged(){}; 而protected修饰了setChanged方法,表明只有它的子类才能调用它(打开这个开关),所以客户端无论怎么调用notifyObservers()方法还是无济于事。
Observable类只提供这个boolean值来表明是否发生变化,而不定义什么叫变化,因为每个业务中对变化的具体定义不一样,因此子类自己来判断是否变化;
观察者模式是很常用的模式,尤其在界面编程,比如android中的BaseAdapter,就使用了观察者模式,当数据源发生变化时,通知界面重新绘制。下面我们来利用jdk中提供的Observer和Observable来实现一个观察者模式的例子,例子非常得有趣。
需求:
镇上来了一位小丑,为大家表演节目,所有观看的观众会根据小丑表演的精彩与否来做出相应的反应,比如表演的好就鼓掌喝彩,表演的不好就倒喝彩,表演完毕观众就退场。
分析:
这里涉及到小丑和观众,小丑的数量是1,而观众的数量是n,观众会对小丑的举动做出相应的反应。这里很符合观察者模式的场景,因此,我们需要创建一个小丑类Clown,作为被观察对象,因此需要继承Observable类,同时具有表演的行为,退场的行为;同时需要创建一个观众类Viewer,作为观察者,因此需要实现Observer接口,同时观众具有喝彩的行为,倒喝彩的行为,退场的行为,每个观众还对应一个座位号。
public class Clown extends Observable{
public static final int PERFORM_GOOD = 0;
public static final int PERFORM_BAD = 1;
public static final int PERFORM_COMPLETE = 2;
public void perfom() {
if(countObservers()==0) {
System.out.println("都没人,表演个毛线!!!!");
return;
}
//此参数可以由客户端传递过来
int random = new Random().nextInt(2);
setChanged();
notifyObservers(random);
}
public void exit() {
System.out.println("表演结束,谢谢大家!");
setChanged();
notifyObservers(PERFORM_COMPLETE);
}
}
public class Viewer implements Observer{
private int seatNo;//座位号
public Viewer(int seatNo) {
this.seatNo = seatNo;
}
@Override
public void update(Observable o, Object arg) {
int r = (int)arg;
switch (r) {
case Clown.PERFORM_BAD:
System.out.println(seatNo+"号观众:表演得太烂了吧!");
if(new Random().nextBoolean()) {
o.deleteObserver(this);
System.out.println(seatNo+"号观众已经中途退场!!!!");
}
break;
case Clown.PERFORM_GOOD:
System.out.println(seatNo+"号观众:太好笑了,不错不错!");
break;
case Clown.PERFORM_COMPLETE:
o.deleteObserver(this);
System.out.println(seatNo+"号观众离场");
break;
}
}
}
public class ObserverApp {
public static void main(String[] args) {
Clown c = new Clown();
for(int i=0;i<10;i++) {
Viewer v = new Viewer(i+1);
c.addObserver(v);
}
System.out.println("观众都已经入座,小丑,请开始你的表演!");
c.perfom();
System.out.println("请在换个节目表演一次!");
c.perfom();
System.out.println("请在换个节目表演一次!");
c.perfom();
c.exit();
c.perfom();
}
}
观众都已经入座,小丑,请开始你的表演!
10号观众:太好笑了,不错不错!
9号观众:太好笑了,不错不错!
8号观众:太好笑了,不错不错!
7号观众:太好笑了,不错不错!
6号观众:太好笑了,不错不错!
5号观众:太好笑了,不错不错!
4号观众:太好笑了,不错不错!
3号观众:太好笑了,不错不错!
2号观众:太好笑了,不错不错!
1号观众:太好笑了,不错不错!
请在换个节目表演一次!
10号观众:表演得太烂了吧!
10号观众已经中途退场!!!!
9号观众:表演得太烂了吧!
9号观众已经中途退场!!!!
8号观众:表演得太烂了吧!
8号观众已经中途退场!!!!
7号观众:表演得太烂了吧!
6号观众:表演得太烂了吧!
5号观众:表演得太烂了吧!
5号观众已经中途退场!!!!
4号观众:表演得太烂了吧!
3号观众:表演得太烂了吧!
2号观众:表演得太烂了吧!
1号观众:表演得太烂了吧!
1号观众已经中途退场!!!!
请在换个节目表演一次!
7号观众:表演得太烂了吧!
6号观众:表演得太烂了吧!
6号观众已经中途退场!!!!
4号观众:表演得太烂了吧!
4号观众已经中途退场!!!!
3号观众:表演得太烂了吧!
2号观众:表演得太烂了吧!
表演结束,谢谢大家!
7号观众离场
3号观众离场
2号观众离场
都没人,表演个毛线!!!!
可以参考博客:【java】Observer和Observable详解
参考电子书下载:设计模式的艺术–软件开发人员内功修炼之道_刘伟(2013年).pdf
《道德经》第六章:
谷神不死,是谓玄牝。玄牝(pin)之门,是谓天地之根。绵绵呵!其若存!用之不堇(jin)。
译文:生养天地万物的道(谷神)是永恒长存的,这叫做玄妙的母性。玄妙母体的生育之产门,这就是天地的根本。连绵不绝啊!它就是这样不断的永存,作用是无穷无尽的。