[C#] 用 interface 模擬多類別繼承
最近負責的 Unity 專案中,有很多 UI 有返回的按鈕,而這些按鈕也要可以透過程式觸發。如果為每個 UI 撰寫觸發的函式,一但觸發的邏輯改變,修改會是個大工程。如果為了這個小功能撰寫基礎類別,又顯得大才小用,而且 C# 的類別只能同時繼承一個類別,擴充性也不高。但 C# 類別可以繼承多個 interface,所以就思考能不能讓類別繼承 interface 的同時,也能提供功能,就好像是繼承了一個類別。
實作
作法是利用 interface 指定對象成員,再配合一個 static 類別來擴充 interface 的函式實作,來讓 interface 看起來也能提供功能。
public interface IPressBack
{
public UnityEvent onBack { get; }
}
public static class PressBackCaller
{
public static void PressBack(this IPressBack i)
{
i.onBack.Invoke();
}
}
主要是在 static 類別上的函式參數用了 this
,讓 PressBackCaller.PressBack(IPressBack)
函式能以 IPressBack.PressBack()
的形式呼叫。如此一來,繼承 interface IPressBack
的類別,就會因為 static 類別 PressBackCaller
而多了 PressBack()
的函式。這種作法稱為「擴充方法(Extension Method)」。
使用的感覺如下:
public class SomeUI : MonoBehaviour, IPressBack
{
[SerializeField]
private Button _backButton;
public UnityEvent onBack => _backButton.onClick;
}
public class OtherComponent : MonoBehaviour
{
[SerializeField]
private SomeUI _someUI;
private void SomeOperation()
{
_someUI.PressBack();
}
}
優缺點
優點
擴充性高,能針對不同的用途提供對應的功能,而且一個類別能夠繼承多個 interface。彈性也高,如果這個 UI 不需要透過程式來觸發事件的話,就不繼承 IPressBack
就好了,就像是在「開關」這個功能。
缺點
因為是繼承自 interface,所以對象成員跟能提供的功能都必需是 public 的。所以在上面的例子裡我只開放按鈕的事件,而不是按鈕本身,除了可以保護按鈕不被修改,而且外部也只需要註冊事件的 callback 就夠了。