unity 委託和事件

在面試中,委託與事件幾乎是必問的。如果面試官和麪試者互不相識,面試官不問委託,那我嚴重懷疑這家公司的技術水平,這個可以說是C#入門與掌握的分水嶺之一,非常重要。所以,這裏我整理一片筆記,對委託和事件做一個全面的整理和歸納。這裏重在理解,只有理解了才能真正掌握它。

目錄

  1. 爲什麼C#會有委託事件?
    2.觀察者模式的缺陷:
    3.爲什麼要費這麼大勁?
    4.委託?事件?封裝!
    5.是不是還少了點什麼?
  2. 爲什麼C#會有委託事件?
    解決觀察者模式的缺陷。這裏又引申出一個問題,什麼是觀察者模式,觀察者模式的缺陷又是什麼?在回答這個問題前,我們其實已經知道一個答案,那就是先有設計模式中的觀察者模式,再有委託事件這一技術。它有一個前後順序,如果你要徹底學會委託事件,那麼就要先學會觀察者模式,還要了解觀察者模式的缺陷。

2.觀察者模式的缺陷:
觀察者模式是對主題對象和觀察者對象進行解耦,使雙方都依賴與抽象,而不是依賴於對方的具體對象,使雙方的變化都不會影響到對方的具體對象。當多個對象需要根據一個對象的狀態發生相應的改變或操作時,可使用觀察者模式。

這裏我寫一個小demo,非常簡潔。還是貓和老鼠,貓是主題對象,人和老鼠是觀察者。貓叫的時候,人和老鼠要執行相應操作。

使用觀察者模式實現這一功能:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ObserverCatDemo : MonoBehaviour
{
    void Start()
    {
        CatSubject cat = new CatSubject();
        Mouse mouse = new Mouse();
        Person person = new Person();
        cat.Add(mouse);
        cat.Add(person);
        cat.Call();
    }
}
public abstract class Subjectss
{
    public abstract void Add(Observerss observer);
    public abstract void Remove(Observerss observer);
    public abstract void Call();

}
public abstract class Observerss
{
    public abstract void Update();
}
public class CatSubject : Subjectss
{
    public IList<Observerss> Observers = new List<Observerss>();
    public override void Add(Observerss observer)
    {
        Observers.Add(observer);
    }
    public override void Remove(Observerss observer)
    {
        Observers.Remove(observer);
    }
    public override void Call()
    {
        foreach (Observerss observer in Observers)
        {
            observer.Update();
        }
    }
}
public class Mouse : Observerss
{
    public override void Update()
    {
        Debug.Log("貓叫了,我得跑了");
    }
}
public class Person : Observerss
{
    public override void Update()
    {
        Debug.Log("貓叫了,應該是餓了");
    }
}

然後,我們使用委託來實現這一功能

using System;
using System.Collections.Generic;
using UnityEngine;

public class DelegateCat:MonoBehaviour
{
    private void Start()
    {
        DeCat deCat = new DeCat();
        DePerson person = new DePerson();
        DeMouse mouse = new DeMouse();
        deCat.call += person.Notify;
        deCat.call += mouse.Run;
        deCat.call();
    }
}
public delegate void CallEvent();//聲明委託類型
public class DeCat
{
    public CallEvent call;

}
public class DePerson
{
    public void Notify()
    {
        Debug.Log("貓叫了,應該是餓了");
    }
}
public class DeMouse
{
    public void Run()
    {
        Debug.Log("貓叫了,我得跑了!");
    }
}

我們做一下對比:
1.發現實現同樣的功能,使用委託非常的簡潔,並且主題貓的類裏沒有任何觀察者類成員,兩者是完全獨立的。而觀察者模式雙方並沒有完全的獨立,抽象主題通知時依然依賴於抽象觀察者。

2.觀察者不需要繼承觀察者接口,它的通知方法名可以不同,比如說人的執行方法是Notify(),貓的執行方法是Run(),現實中本就是這樣的,方法名本就不一定相同。

也就是說觀察者模式的缺陷爲:

1.抽象主題依舊依賴於抽象觀察者。

2.具體的觀察者,通知方法被固定了。

觀察者模式存在的意義就是解耦,它使觀察者和被觀察者的邏輯不再攪在一起。而是彼此獨立,互不依賴。而使用委託則能使得觀察者和主題完全解耦,甚至不需要知道對方的存在。

3.爲什麼要費這麼大勁?
設計的核心思想:儘可能減少耦合,如果發現代碼耦合,就要採取解耦技術,讓數據模型,業務邏輯和視圖顯示三層之間彼此降低耦合,把關聯依賴降到最低,而不至於牽一髮而動全身。原則就是A功能的代碼不要寫在B功能代碼中,如果兩者之間需要交互,可以通過接口,通過消息,甚至是引入框架,但總之就是不要直接交叉寫。

在軟件工程中,模塊的內聚和耦合是度量模塊化質量的之一。

高內聚:是指模塊是由相關性很強的代碼組成,只負責一項任務,也就是單一職責原則,這樣的模塊,無論是從設計,實現,還是閱讀,都能體現出保持專一性帶來的好處。

低耦合:是指模塊之間儘可能的使其獨立存在,模塊之間不產生聯繫不可能,但模塊與模塊之間的接口應該儘量少而簡單。

耦合:在軟件工程中,對象之間的耦合度就是對象之間的依賴性。對象之間的耦合越高,維護成本越高,因此對象的設計應該使類和構建之間的耦合最小。

解耦:字面意思,就是解除耦合關係,在軟件工程中,降低耦合度即可以理解爲解耦,模塊間有依賴關係,必然存在耦合,理論上的絕對零耦合是做不到,但可以通過一些現有的方法將耦合度降至最低。

4.委託?事件?封裝!
這裏我們只要對委託的例子做一個小小的變動

public class DeCat
{
    public event CallEvent call;

}

沒錯我們給聲明前面加個event就可以了,但是你突然發現,編譯不過去,有報錯。這裏也是事件與委託核心的區別。
事件是類或對象向其他類或對象通知發生的一種特殊簽名的委託。它的本意是對委託的封裝,它在外部只能被訂閱或取消訂閱,但是不能發佈。一句話總結:事件只能在外部被訂閱,但是不能在外部被觸發。只能通過內部的公開方法,在方法內部觸發事件,這樣可以使得程序更加的安全。

這裏我通過的例子來說明:當使用委託變量時,客戶端可以直接通過委託變量觸發事件,也就是直接調用
deCat.call();這將會影響到所有註冊了該委託的變量。而事件的本意應該爲在事件發佈者在其本身的某個行爲中觸發,不如說在方法CallMethod()中滿足某個條件後觸發。通過添加event關鍵字來發布事件,事件發佈者的封裝性會更好,事件僅僅是供其他類型訂閱,而客戶端不能直接觸發事件(語句deCat.call()無法通過編譯),事件只能通過事件發佈者DeCat類的內部觸發(比如在方法deCat.CallMethod()中),換言之,就是call()語句只能在deCat內部被調用。這樣纔是事件的本意,事件發佈者的封裝纔會更號。
以下是完整代碼

using System;
using System.Collections.Generic;
using UnityEngine;

public class DelegateCat:MonoBehaviour
{
    private void Start()
    {
        DeCat deCat = new DeCat();
        DePerson person = new DePerson();
        DeMouse mouse = new DeMouse();
        deCat.call += person.Notify;
        deCat.call += mouse.Run;
        //deCat.call();//編譯無法通過
        deCat.CallMethod();
    }
}
public delegate void CallEvent();//聲明委託類型
public class DeCat
{
    public event CallEvent call;
    public void CallMethod()
    {
        call();
    }

}
public class DePerson
{
    public void Notify()
    {
        Debug.Log("貓叫了,應該是餓了");
    }
}
public class DeMouse
{
    public void Run()
    {
        Debug.Log("貓叫了,我得跑了!");
    }
}

轉載自:https://blog.csdn.net/q493201681/article/details/82352616

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章