目錄
思維導(dǎo)圖
一、什么是觀察者模式?
二、有什么優(yōu)點(diǎn)嗎?
三、有什么缺點(diǎn)嗎?
四、什么時(shí)候使用觀察者模式?
五、代碼展示
①、雙向耦合的代碼
②、解耦實(shí)踐一
③、解耦實(shí)踐二
④、觀察者模式
六、這個(gè)模式涉及到了哪些知識(shí)點(diǎn)?
思維導(dǎo)圖
一、什么是觀察者模式?
又叫發(fā)布-訂閱(publish/Subscrib)模式。定義了一種一對(duì)多的依賴關(guān)系,讓多個(gè)觀察者對(duì)象同時(shí)監(jiān)聽某一個(gè)主題對(duì)象。這個(gè)主題對(duì)象在狀態(tài)發(fā)生變化時(shí),會(huì)通知所有觀察者對(duì)象,使他們能夠自動(dòng)更新自己。
觀察者類似于臥底這樣一個(gè)角色。在上班的時(shí)候,難免會(huì)有人趁老板不在的時(shí)候做一些工作之外的事情,例如炒炒股,看看NBA之類的,那如何應(yīng)對(duì)老板突然回來這件事情呢?我們就需要臥底這樣一個(gè)人給我們放風(fēng),老板一回來就通知這些人“老板回來了”。這其實(shí)就是一種觀察者模式
Subject類:它把所有對(duì)觀察者對(duì)象的引用保存在一個(gè)聚集里,每個(gè)主題都可以有任何數(shù)量的觀察者。抽象主題提供一個(gè)接口,可以增加和刪除觀察者對(duì)象。
Observer類:抽象觀察者,為所有的具體觀察者定義一個(gè)接口,在得到主題的通知時(shí)更新自己。
ConcreteSubject類:具體主題,將有關(guān)狀態(tài)存入具體觀察者對(duì)象;在具體主題的內(nèi)部狀態(tài)改變時(shí),給所有登記過的觀察者發(fā)出通知。
ConcreteObserver類:具體觀察者,實(shí)現(xiàn)抽象觀察者角色所要求的更新接口,以便使本身的狀態(tài)與主題的狀態(tài)相協(xié)調(diào)。
二、有什么優(yōu)點(diǎn)嗎?
觀察者模式所做的工作其實(shí)就是在解除耦合,讓耦合的雙方都依賴于抽象,而不是依賴于具體。從而使得各自的變化都不會(huì)影響另一邊的變化。滿足依賴倒轉(zhuǎn)原則
三、有什么缺點(diǎn)嗎?
如果觀察者很多的話,一個(gè)一個(gè)的通知會(huì)影響效率
四、什么時(shí)候使用觀察者模式?
當(dāng)一個(gè)對(duì)象的改變需要同時(shí)改變其他對(duì)象的時(shí)候。
而且它不知道具體有多少對(duì)象有待改變時(shí),應(yīng)該考慮使用觀察者模式。
五、代碼展示
場景:小菜公司的同事在老板外出的期間偷偷地通過網(wǎng)頁看股票行情,但是一不小心老板就回來了,讓老板看到工作的時(shí)候做這些總是不太好的,如果避免這樣的情況呢?就需要前臺(tái)秘書這樣的一個(gè)臥底,老板一回來了,前臺(tái)秘書童子喆就撥了個(gè)電話告訴同事。但有一次因?yàn)槔习寤貋碇苯泳驼彝訂醋鲆恍┢渌虑?,童子喆就沒來得及打電話,導(dǎo)致同事就沒被通知到“老板回來了”
①、雙向耦合的代碼
//前臺(tái)秘書類
class Secretary
{
//同事列表
private IList<StockObserver> observers = new List<StockObserver>(); //IList接口,List泛型,集合數(shù)據(jù)類型是StockObserver
private string action; //成員變量
//構(gòu)造方法
public Secretary() { }
//增加
public void Attach(StockObserver observer)
{
observers.Add(observer); //向集合里面添加同事
}
//通知
public void Notify()
{
foreach (StockObserver o in observers) //foreach遍歷集合:數(shù)據(jù)類型 變量名 in 遍歷的集合
o.Update(); //一個(gè)一個(gè)的給同事通知
}
//前臺(tái)狀態(tài)
public string SecretaryAction
{
get { return action; } //get訪問器
set { action = value; } //set訪問器
}
}
//看股票同事類
class StockObserver
{
private string name; //字符串成員變量
private Secretary sub; //前臺(tái)秘書類類型成員變量
public StockObserver(string name, Secretary sub) //構(gòu)造方法
{
this.name = name; //賦值
this.sub = sub;
}
public void Update() //通知
{
Console.WriteLine("{0}{1}關(guān)閉股票行情,繼續(xù)工作!", sub.SecretaryAction, name); //要通知的語句
}
}
客戶端代碼:
//前臺(tái)小姐童子喆
Secretary tongzizhe = new Secretary();
//看股票的同事
StockObserver tongshi1 = new StockObserver("魏關(guān)姹", tongzizhe);
StockObserver tongshi2 = new StockObserver("易管查", tongzizhe);
//前臺(tái)記下了兩位同事
tongzizhe.Attach(tongshi1);
tongzizhe.Attach(tongshi2);
//發(fā)現(xiàn)老板回來
tongzizhe.SecretaryAction = "老板回來了";
//通知兩個(gè)同事
tongzizhe.Notify();
Console.ReadKey ();
問題:前臺(tái)類和看股票者類之間互相耦合。
②、解耦實(shí)踐一
抽象觀察類
abstract class Observer
{
protected string name;
protected Secretary sub;
public Observer(string name, Secretary sub) //有參的構(gòu)造方法
{
this.name = name;
this.sub = sub;
}
//通知
public abstract void Update();
}
看股票的同事
class StockObserver : Observer
{
//繼承父類的構(gòu)造方法
public StockObserver(string name, Secretary sub) : base(name, sub) { }
public override void Update()
{
Console.WriteLine("{0}{1} 關(guān)閉股票行情,繼續(xù)工作!", sub.SecretaryAction, name);
}
}
看NBA的同事
class NBAObserver : Observer
{
public NBAObserver(string name, Secretary sub) : base(name, sub) { }
public override void Update()
{
Console.WriteLine("{0}{1} 關(guān)閉NBA直播,繼續(xù)工作!", sub.SecretaryAction, name);
}
}
? ?前臺(tái)秘書類
class Secretary
{
private IList<Observer> observers = new List<Observer>(); //集合
private string action; //成員變量
//增加
public void Attach(Observer observer) //針對(duì)抽象編程,減少了與具體類的耦合
{
observers.Add(observer);
}
//減少
public void Detach(Observer observer) //針對(duì)抽象編程,減少了與具體類的耦合
{
observers.Remove(observer);
}
//通知
public void Notify()
{
foreach (Observer o in observers)
o.Update();
}
//前臺(tái)狀態(tài)
public string SecretaryAction //屬性
{
get { return action; }
set { action = value; }
}
}
客戶端代碼
//前臺(tái)小姐童子喆
Secretary tongzizhe = new Secretary();
//看股票的同事
StockObserver tongshi1 = new StockObserver("魏關(guān)姹", tongzizhe);
StockObserver tongshi2 = new StockObserver("易管查", tongzizhe);
//前臺(tái)記下了兩位同事
tongzizhe.Attach(tongshi1);
tongzizhe.Attach(tongshi2);
//發(fā)現(xiàn)老板回來
tongzizhe.SecretaryAction = "老板回來了";
//通知兩個(gè)同事
tongzizhe.Notify();
Console.ReadKey ();
③、解耦實(shí)踐二
? ? 通知者接口
interface Subject
{
void Attach(Observer observer); //增加
void Detach(Observer observer); //減少
void Notify(); //通知
string SubjectState //前臺(tái)狀態(tài)
{
get;
set;
}
}
? ? 老板
class Boss : Subject
{
//同事列表
private IList<Observer> observers = new List<Observer>();
private string action;
//增加
public void Attach(Observer observer)
{
observers.Add(observer);
}
//減少
public void Detach(Observer observer)
{
observers.Remove(observer);
}
//通知
public void Notify()
{
foreach (Observer o in observers)
o.Update();
}
//前臺(tái)狀態(tài)
public string SubjectState
{
get { return action; }
set { action = value; }
}
}
前臺(tái)秘書類
class Secretary
{
//同事列表
private IList<Observer> observers = new List<Observer>();
private string action;
//增加
public void Attach(Observer observer)
{
observers.Add(observer);
}
//減少
public void Detach(Observer observer)
{
observers.Remove(observer);
}
//通知
public void Notify()
{
foreach (Observer o in observers)
o.Update();
}
//前臺(tái)狀態(tài)
public string SubjectState
{
get { return action; }
set { action = value; }
}
}
抽象觀察者
abstract class Observer
{
protected string name; //成員變量,姓名
protected Subject sub; //成員變量,通知者
public Observer(string name, Subject sub) //構(gòu)造方法
{
this.name = name; //賦值
this.sub = sub;
}
public abstract void Update();
}
看股票的同事
class StockObserver : Observer
{
public StockObserver(string name, Subject sub) : base(name, sub) { }
public override void Update()
{
Console.WriteLine("{0}{1} 關(guān)閉股票行情,繼續(xù)工作!", sub.SubjectState, name);
}
}
#endregion
客戶端代碼
//老板胡漢三
Boss huhansan = new Boss();
//看股票的同事
StockObserver tongshi1 = new StockObserver("魏關(guān)姹", huhansan);
//看NBA的同事
StockObserver tongshi2 = new StockObserver("易管查", huhansan);
huhansan.Attach(tongshi1);
huhansan.Attach(tongshi2);
huhansan.Detach(tongshi1); //魏關(guān)姹其實(shí)是沒有被老板通知到,所以減去
//老板回來
huhansan.SubjectState = "我胡漢三回來了";
//發(fā)出通知
huhansan.Notify();
Console.ReadKey();
④、觀察者模式
Subject:抽象通知者/主題,一般用一個(gè)抽象類或者一個(gè)接口實(shí)現(xiàn).它把所有對(duì)觀察者對(duì)象的引用保存在一個(gè)聚集里,每個(gè)主題都可以由任何數(shù)量的觀察者.抽象主題提供一個(gè)接口,可以增加和刪除觀察者對(duì)象
abstract class Subject
{
private IList<Observer> observers = new List<Observer>();
//增加觀察者
public void Attach(Observer observer)
{
observers.Add(observer);
}
//刪除觀察者
public void Detach(Observer observer)
{
observers.Remove(observer);
}
//通知
public void Notify()
{
foreach (Observer o in observers)
o.Update();
}
}
Observer類:抽象觀察者,為所有的具體觀察者定義一個(gè)接口,在得到主題的通知時(shí)更新自己.這個(gè)接口叫做更新接口.抽象觀察者一般用一個(gè)抽象類或者一個(gè)接口實(shí)現(xiàn).更新接口通常包含一個(gè)Update()方法,這個(gè)方法叫做更新方法.
abstract class Observer
{
public abstract void Update();
}
ConcreteSubject:具體主題/具體通知者,將有關(guān)狀態(tài)存入具體觀察者對(duì)象;在具體主題的內(nèi)部狀態(tài)改變時(shí),給所有登記過的觀察者發(fā)出通知.具體主題角色同一個(gè)具體子類實(shí)現(xiàn).
class ConcreteSubject : Subject
{
private string subjectState;
//具體被觀察者狀態(tài)
public string SubjectState
{
get { return subjectState; }
set { subjectState = value; }
}
}
//?ConcreteObserver:具體觀察者,實(shí)現(xiàn)抽象觀察者角色所要求的更新接口,以便使本身的狀態(tài)與主題的狀態(tài)相協(xié)調(diào).具體觀察者角色可以保存一個(gè)指向具體主題對(duì)象的引用.具體觀察者角色通常用一個(gè)具體子類實(shí)現(xiàn).
class ConcreteObserver : Observer
{
//成員變量
private string name;
private string observerState;
private ConcreteSubject subject;
//構(gòu)造方法
public ConcreteObserver(ConcreteSubject subject,string name)
{
this.subject = subject; //賦值
this.name = name;
}
//重寫抽象類Update方法
public override void Update()
{
observerState = subject.SubjectState; //賦值
Console.WriteLine("觀察者{0}的新狀態(tài)是{1}", name, observerState); //控制臺(tái)輸出結(jié)果
}
//具體觀察者狀態(tài)
public ConcreteSubject Subject
{
get { return subject; }
set { subject = value; }
}
}
觀察者模式的動(dòng)機(jī)是將一個(gè)系統(tǒng)分割成一系列相互協(xié)作的類有一個(gè)很不好的副作用,那就需要維護(hù)相關(guān)對(duì)象間的一致性.我們不希望為了維持一致性而使各類緊密耦合,這樣會(huì)給維護(hù)\擴(kuò)展和重用都帶來不便.
六、這個(gè)模式涉及到了哪些知識(shí)點(diǎn)?
①、一個(gè)類里面有哪些東西?
②、字段和屬性
字段
是什么?與類相關(guān)的變量;
干什么的?用來保存數(shù)據(jù)
屬性
是什么?一個(gè)方法或一對(duì)方法
什么作用?具有兩個(gè)方法,get、set訪問器,讀、寫值
- get訪問器:返回與聲明的屬性相同的數(shù)據(jù)類型,表示的意思是調(diào)用時(shí)可以得到內(nèi)部字段的值或引用;
- set訪問器:沒有顯式設(shè)置參數(shù),但它有一個(gè)隱式參數(shù),用關(guān)鍵字value表示,它的作用是調(diào)用屬性時(shí)可以給內(nèi)部的字段或引用賦值
顯示和隱式字段初始化
字段的初始值必須是編譯時(shí)可確定的。
如果沒有初始化語句,字段的值會(huì)被編譯器設(shè)為默認(rèn)值,默認(rèn)值由字段的類型決定。每種值類型的默認(rèn)值都是0,bool型是false,引用類型默認(rèn)為null。
class MyClass、
{
int F1; //初始化為0 -值類型
string F2; //初始化為null -引用類型
int F3 =25; //初始化為25
string F4="abcd"; //初始化為“abcd”
//聲明多個(gè)字段,東逗號(hào)分隔
int F1,F3 =25;
string F2,F4="abcd";
}
③、訪問修飾符
什么作用?指定程序的其他部分如何訪問成員
有哪些?
- private:私有的,只在類的內(nèi)部可訪問,成員默認(rèn)是這個(gè)
- public:公有的,對(duì)任何類可訪問
- protected:受保護(hù)的,只允許該類的派生類訪問
- internal:內(nèi)部的,同一項(xiàng)目所有類可訪問
④、構(gòu)造方法
目的:對(duì)類進(jìn)行初始化
特點(diǎn):與類同名,無返回值、無void、在new時(shí)候調(diào)用
所有的類都有構(gòu)造方法,不帶參數(shù)的構(gòu)造方法稱為“默認(rèn)構(gòu)造方法”,如果你不編碼則系統(tǒng)默認(rèn)生成空的構(gòu)造方法。若定義了新的構(gòu)造方法,那么默認(rèn)的構(gòu)造方法就會(huì)失效。
⑤、方法重載
目的:在不改變?cè)椒ǖ幕A(chǔ)上,新增功能
特點(diǎn):一同二不同:多個(gè)方法名相同、參數(shù)類型/個(gè)數(shù)不同
class Animal
{
private string name;
//方法重載:方法名相同、數(shù)據(jù)類型/個(gè)數(shù)不同
public Animal(){} //無參的構(gòu)造方法
public Animal(string name) //有參的構(gòu)造方法
{
this.name = name;
}
}
⑥、foreach循環(huán)
連續(xù)訪問數(shù)組中的每一個(gè)元素。
//語法
foreach(Type Identifier in ArrayName)
Statement
//實(shí)例
int[] arr1 = {10,11,12,13};
foreach(int item in arr1)
Console.WriteLine("Item Value:{0}",item);
⑦、List泛型集合
- arrayList集合:不知道存什么類型,不知道存多少個(gè)
- List:知道存什么類型,不知道存多少個(gè)。就是為了專門處理某種類型,在尖括號(hào)中寫什么類型,這個(gè)集合就變成了什么類型的集合
- 添加數(shù)據(jù)、插入數(shù)據(jù)、索引訪問數(shù)據(jù)都是這個(gè)類型的,不用考慮所有的轉(zhuǎn)換問題
static void Main(string[] args)
{
List<int> list = new List<int>(); //實(shí)例化int類型
//隨機(jī)的往這個(gè)List集合中添加十個(gè)數(shù)字,不能重復(fù),求和,求最大值,求最小值,求平均值
Random r = new Random();
int num = 0;
while (list.Count !=10)
{
num = r.Next(1, 100);
if (!list.Contains (num))
{
list.Add(num);
}
}
Console.WriteLine("最大值:{0}",list.Max ());
Console.WriteLine("最小值:{0}",list.Min ());
Console.WriteLine("和為:{0}",list .Sum ());
Console.WriteLine("平均值為:{0}",list.Average ());
Console.ReadKey();
List<string> listStr = new List<string>(); //實(shí)例化string類型
listStr.Add("哈哈,小楊又變帥了");
}
⑧、數(shù)組、ArrayList和List三者之間的對(duì)比:
⑨、抽象類
目的:抽取相同代碼,實(shí)現(xiàn)封裝思想
特點(diǎn):
- 抽象類不能實(shí)例化;
- 抽象方法是必須被子類重寫的方法;
- 如果類中包含抽象方法,那么類就必須定義為抽象類,不論是否還包含其他一般方法
⑩、重寫
將父類實(shí)現(xiàn)替換為它自己的實(shí)現(xiàn)
虛成員 |
抽象成員 |
|
關(guān)鍵字 |
virtual |
abstract |
實(shí)現(xiàn)體 |
有實(shí)現(xiàn)體 |
沒有實(shí)現(xiàn)體,被分號(hào)取代 |
在派生類中被覆寫 |
可重寫,也可不重寫,使用override |
必須被重寫,使用override |
⑩①、接口
接口提出了一種契約(或者說規(guī)范),讓使用接口的程序設(shè)計(jì)人員必須嚴(yán)格遵守接口提出的約定。接口就可以看做是這種標(biāo)準(zhǔn),它強(qiáng)制性地要求派生類必須實(shí)現(xiàn)接口約定的規(guī)范,以保證派生類必須擁有某些特性。
特點(diǎn):
- 不能實(shí)例化;
- 不能有構(gòu)造方法和字段;
- 不能有修飾符;
- 接口包含(事件、索引器、方法、屬性);
- 不包含方法的實(shí)現(xiàn);
- 實(shí)現(xiàn)接口的類必須實(shí)現(xiàn)接口中的所有方法和屬性
⑩②、六大原則:依賴倒轉(zhuǎn)原則
-高層模塊不應(yīng)該依賴底層模塊。兩個(gè)都應(yīng)該依賴抽象(接口/抽象類)。
-抽象不應(yīng)該依賴細(xì)節(jié)(具體類)。細(xì)節(jié)應(yīng)該依賴抽象。
我現(xiàn)在要設(shè)計(jì)一個(gè)汽車,我先設(shè)計(jì)汽車大概的樣子,先設(shè)計(jì)輪胎,根據(jù)輪胎在設(shè)計(jì)底盤,根據(jù)底盤在設(shè)計(jì)車身?,F(xiàn)在我想要修改輪胎的尺寸,是不是就需要修改底盤,修改車身,整個(gè)全部都得修改?如果我們倒過來看,在設(shè)計(jì)車身,根據(jù)車身在設(shè)計(jì)底盤,根據(jù)底盤在設(shè)計(jì)輪胎,這樣的話如果輪胎的尺寸變了,也不會(huì)影響到其他的部分
誰也不要依賴誰,除了約定的接口,大家都可以靈活自如。
針對(duì)書上所舉的例子:無論主板、CPU、內(nèi)存、硬盤都是在針對(duì)接口設(shè)計(jì)的。CPU作為主板上一個(gè)可移動(dòng)的、可擴(kuò)展的部分,在設(shè)計(jì)主板的時(shí)候只需要把接口定義好,內(nèi)部再復(fù)雜我也不讓外界知道,而主板只需要預(yù)留與CPU陣腳的插槽就可以了。內(nèi)存、硬盤、顯卡都是如此,哪部分壞了直接更換那部分就行了,而不會(huì)導(dǎo)致整個(gè)主板全部都要換。
?
⑩③、六大關(guān)系
- 依賴:使用關(guān)系,一個(gè)類的使用需要另一個(gè)類的協(xié)助
實(shí)現(xiàn):局部變量、構(gòu)造方法的參數(shù)、靜態(tài)方法的調(diào)用
圖形:虛線+箭頭,指向被擁有者
Animal {
Public Water Grownup(Water water)
{
return null;
}
}
- 關(guān)聯(lián):擁有關(guān)系,一個(gè)類需要使用另一個(gè)類的屬性和方法
實(shí)現(xiàn):成員變量
圖形:實(shí)線+箭頭
class Water {
public Climate m_Climate;
public Water(){}
}
class Climate {
public Climate() {}
}
- 聚合:整體和部分的關(guān)系,部分不能脫離整體而單獨(dú)存在
實(shí)現(xiàn):成員變量+構(gòu)造方法的參數(shù)賦值
圖形:實(shí)線+空心菱形,菱形指向整體
class GooseGroup {
public Goose goose;
Public GooseGroup(Goose goose)
{
this.goose = goose;
}
}
class Work
{
private State current;
public Work()
{
current = new ForenoonState();
}
}
- 繼承:子類繼承父類
實(shí)現(xiàn):子類:父類
圖形:實(shí)線+空心三角形,箭頭指向父類
class Cat:Animal
{
}
- 實(shí)現(xiàn):類和接口的關(guān)系,類實(shí)現(xiàn)接口的所有特征
實(shí)現(xiàn):類:接口
圖形:虛線+空心三角,箭頭指向接口文章來源:http://www.zghlxwxcb.cn/news/detail-680408.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-680408.html
Class WideGoose:Ifly
{
}
到了這里,關(guān)于設(shè)計(jì)模式—觀察者模式(Observer)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!