觀察者模式學(xué)習(xí)筆記(詳細(xì))
一首量、什么是觀察者模式
觀察者模式,是定義對(duì)象之間的一對(duì)多的關(guān)系进苍,主要作用是減少對(duì)象之間的耦合度加缘,分為兩個(gè)角色
- 被觀察者:其實(shí)就是發(fā)布者,發(fā)布消息通知所有的觀察者
- 觀察者:接到被觀察者發(fā)布的消息做出相應(yīng)的動(dòng)作
上圖中觉啊,左邊一組是被觀察者拣宏,右邊一組是觀察者
Subjecct:被觀察者抽象類,擁有類成員ObserverList杠人,和三個(gè)抽象方法
ObserverList:存放所有的觀察者對(duì)象
addObserver():向ObserverList中添加觀察者對(duì)象
delObject():從ObserverList中刪除觀察者對(duì)象
notifyObservers():通知所有的觀察者對(duì)象SubjectItem:具體的被觀察者類勋乾,繼承Subject接口,有具體的動(dòng)作方法嗡善,調(diào)用父類的notifyObject()方法通知所有的觀察者
Observer:觀察者接口各吨,有一個(gè)抽象方法update()昭躺,供被觀察者調(diào)用
ObserverItem:具體的觀察者類,實(shí)現(xiàn)Observer類似舵,實(shí)現(xiàn)update()方法
二、使用場(chǎng)景
游戲中,被觀察者主角有所動(dòng)作就通知觀察者們
支付中仅淑,一個(gè)商品購(gòu)買成功,需要執(zhí)行許多操作(更新訂單,發(fā)送郵件,更新賬戶...)赋朦,這時(shí)就可以使用觀察者模式來(lái)減少耦合
再比如琳拨,關(guān)注動(dòng)態(tài),動(dòng)態(tài)更新颜启,需要發(fā)送通知給所有的關(guān)注者
三、具體實(shí)現(xiàn)
簡(jiǎn)單的一個(gè)冒險(xiǎn)游戲,勇者做為被觀察者川抡,觀察者有怪物耐床,食物老玛,NPC
當(dāng)勇者移動(dòng)碰到怪物時(shí)血量減1镜廉,碰到食物時(shí)血量加1寂玲,碰到NPC時(shí)展開(kāi)劇情
- 編寫(xiě)Observer接口
public interface Observer {
public void update();
}
- 編寫(xiě)Subject抽象類
public abstract class Subject {
//觀察者集合
private List<Observer> observerList = new ArrayList<>();
//添加觀察者
public void addObserver(Observer observer){
observerList.add(observer);
}
//刪除觀察者
public void delObserver(Observer observer){
observerList.remove(observer);
}
//通知所有觀察者
public void notifyObservers(){
for(Observer observer : observerList){
observer.update();
}
}
}
observerList集合存放了觀察者接口流纹,不直接存放觀察者類
notifyObservers()方法遍歷observerList集合茸炒,調(diào)用update()方法
通過(guò)抽象層減少耦合
- 編寫(xiě)勇者類(被觀察者)
public class Hero extends Subject {
static int X = 0;
static int Y = 0;
//勇者移動(dòng)
public void move(int x, int y){
X = x;
Y = y;
super.notifyObservers();
}
}
Hero類有move()方法慎玖,讓勇者移動(dòng)并調(diào)用父類的notifyObservers()方法通知所有的觀察者
- 編寫(xiě)怪物類(觀察者)
public class Monster implements Observer {
static int X = 5;
static int Y = 7;
@Override
public void update() {
if(Hero.X == X && Hero.Y == Y){
System.out.println("怪物攻擊勇者!");
}
}
}
Monster類實(shí)現(xiàn)Observer接口润努,實(shí)現(xiàn)具體的update()方法
- 編寫(xiě)食物類(觀察者)
public class Food implements Observer {
static int X = 8;
static int Y = 8;
@Override
public void update() {
if(Hero.X == X && Hero.Y == Y){
System.out.println("勇者吃到了食物关斜,血量加1!");
}
}
}
Food類實(shí)現(xiàn)Observer接口铺浇,實(shí)現(xiàn)具體的update()方法
- 編寫(xiě)npc類(觀察者)
public class Npc implements Observer {
static int X = 15;
static int Y = 20;
@Override
public void update() {
if(Hero.X == X && Hero.Y == Y){
System.out.println("勇者遇到NPC鳍侣,劇情展開(kāi)惑折!");
}
}
}
Npc類實(shí)現(xiàn)Observer接口粗卜,實(shí)現(xiàn)具體的update()方法
- 編寫(xiě)執(zhí)行類
public class Run {
public static void main(String[] args){
//初始化對(duì)象
Hero hero = new Hero();
Monster monster = new Monster();
Food food = new Food();
Npc npc = new Npc();
//添加觀察者
hero.addObserver(monster);
hero.addObserver(food);
hero.addObserver(npc);
//勇者移動(dòng),遇到怪物
hero.move(5,7);
//勇者移動(dòng),吃到食物
hero.move(8,8);
//勇者移動(dòng),遇到npc
hero.move(15,20);
}
}
勇者移動(dòng)時(shí)通知了所有已注冊(cè)的觀察者呜投,執(zhí)行各自的update方法
- 運(yùn)行結(jié)果
四辑甜、總結(jié)
優(yōu)點(diǎn):
使用抽象的方式來(lái)減少被觀察者和觀察者之間的耦合,可以方便的增加觀察者缺點(diǎn):
如果被觀察者有過(guò)多的直接觀察者和間接觀察者的話袍冷,會(huì)花費(fèi)更多的時(shí)間磷醋,如果觀察者之間存在循環(huán)依賴的話,會(huì)導(dǎo)致他們直接進(jìn)行循環(huán)調(diào)用胡诗,最終導(dǎo)致系統(tǒng)崩潰
觀察者可以知道被觀察者發(fā)生的變化邓线,但是無(wú)法知道被觀察者是如何發(fā)生的變化