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上,点击此了解

欢迎关注个人公众号:

相关文章
相关标签/搜索