觀察者模式
為了防止被“殺”了祭天,學(xué)點設(shè)計模式茎刚,并總結(jié)下還是有必要的悲关。
一:模式理解
- 觀察者模式也可以稱為訂閱者模式朴肺。
- 無論是觀察者還是訂閱者,關(guān)注的是主題的變化坚洽。
- 主題發(fā)生某些變化時戈稿,需要發(fā)生通知給所有的觀察者和訂閱者。
- 觀察者和訂閱者可以取消對主題的觀察或訂閱讶舰。
- 一些對象需要能感知到一個對象變了(做了某些操作)鞍盗。假設(shè)只有一個主題。
二:例子
你家的仆人
你是個富二代跳昼,家里有很多仆人般甲。
為了方便,只舉例三種鹅颊,分別是程序員敷存,老司機,女仆堪伍。
他們都繼承自Servant抽象類锚烦,并且都有跪舔的技能,即guitian方法帝雇。
public abstract class Servant {
private FuErDai fuErDai;
public Servant(FuErDai fuErDai) {
this.fuErDai = fuErDai;
fuErDai.addServant(this);
}
public void add() {
fuErDai.addServant(this);
}
public void remove() {
fuErDai.fireServant(this);
}
public abstract void guitian();
}
- 每個仆人都有一個fuErDai屬性涮俄,代表心中時刻都有主人。
- 并且在構(gòu)造器中直接把自己作為富二代的仆人尸闸。
- 富二代比較民主彻亲,允許仆人們可以自主決定參加工作或是不干了,分別對應(yīng)add和remove方法吮廉。
- 不同仆人跪舔主人的方法不一樣苞尝,設(shè)置為抽象方法。
分別新建程序員宦芦,老司機宙址,女仆三個類。
// 程序員
public class Programmer extends Servant {
public Programmer(FuErDai fuErDai) {
super(fuErDai);
}
@Override
public void guitian() {
System.out.println("表演敲代碼!");
}
}
// 老司機
public class Driver extends Servant {
public Driver(FuErDai fuErDai) {
super(fuErDai);
}
@Override
public void guitian() {
System.out.println("老司機開個車讓富二代開心開心!");
}
}
// 女仆
public class Maidservant extends Servant {
public Maidservant(FuErDai fuErDai) {
super(fuErDai);
}
@Override
public void guitian() {
System.out.println("如果你追到我,我就和你嘿嘿嘿!");
}
}
作為富二代的你踪旷,也有一個對應(yīng)的類曼氛,F(xiàn)uErDai。
public class FuErDai {
private List<Servant> servantList = Lists.newArrayList();
private int happyIndex;
private int healthIndex;
public FuErDai(int happyIndex, int healthIndex) {
this.happyIndex = happyIndex;
this.healthIndex = healthIndex;
}
public void addServant(Servant servant) {
int index = servantList.indexOf(servant);
if (index == -1) {
servantList.add(servant);
}
}
public void fireServant(Servant servant) {
int index = servantList.indexOf(servant);
servantList.remove(index);
}
private void notifyServants() {
for (Servant servant : servantList) {
servant.guitian();
}
}
public void changeIndex(int happyIndex, int healthIndex) {
this.happyIndex = happyIndex;
this.healthIndex = healthIndex;
notifyServants();
}
}
你有三個屬性令野,分別是仆人隊列舀患,開心指數(shù),健康指數(shù)气破。
有添加仆人和刪除仆人的方法聊浅。
在沒有觀察者模式之前,為了防止你不舒服了或是不開心了现使,仆人們隔三差五都需要來詢問一次低匙。
仆人們發(fā)現(xiàn)每次走進你房間的時候,你總是對著藍天白云的電腦桌面發(fā)呆碳锈。同時顽冶,你也覺得很不開心。
有了觀察者模式之后售碳,當(dāng)你的開心指數(shù)或者健康指數(shù)變化的時候强重,就會通知每個仆人,即notifyServants贸人,每個仆人都會前來跪舔你间景。
public class Client {
public static void main(String[] args) {
FuErDai fuErDai = new FuErDai(100, 100);
Programmer programmer = new Programmer(fuErDai);
Driver driver = new Driver(fuErDai);
Maidservant maidservant = new Maidservant(fuErDai);
fuErDai.changeIndex(100, 90);
System.out.println("--------------");
programmer.remove();
fuErDai.changeIndex(100, 80);
}
}
輸入/輸出:
表演敲代碼!
老司機開個車讓富二代開心開心!
如果你追到我,我就和你嘿嘿嘿!
老司機開個車讓富二代開心開心!
如果你追到我,我就和你嘿嘿嘿!
可以看到,當(dāng)你健康指數(shù)變化的時候艺智,所有仆人都會前來跪舔倘要。
某天,程序員突然想起之前你答應(yīng)帶他去徹夜鼓掌卻一直沒再提起十拣,調(diào)用了remove方法封拧,不再作為你的仆人。
在你的指數(shù)再次變化的時候夭问,程序員將不會來跪舔哮缺。
三:再理解
- 在例子中,作為富二代的你就是主題甲喝,你的仆人們都是觀察者/訂閱者尝苇。
- 仆人們再也不用輪詢來檢查你的變化。
- 在通知仆人的方法notifyServants中埠胖,可以傳參糠溜,例子為方便沒傳遞參數(shù)。
- 在Java util包中有觀察者模式的實現(xiàn)直撤。
- 觀察者實現(xiàn)Observer接口非竿,實現(xiàn)update方法;
- 主題繼承Observable類谋竖,意思為可被觀察红柱,底層用Vector<Observer> obs保存所有的觀察者承匣,可做到多線程安全;
- 在主題變化時锤悄,可調(diào)用setChanged()和notifyObservers()方法來通知觀察者韧骗,觀察者們執(zhí)行update()方法;
- 觀察者可通過調(diào)用addObserver(this)和deleteObserver(this)方法加入/退出主題的觀察者隊列零聚。
- Java util自帶Observable是個類袍暴,主題繼承這個類,就不能再繼承其他類隶症,不利于擴展政模。
- 觀察者模式中,主題擁有一個觀察者列表蚂会,觀察者又引用了主題淋样。