在这一节,我们将通过接口阉割和拓展方法来给架构添加Rule(规则),防止各个层级都能随便互相调用
目前,Model层可以发送Command,因为调用GetArchitecture()
之后便能够访问很多我们在IArchitecture
接口中定义的方法,那么在Model层必然可以调用SendCommand
方法。
阉割AbstractModel Command层是表现层与底层系统层交互的方式,而在Model层使用Command是不合理的。在技术上讲,Model层是可以发送Command的,所以在实际开发中,尤其的多人开发,可能会有人写出不符合规范的代码,所以我们要限制Model层,让Model层不能发送Command。
首先,我们先使用接口阉割中的“接口——抽象类——实现类”的方式,把GetArchitecture
方法和SetArchitecture
方法从AbstractModel
里面阉割掉
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 namespace FrameWorkDesign { public interface IModel : IBelongToArchitecture , ICanSetArchitecture { void Init () ; } public abstract class AbstractModel : IModel { private IArchitecture m_Architecture; IArchitecture IBelongToArchitecture.GetArchitecture() { return m_Architecture; } void ICanSetArchitecture.SetArchitecture(IArchitecture architecture) { m_Architecture = architecture; } void IModel.Init() { OnInit(); } protected abstract void OnInit () ; } }
添加ICanGetUtility 接下来我们使用“接口——静态拓展”来拓展IModel
。首先在FrameworkDesign——Framework——Architecture里面新建文件夹Rule,然后在里面新建脚本ICanGetUtility
,这个接口就是为了在最后实现的类里边能使用下面的拓展方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 namespace FrameWorkDesign { public interface ICanGetUtility : IBelongToArchitecture { } public static class CanGetUtilityExtension { public static T GetUtility <T >(this ICanGetUtility self ) where T : class , IUtility { return self.GetArchitecture().GetUtility<T>(); } } }
让IModel继承ICanGetUtility接口 这里IBelongToArchitecture和ICanGetUtility重复继承了,不过重复继承接口这种事情不是什么问题
1 2 3 4 5 6 7 8 namespace FrameWorkDesign { public interface IModel : IBelongToArchitecture , ICanSetArchitecture ,ICanGetUtility { void Init () ; } }
修改CounterModel
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class CounterModel : AbstractModel ,ICounterModel { protected override void OnInit () { var storage = this .GetUtility<IStorage>(); Count.Value = storage.LoadInt("COUNTER_COUNT" , 0 ); Count.OnValueChanged += count => { storage.SaveInt("COUNTER_COUNT" , count); }; } public BindableProperty<int > Count { get ; } = new BindableProperty<int >() { Value = 0 }; }
使用this比使用GetArchitecture()
更简单清晰,直观方便
添加ICanGetModel 还是在Rule文件夹,添加ICanGetModel
脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 namespace FrameWorkDesign { public interface ICanGetModel : IBelongToArchitecture { } public static class CanGetModelExtension { public static T GetModel <T >(this ICanGetModel self ) where T : class , IModel { return self.GetArchitecture().GetModel<T>(); } } }
让ISystem继承两个接口并阉割其抽象类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 namespace FrameWorkDesign { public interface ISystem : IBelongToArchitecture , ICanSetArchitecture , ICanGetModel , ICanGetUtility { void Init () ; } public abstract class AbstractSystem : ISystem { private IArchitecture m_Architecture; IArchitecture IBelongToArchitecture.GetArchitecture() { return m_Architecture; } void ICanSetArchitecture.SetArchitecture(IArchitecture architecture) { m_Architecture = architecture; } void ISystem.Init() { OnInit(); } protected abstract void OnInit () ; } }
修改AchievementSystem
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 using UnityEngine;using FrameWorkDesign;namespace CounterApp { public interface IAchievementSystem : ISystem { } public class AchievementSystem : AbstractSystem , IAchievementSystem { protected override void OnInit () { var counterModel = this .GetModel<ICounterModel>(); } } }
添加ICanGetSystem 我们还没有在Architecture
中声明GetSystem的方法,我们先添加上
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 using System;using System.Collections.Generic;namespace FrameWorkDesign { public interface IArchitecture { T GetSystem <T >() where T : class , ISystem ; } public abstract class Architecture <T > : IArchitecture where T : Architecture <T >,new () { public T GetSystem <T >() where T : class , ISystem { return m_Container.Get<T>(); } } }
还是在Rule文件夹,添加ICanGetSystem
脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 namespace FrameWorkDesign { public interface ICanGetSystem : IBelongToArchitecture { } public static class CanGetSystemExtension { public static T GetSystem <T >(this ICanGetSystem self ) where T : class , ISystem { return self.GetArchitecture().GetSystem<T>(); } } }
ISystem也继承ICanGetSystem 有的时候System也是可以访问System的,我们给ISystem
也添加此接口
1 2 3 4 5 namespace FrameWorkDesign { public interface ISystem : IBelongToArchitecture , ICanSetArchitecture , ICanGetModel , ICanGetUtility , ICanGetSystem }
让ICommand继承三个接口并阉割其抽象类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 namespace FrameWorkDesign { public interface ICommand : IBelongToArchitecture , ICanSetArchitecture , ICanGetUtility , ICanGetModel , ICanGetSystem { void Execute () ; } public abstract class AbstractCommand : ICommand { private IArchitecture m_Architecture; IArchitecture IBelongToArchitecture.GetArchitecture() { return m_Architecture; } void ICanSetArchitecture.SetArchitecture(IArchitecture architecture) { m_Architecture = architecture; } void ICommand.Execute() { OnExecute(); } protected abstract void OnExecute () ; } }
修改AddCountCommand
、SubCountCommand
、KillEnemyCommand
方法调用的部分为this
即可,这里就不贴上代码了
添加ICanSendCommand 还是在Rule文件夹,添加ICanSendCommand
脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 namespace FrameWorkDesign { public interface ICanSendCommand : IBelongToArchitecture { } public static class CanSendCommandExtension { public static void SendCommand <T >(this ICanSendCommand self ) where T : ICommand, new () { self.GetArchitecture().SendCommand<T>(); } public static void SendCommand <T >(this ICanSendCommand self,T command ) where T : ICommand { self.GetArchitecture().SendCommand<T>(command); } } }
让ICommand再继承ICanSendCommand接口
1 2 3 4 5 namespace FrameWorkDesign { public interface ICommand : IBelongToArchitecture , ICanSetArchitecture , ICanGetUtility , ICanGetModel , ICanGetSystem , ICanSendCommand }
让IController继承三个接口 Controller属于表现层,能够SendCommand,能够获取System和Model
1 2 3 4 5 6 namespace FrameWorkDesign { public interface IController : IBelongToArchitecture , ICanSendCommand , ICanGetSystem , ICanGetModel { } }
由于表现层的实现是不一样的,有可能是基于MonoBehaviour的,有可能是基于UnityEditor的,所以Controller并没有抽象基类,它的接口阉割需要在具体实现的地方写。
修改CounterViewController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 using UnityEngine.UI;using FrameWorkDesign;namespace CounterApp { public class CounterViewController : MonoBehaviour , IController { private ICounterModel m_CounterModel; IArchitecture IBelongToArchitecture.GetArchitecture() { return CounterApp.Interface; } private void Start () { m_CounterModel = this .GetModel<ICounterModel>(); m_CounterModel.Count.OnValueChanged += UpdateView; UpdateView(m_CounterModel.Count.Value); transform.Find("Button_add" ).GetComponent<Button>().onClick.AddListener(() => { this .SendCommand<AddCountCommand>(); }); transform.Find("Button_sub" ).GetComponent<Button>().onClick.AddListener(() => { this .SendCommand<SubCountCommand>(); }); } } }
修改Enemy
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 using UnityEngine;namespace FrameWorkDesign.Example { public class Enemy : MonoBehaviour ,IController { IArchitecture IBelongToArchitecture.GetArchitecture() { return PointGame.Interface; } private void OnMouseDown () { this .SendCommand<KillEnemyCommand>(); Destroy(gameObject); } } }
修改GameStartPanel
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 using UnityEngine;using UnityEngine.UI;namespace FrameWorkDesign.Example { public class GameStartPanel : MonoBehaviour ,IController { IArchitecture IBelongToArchitecture.GetArchitecture() { return PointGame.Interface; } void Start () { transform.Find("Button_Start" ).GetComponent<Button>() .onClick.AddListener(() => { gameObject.SetActive(false ); this .SendCommand<StartGameCommand>(); }); } } }
修改EditorCounterApp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 using UnityEngine;using UnityEditor;using FrameWorkDesign;namespace CounterApp.Editor { public class EditorCounterApp : EditorWindow ,IController { IArchitecture IBelongToArchitecture.GetArchitecture() { return CounterApp.Interface; } private void OnGUI () { if (GUILayout.Button("+" )) this .SendCommand<AddCountCommand>(); GUILayout.Label(this .GetModel<ICounterModel>().Count.Value.ToString()); if (GUILayout.Button("-" )) this .SendCommand<SubCountCommand>(); } } }
举例,实现IController的抽象基类 如果我们在设计中有大量的表现层代码,那么我们可以实现抽象基类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 using FrameWorkDesign.Example;using UnityEngine;namespace FrameWorkDesign { public interface IController : IBelongToArchitecture , ICanSendCommand , ICanGetSystem , ICanGetModel { } public abstract class AbstractPointGameController : MonoBehaviour ,IController { IArchitecture IBelongToArchitecture.GetArchitecture() { return PointGame.Interface; } } }
这里仅作演示,上面的代码并没有保留
使用这一套规则限制,能够规范化架构的使用,也可以通过接口将一个模块能够做的东西一目了然,减少一部分说明文档的维护。