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

木宛城主發表於2019-01-22

『可複用』這個詞相信大家都熟悉,通過『可複用』的元件,可以大大提高軟體開發效率。 值得注意的事,當我們設計一個可複用的物件導向元件時,需要保證其獨立性,也就是我們熟知的『高內聚,低耦合』原則。

元件化設計的思路

不管是開發客戶端應用程式還是開發伺服器端應用程式,『元件』這個詞我們並不陌生。不管是在iOS中的xib,還是在AngularJS的Component,或者後端開發的 User Control,可複用的元件是物件導向開發的基礎。所以在Unity 3D 框架設計時,元件化是核心的概念。那麼如何去設計SubView和SubViewModel,我總結出幾條原則:

  • 當一個功能被不同的場合頻繁用到,建議將這個功能抽象成SubView(SubViewModel)
  • SubView(SubViewModel)應該保持高內聚,低耦合原則
  • SubViewModel不應該處理具體的業務邏輯,它很單純,可通過委託Delegate的方式交由外部處理

構建SubView和SubViewModel

假設現在有如下一個需求,需要繫結角色的資訊到頭像上,如下圖所示:

這是一個很常見的需求,建立一個MonoBehaviour,定義Public的變數並引用這些控制元件,最後再將這個MonoBehaviour附加到GameObject上,很快就能完成。當然,我不能說這樣的實施是錯誤的,畢竟我們只要保證執行正確就可以了。

看到左上角的勳章嗎,這個勳章會在不同的場景出現,我們優先把它考慮成一個SubView(BadgeView),也就是最外層的FaceBoxView裡巢狀了一個BadgeView

public class FaceBoxView:UnityGuiView<FaceBoxViewModel>
{
    public Text nameText;
    public Text levelText;
    public Image faceImage;
    public BadgeView badgeView;
}複製程式碼

我們在分析一下BadgeView需要什麼資料?它需要武器的Icon和屬性顏色,所以我們抽象出一個Badge的DataModel:

public class Badge
{
    public string Icon { get; set; }
    public string ElementColor { get; set; }
}複製程式碼

所以對於FaceBoxViewModel而言,它為FaceBoxView服務。FaceBoxView需要什麼資料,它就提供什麼資料。顯然它需要提供Name,Level,Face以及Badge元件的DataModel:

public class FaceBoxViewModel:ViewModelBase
{
    public readonly BindableProperty<string> Name=new BindableProperty<string>();
    public readonly BindableProperty<int> Level=new BindableProperty<int>();
    public readonly BindableProperty<string> Face=new BindableProperty<string>();
    public readonly BindableProperty<Badge> Badge=new BindableProperty<Badge>();

    public override void OnStartReveal()
    {
        base.OnStartReveal();
        Initialization();
    }

    public void Initialization()
    {
        Name.Value = "比爾";
        Level.Value = 9;
        Face.Value = "Avatar204_Face";
        Badge.Value = new Badge() {Icon = "Icon_WeaponRod", ElementColor = "1CB9FFFF"};
    }
}複製程式碼

因為Badge是BindableProperty型別物件,特點是當Badge Value改變時,觸發的OnValueChanged事件就可以給BadgeViewModel傳遞資料,從而初始化BadgeView:

    protected override void OnInitialize()
    {
        base.OnInitialize();
        Binder.Add<string>("Name",OnNamePropertyVlaueChanged);
        Binder.Add<int>("Level",OnLevelPropertyValueChanged);
        Binder.Add<string>("Face",OnFacePropertyValueChanged);
        Binder.Add<Badge>("Badge",OnBadgePropertyValueChanged);
    }

    private void OnBadgePropertyValueChanged(Badge oldValue, Badge newValue)
    {
        badgeView.BindingContext = new BadgeViewModel() ;
        badgeView.BindingContext.Initialization(newValue);
    }複製程式碼

我們可以看到,元件化的實施從程式碼量上是變得複雜了,元件的顆粒度越細,那麼巢狀的層次就越深,如果某個功能只出現一次,並且不會被複用,那麼我不推薦將它變為一個SubView(SubViewModel)

小結

本文為大家介紹怎樣將元件化模式思想引入到Unity 3D中,在我的uMVVM框架中,元件化是核心,就像使用者控制元件一樣,隨拿隨走,它們保持高度獨立,這樣的好處是不會產生緊耦合。還值得一提的是,其實Unity 3D本身的開發模式就是基於元件化開發的。只要建立一個MonoBehaviour元件然後附加到GameObject上就能正常執行。但需要注意的事,如果沒有好的約束,一個GameObject上就會附加好多個MonoBehaviour,GameObject的子GameObject也會附加很多個MonoBehaviour,久而久之,整個層級結構會變得異常複雜和難以維護。uMVVM的理念是隻需要一個View,View是唯一的入口,並且View可以是非常複雜,裡面維護了所有的SubView,所以換UI也好,換功能也罷,只要關注於對應的View即可。
原始碼託管在Github上,點選此瞭解

歡迎關注我的公眾號:

相關文章