上一节我们实现了IController接口,并且拆分了IBelongToArchitecture引入ICanSetArchitecture从而重构了IModel和ISystem接口。
在这一堂课,我们接着完成剩下的接口,IUtility和ICommand
实现IUtility 首先实现IUtility,Utility层和Model、System不一样,它作为一个工具层,我们使用时只能获取实例并使用它,而前面的两个需要有自己的初始化方法,需要能获取到其他层,而Utility不用获取其他层,所以不用继承IBelongToArchitecture和ICanSetArchitecture接口。
在FrameworkDesign——Framework——Architecture文件夹中新建IUtility
脚本
1 2 3 4 5 6 7 namespace FrameWorkDesign { public interface IUtility { } }
所以目前我们的IUtility脚本仅仅是为了提供一层抽象,我们修改Architecture
脚本中声明和使用了Utility的部分
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 using System;using System.Collections.Generic;namespace FrameWorkDesign { public interface IArchitecture { T GetUtility <T >() where T : class , IUtility ; void RegisterUtility <T >(T isntance ) where T : IUtility ; } public abstract class Architecture <T > : IArchitecture where T : Architecture <T >,new () { public void RegisterUtility <T >(T utility ) where T : IUtility { m_Container.Register<T>(utility); } public T GetUtility <T >() where T : class , IUtility { return m_Container.Get<T>(); } } }
然后修改目前我们使用过的一个Utility,就是IStorage
,让这个接口继承IUtility
即可
1 2 3 4 5 6 7 8 9 10 using FrameWorkDesign;namespace CounterApp { public interface IStorage : IUtility { } }
完善ICommand接口 当前我们调用Command使用的是类似CounterApp.Get<ICounterModel>().Count.Value++;
的写法,从我们引入IModel开始,架构在演化中就不再使用CounterApp.Get
这种单例结构,因为这个方法总是调用MakeSureArchitecture
Command和Model层以及System层一样,有获取其他层的权力,所以我们也需要让它继承IBelongToArchitecture
和ICanSetArchitecture
修改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 { void Execute () ; } public abstract class AbstractCommand : ICommand { private IArchitecture m_Architecture; public IArchitecture GetArchitecture () { return m_Architecture; } public void SetArchitecture (IArchitecture architecture ) { m_Architecture = architecture; } void ICommand.Execute() { OnExecute(); } protected abstract void OnExecute () ; } }
仅仅实现这两个接口是不行的,因为我们的Architecture
脚本中还没有调用ICommand的SetArchitecture
我们先在Architecture
脚本中声明SendCommand
方法并实现
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 using System;using System.Collections.Generic;namespace FrameWorkDesign { public interface IArchitecture { void SendCommand <T >() where T : ICommand, new () ; void SendCommand <T >(T command ) where T : ICommand ; } public abstract class Architecture <T > : IArchitecture where T : Architecture <T >,new () { public void SendCommand <T >() where T : ICommand, new () { var command = new T(); command.SetArchitecture(this ); command.Execute(); } public void SendCommand <T >(T command ) where T : ICommand { command.SetArchitecture(this ); command.Execute(); } } }
可以看到Command的调用和System、Model不太一样,Command属于表现层发送过来的东西,所以它不像前两者在注册的时候完成初始化并获得Architecture引用,它在需要执行的时候创建实例并且获得Architecture引用
修改Command的具体实现 先修改AddCountCommand
1 2 3 4 5 6 7 8 9 10 11 12 using FrameWorkDesign;namespace CounterApp { public class AddCountCommand : AbstractCommand { protected override void OnExecute () { GetArchitecture().GetModel<ICounterModel>().Count.Value++; } } }
同样修改SubCountCommand
1 2 3 4 5 6 7 8 9 10 11 12 using FrameWorkDesign;namespace CounterApp { public class SubCountCommand : AbstractCommand { protected override void OnExecute () { GetArchitecture().GetModel<ICounterModel>().Count.Value--; } } }
修改《点点点》中使用的KillEnemyCommand
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 namespace FrameWorkDesign.Example { public class KillEnemyCommand : AbstractCommand { protected override void OnExecute () { var gameModel = GetArchitecture().GetModel<IGameModel>(); gameModel.KillCount.Value++; if (gameModel.KillCount.Value == 9 ) { GameEndEvent.Trigger(); } } } }
修改《点点点》中的GameModel
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 namespace FrameWorkDesign.Example { public interface IGameModel : IModel { BindableProperty<int > KillCount { get ; } BindableProperty<int > Gold { get ; } BindableProperty<int > Score { get ; } BindableProperty<int > BestScore { get ; } } public class GameModel : AbstractModel , IGameModel { public BindableProperty<int > KillCount { get ; } = new BindableProperty<int >() { Value = 0 }; public BindableProperty<int > Gold { get ; } = new BindableProperty<int >() { Value = 0 }; public BindableProperty<int > Score { get ; } = new BindableProperty<int >() { Value = 0 }; public BindableProperty<int > BestScore { get ; } = new BindableProperty<int >() { Value = 0 }; protected override void OnInit () { } } }
修改StartGameCommand
1 2 3 4 5 6 7 8 9 10 namespace FrameWorkDesign.Example { public class StartGameCommand : AbstractCommand { protected override void OnExecute () { GameStartEvent.Trigger(); } } }
修改Command的调用方 修改EditorCounterApp
,它本身属于Controller,所以要继承IController
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 using UnityEngine;using UnityEditor;using FrameWorkDesign;namespace CounterApp.Editor { public class EditorCounterApp : EditorWindow ,IController { public IArchitecture GetArchitecture () { return CounterApp.Interface; } private void OnGUI () { if (GUILayout.Button("+" )) GetArchitecture().SendCommand<AddCountCommand>(); GUILayout.Label(GetArchitecture().GetModel<ICounterModel>().Count.Value.ToString()); if (GUILayout.Button("-" )) GetArchitecture().SendCommand<SubCountCommand>(); } } }
修改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 using UnityEngine;using UnityEngine.UI;using System;using FrameWorkDesign;namespace CounterApp { public class CounterViewController : MonoBehaviour , IController { private void Start () { transform.Find("Button_add" ).GetComponent<Button>().onClick.AddListener(() => { GetArchitecture().SendCommand<AddCountCommand>(); }); transform.Find("Button_sub" ).GetComponent<Button>().onClick.AddListener(() => { GetArchitecture().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 { public IArchitecture GetArchitecture () { return PointGame.Interface; } private void OnMouseDown () { GetArchitecture().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 { public IArchitecture GetArchitecture () { return PointGame.Interface; } void Start () { transform.Find("Button_Start" ).GetComponent<Button>() .onClick.AddListener(() => { gameObject.SetActive(false ); GetArchitecture().SendCommand<StartGameCommand>(); }); } } }
总结 现在我们的Command使用的是AbstractCommand,必须用class来实现,比struct多一些消耗,如果这些消耗实在影响了性能,可以考虑在AbstractCommand中实现一下对象池。