Unity應用架構設計(4)——設計可複用的SubView和SubViewModel(Part 2)

在咱們設計和開發應用程序時,常常要用到控件。好比開發一個客戶端WinForm應用程序時,微軟就爲咱們提供了若干控件,這些控件爲咱們提供了可被定製的屬性和事件。屬性能夠更改它的外觀,好比背景色,標題等,而事件能夠豐富控件的行爲,好比最多見的『按鈕點擊』,誰也不能肯定點擊以後將發生什麼事,是鏈接數據庫呢仍是彈出警告框,在不一樣的場景下,『按鈕點擊』 的行爲每每呈現不一致。因此,與其猶豫不定,還不如把處理委託給開發者,這就是『OnClick』事件。git

SubView行爲多變性

在上篇文章中,我闡述了爲何要使用SubView,總結起來就3個字:『可複用』 。那麼問題來了,既然是可複用,那就意味着SubView能夠在任何場景下使用,那怎樣才能確保它作的是正確的行爲呢?github

舉個栗子,仍是 以以下圖FaceBox爲例,不一樣的場景下點擊頭像應該處理不一樣的事:數據庫

  • 在戰團中點擊頭像,則顯示該成員的具體信息
  • 在隊伍裏點擊頭像,則進入換人界面
  • 在戰鬥時點擊頭像,則顯示它配置的戰術

你看,一樣一個SubView,在不一樣的場景下它的行爲每每是不一致的。那咱們怎麼去跟蹤這些行爲呢?設計模式

定製SubView的行爲

你可能會以以下方式去定製SubView的行爲:ide

void OnClick(){
    if(戰團){
        顯示該成員的具體信息
    }else if(隊伍){
        進入換人界面
    }else if(戰鬥){
        顯示它配置的戰術
    }else{
        //其餘
    }
}複製代碼

仍是那句話這樣,這樣並無錯,甚至對某些SubView而言邏輯還很清晰。但仔細想一想,這是最好的實踐嗎?spa

  • 若是我要繼續添加一種狀況,是否是隻能在else if擴展,違反了開閉原則,應該對擴展是開放的,對修改是關閉的
  • 既然這個SubView是可複用的,那意味着將它放在任何項目中都是沒問題的,但實際上OnClick裏面處理了業務邏輯,緊耦合當前遊戲的業務

因此顯然上述代碼不是最佳實踐。那咱們應該怎樣去解決呢?設計

實際從開頭的引言我已經提出瞭解決方案,以事件的形式委託給開發者來肯定。一個Button也好,仍是一個SubView也好,他們都是可複用的組件,不該該與具體的業務邏輯相結合。經過事件或者委託的形式,暴露給開發者來決定究竟要處理什麼邏輯,這樣才能和具體業務邏輯解耦。代理

委託的介入

仍是以FaceBox舉例,那麼從上面的分析得出結論,咱們須要定義委託或者事件,那應該定義在FaceBoxView呢仍是FaceBoxViewModel中呢?code

仍是那句話,View不處理具體的業務邏輯,View將請求交給ViewModel去處理。orm

故在FaceBoxViewModel中增長可被外界監聽的委託或者事件,我以委託舉例,實際上事件就是特殊的委託。

public class FaceBoxViewModel:ViewModelBase
{
    //省略部分代碼
    public delegate void OnBeginDragHandler();
    public OnBeginDragHandler OnBeginDrag;
    public delegate void OnDragHandler();
    public OnDragHandler OnDrag;
    public delegate void OnEndDragHandler();
    public OnEndDragHandler OnEndDrag;
    public delegate void OnClickHandler();
    public OnClickHandler OnClick;

    //省略部分代碼
}複製代碼

FaceBoxView不處理具體的邏輯,交由FaceBoxViewModel去實現:

protected override void OnInitialize() {
    //省略部分代碼

    //監聽事件
    var beginDragEntry = new EventTrigger.Entry();
    beginDragEntry.eventID = EventTriggerType.BeginDrag;
    beginDragEntry.callback.AddListener(eventData => { OnBeginDrag(); });
    eventTrigger.triggers.Add(beginDragEntry);

    var dragEntry = new EventTrigger.Entry();
    dragEntry.eventID = EventTriggerType.Drag;
    dragEntry.callback.AddListener(eventData => { OnDrag(); });
    eventTrigger.triggers.Add(dragEntry);

    var endDragEntry = new EventTrigger.Entry();
    endDragEntry.eventID = EventTriggerType.EndDrag;
    endDragEntry.callback.AddListener(eventData => { OnEndDrag(); });
    eventTrigger.triggers.Add(endDragEntry);

    var pointClickEntry = new EventTrigger.Entry();
    pointClickEntry.eventID = EventTriggerType.PointerClick;
    pointClickEntry.callback.AddListener(eventData => { OnClick(); });
    eventTrigger.triggers.Add(pointClickEntry);
}

private void OnClick() {
    if (BindingContext.OnClick != null)
    {
        BindingContext.OnClick();
    }
}複製代碼

腦海裏梳理一下請求的流程:FaceBoxView.PointClick->FaceBoxViewModel.OnClick()->委託給外部的某個Handler。

小結

實際上『委託』這個概念很是重要,和具體的語言、平臺無關。好比在iOS開發常常聽到代理模式,顧名思義,將請求交給具體的處理者去處理。設計模式並不深奧,不少模式的理念都是相通的,不一樣的是對應語言下不一樣的表現形態,善於剖開現象看本質,不少都是相通的。
源代碼託管在Github上,點擊此瞭解

歡迎關注個人公衆號:

相關文章
相關標籤/搜索