上一节我们实现了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层一样,有获取其他层的权力,所以我们也需要让它继承IBelongToArchitectureICanSetArchitecture

修改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//抽象类只能用class实现
{
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中实现一下对象池。