在这一节,我们将通过接口阉割和拓展方法来给架构添加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>();//直接使用this访问
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();
}
}

修改AddCountCommandSubCountCommandKillEnemyCommand方法调用的部分为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;
}
}
}

这里仅作演示,上面的代码并没有保留

使用这一套规则限制,能够规范化架构的使用,也可以通过接口将一个模块能够做的东西一目了然,减少一部分说明文档的维护。