觀察者模式
意圖
定義對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時,所有依賴於他的對象都得到通知並被自動更新
適用性
1、當一個抽象模型有兩個方面,其中一個依賴另外一個
2、當對一個對象的改變需要同時改變其他對象,而不知道具體有多少對吸納個有待改變
3、當一個對象那個必須通知其他對象,而它又不能假定其他對象是誰,換言之,你不希望這些對象時緊密耦合的
結構
Subject
目標知道他的觀察者。可以有任意多的觀察者觀察同一個目標
提供註冊和刪除觀察者對象的接口
Observer
爲那些在目標發生變化時需要被通知的對象定義一個更新接口
ConcreteSubject
將有關狀態存入各ConcreteObserver對象
當他的狀態發生改變時,向他的各個觀察者發出通知
ConcreteObserver
維護一個指向ConcreteSubject對象的引用
存儲有關狀態,這些狀態應與目標狀態保持一致
是想Observer的更新接口以使自身狀態與目標狀態保持一致
實現
現在,我們有一個遊戲,一共有5個角色,當前是其中有一個角色被攻擊的時候,通知其他角色,並且其他角色對其作出反應
package observer;
/**
* @Author fitz.bai
* @Date 2018/9/6 20:50
*/
public interface Observer {
String getName();
void setName(String name);
void help(); //update();
void beAttacked(ControlCenter controlCenter);
}
package observer;
/**
* @Author fitz.bai
* @Date 2018/9/6 21:03
*/
public class Player implements Observer {
private String name;
public Player(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public void setName(String name) {
this.name = name;
}
@Override
public void help() {
System.out.println(this.name + "--lei了,他lei了");
}
@Override
public void beAttacked(ControlCenter controlCenter) {
System.out.println(this.name + "正受到攻擊!");
controlCenter.notifyObservers(name);
}
}
package observer;
import java.util.ArrayList;
import java.util.List;
/**
* @Author fitz.bai
* @Date 2018/9/6 20:52
*/
abstract class ControlCenter {
protected String name;
protected List<Observer> players = new ArrayList<>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void attach(Observer observer) {
System.out.println(observer.getName() + "加入了遊戲!");
players.add(observer);
}
public void detach(Observer observer) {
System.out.println(observer.getName() + ",你走吧!我們能贏!");
players.remove(observer);
}
public abstract void notifyObservers(String name);
}
package observer;
/**
* @Author fitz.bai
* @Date 2018/9/6 20:58
*/
public class LGDControlCenter extends ControlCenter {
@Override
public void notifyObservers(String name) {
System.out.println("隊友呢?隊友呢?隊友呢?隊友呢?隊友呢?");
for (Observer o : players) {
if (!o.getName().equalsIgnoreCase(name)) {
o.help();
}
}
}
}
package observer;
import iterator.ConcreteList;
/**
* @Author fitz.bai
* @Date 2018/9/6 20:50
*/
public class Client {
public static void main(String[] args) {
ControlCenter controlCenter = new LGDControlCenter();
Observer player1, player2, player3, player4, player5;
player1 =new Player("船長");
controlCenter.attach(player1);
player2 =new Player("PA");
controlCenter.attach(player2);
player3 =new Player("SPE");
controlCenter.attach(player3);
player4 =new Player("AM");
controlCenter.attach(player4);
player5 =new Player("WR");
controlCenter.attach(player5);
System.out.println("=============有玩家推出了遊戲===================");
player1.beAttacked(controlCenter);
controlCenter.detach(player1);
player2.beAttacked(controlCenter);
}
}
/**船長加入了遊戲!
PA加入了遊戲!
SPE加入了遊戲!
AM加入了遊戲!
WR加入了遊戲!
船長正受到攻擊!
隊友呢?隊友呢?隊友呢?隊友呢?隊友呢?
PA--lei了,他lei了
SPE--lei了,他lei了
AM--lei了,他lei了
WR--lei了,他lei了
=============有玩家退出了遊戲===================
船長,你走吧!我們能贏!
PA正受到攻擊!
隊友呢?隊友呢?隊友呢?隊友呢?隊友呢?
SPE--lei了,他lei了
AM--lei了,他lei了
WR--lei了,他lei了
*/
總結
其實觀察者模式很簡單,可以簡單理解爲,有一個對象關聯着其他未知數目的對象,當這個對象發生變化時,通知其他對象,其他對象那個做出相應反應
加入對象A爲被觀察的對象,它維持着一衆仰慕者他的對象B,當對象A發生變化時,他會調用內部的notify方法通知他的仰慕者們,在每一個對象B中呢,維護一個指向對象A的引用,當A變化時就調用update方法作出反應
MVC
MVC(Model-View-Controller)架構中也應用了觀察者模式,其中模型(Model)可對應於觀察者模式中的觀察目標,而視圖(View)對應於觀察者,控制器(Controller)可充當兩者之間的中介者。當模型層的數據發生改變時,視圖層將自動改變其顯示內容。
優點
1、可以實現表示層和數據邏輯層的分離,定義了穩定的消息更新傳遞機制,並抽象了更新接口,使得可以有各種各樣不同的表示層充當具體觀察者角色
2、 觀察者模式在觀察目標和觀察者之間建立一個抽象的耦合。觀察目標只需要維持一個抽象觀察者的集合,無須瞭解其具體觀察者。由於觀察目標和觀察者沒有緊密地耦合在一起,因此它們可以屬於不同的抽象化層次
3、觀察者模式支持廣播通信,觀察目標會向所有已註冊的觀察者對象發送通知,簡化了一對多系統設計的難度
(4) 增加新的具體觀察者無須修改原有系統代碼,在具體觀察者與觀察目標之間不存在關聯關係的情況下,增加新的觀察目標也很方便,滿足“開閉原則”的要求
缺點
1、如果一個觀察目標對象有很多直接和間接觀察者,將所有的觀察者都通知到會花費很多時間。
2、如果在觀察者和觀察目標之間存在循環依賴,觀察目標會觸發它們之間進行循環調用,可能導致系統崩潰。
3、觀察者模式沒有相應的機制讓觀察者知道所觀察的目標對象是怎麼發生變化的,而僅僅只是知道觀察目標發生了變化。