運用泛型委派進行基本的觀察者模式實作
觀察者模式(Observer pattern)又稱為發布/訂閱模式(Publish / Subscribe pattern),可以讓A、B物件間的出現相依性,並且可以當A出現動作時,B就被觸發連動。這篇文章將利用泛型委派(Action)來進行基本的觀察者模式實作。
首先我們先建立狗類別,類別中有一個事件是OnBarking,而當我們讓狗叫Barking();的時候,他就會觸發這個OnBarking事件。
class dog
{
public event Action OnBarking;
public void Barking()
{
OnBarking();
}
}
接著我們建立人類別,一開始的建構子我們就參考一隻狗實體,當狗叫事件出現時,就去觸發Scare();方法。
class human
{
public human(dog d)
{
d.OnBarking += Scare;
}
public void Scare()
{
Console.WriteLine("Scared!");
}
}
回到主控台,我們建立oldMan1、oldMan2實體,然後都參考到同一隻狗。當狗叫的時候,主控台上就可以看到這兩個老人都同時嚇一跳了!
static void Main(string[] args)
{
dog oldDog = new dog();
human oldMan1 = new human(oldDog);
human oldMan2 = new human(oldDog);
oldDog.Barking();
}
執行畫面如下圖:
取消事件的觀察(取消訂閱)
在真實的世界裡,一個人被狗嚇到一定是一開始的突然事件,也就是你不太可能當狗吠叫第二聲還會再嚇到一次,因此我們就用剛才的範例,接著來講事件的取消觀察、取消訂閱。
當一個人被同一隻實體的狗嚇到後,就不會在嚇到了,因此我們在嚇到的事件中,去取消訂閱。而因為我們要共用建構子中引入的狗實體,因此我們要把狗實體的指標拉升到全類別都可存取的範圍。
class human
{
dog _d;
public human(dog d)
{
_d = d;
_d.OnBarking += Scare;
}
public void Scare()
{
Console.WriteLine("Scared!");
_d.OnBarking -= Scare;
}
}
然而,每次當狗吠叫時,Barking();方法每次都會去呼叫一次OnBarking();,我們可以從一開始的程式碼觀察到,Action OnBarking一開始根本就是一個空的匿名方法,更正確地來說根本是null才對。而如果我們去將唯一的訂閱方法Scare();從OnBarking中拿掉,然後又去呼叫OnBarking();,這馬上就會產生NullException了,因此,我們最好做一下檢查是否為null的防錯機制。
class dog
{
public event Action OnBarking;
public void Barking()
{
if (OnBarking != null)
OnBarking();
}
}
接下來就是實驗時刻了,無論這隻狗吠多少次聲音,兩個老人永遠只會嚇一跳!
static void Main(string[] args)
{
dog oldDog = new dog();
human oldMan1 = new human(oldDog);
human oldMan2 = new human(oldDog);
oldDog.Barking(); //有用;會嚇一跳
oldDog.Barking(); //無用
oldDog.Barking(); //無用
oldDog.Barking(); //無用
oldDog.Barking(); //無用
}
補充一下!如果你真的懶得寫if (OnBarking != null),你也可以選擇建立一個空白的匿名函式來當底,只是我懶得去確定到底是每一次都去檢查是否null的效率比較高,還是每一次都去跑一個空的匿名函式效率比較高。實作上建議還是使用檢查null這個方式比較好,因為你永遠不知道引用你的類別元件的使用者,要怎麼惡搞你的元件?(例如連你預設的空白匿名方法,都把它移除光了!)
class dog
{
public event Action OnBarking = () => { };
public void Barking()
{
OnBarking();
}
}