由浅入深,慢慢演化实现框架
两个类的实现代码完全一样,就只有类名或类型不一样的时候,而且还需要不断扩展(未来会增加各种事件)的时候,这时候就用 泛型 + 继承 来提取,继承解决扩展的问题,泛型解决实现代码一致,类不一致的问题,这是一个重构技巧。
数据在大多数情况下需要在多个场景、界面、游戏物体之间是共享的,这些数据不但需要在空间上共享,还需要再时间上也需要共享(需要存储起来),所以在这里,开发者的共识就是把数据的部分会抽离出来,单独放在一个地方进行维护,而比较常见的开发架构就是使用 MVC 的开发架构,我们先不用 MVC 的开发架构,而只用 MVC 中的其中一个概念,就是 Model。
Model 就是管理数据、存储数据,管理数据就是可以通过 Model 对象或类可以对数据进行增删改查,有的时候还可以进行存储。
public class GameModel
{
public static int KillCount = 0;
public static int Gold = 0;
public static int Score = 0;
public static int BestScore = 0;
}
如果是共享的数据就放在 Model 里,如果不是共享的就不需要。
这里共享的数据可以是配置数据、需要存储的数据、多个地方需要访问的数据。
对于配置数据来说,游戏中的场景和 GameObject、Prefab 在运行游戏之前也是一种配置数据,因为他们本质上是用 Yaml(类似 json、xml 的数据格式)存储在磁盘上的。
而需要存储的数据是从时间这个维度共享的数据,即现在可以访问以前某个时刻存储的数据。
using System;
namespace FrameworkDesign
{
public class BindableProperty where T : IEquatable
{
private T mValue;
public T Value
{
get => mValue;
set
{
if (!mValue.Equals(value))
{
mValue = value;
OnValueChanged?.Invoke(value);
}
}
}
public Action OnValueChanged;
}
}
BidableProperty 是 数据 + 数据变更事件 的合体,它既存储了数据充当 C# 中的 属性这样的角色,也可以让别的地方监听它的数据变更事件,这样会减少大量的样板代码
用一个方法来写的逻辑,改成用对象来实现,而这个对象只有一个执行方法。
我们先定义一个接口,叫做 ICommand,代码如下:
namespace FrameworkDesign
{
public interface ICommand
{
void Execute();
}
}
实现接口:
public struct AddCountCommand : ICommand
{
public void Execute()
{
CounterModel.Count.Value++;
}
}
Command 模式就是逻辑的调用和执行是分离的
空间分离的方法就是调用的地方和执行的地方放在两个文件里。
时间分离的方法就是调用的之后,Command 过了一点时间才被执行。
而 Command 模式由于有了调用和执行分离这个特点,所以我们可以用不同的数据结构去组织 Command 调用,比如命令队列,再比如用一个命令的堆栈,来实现撤销功能(ctrl + z)
public class Singleton where T : class
{
public static T Instance
{
get
{
if (mInstance == null)
{
// 通过反射获取构造
var ctors = typeof(T).GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic);
// 获取无参非 public 的构造
var ctor = Array.Find(ctors, c => c.GetParameters().Length == 0);
if (ctor == null)
{
throw new Exception("Non-Public Constructor() not found in " + typeof(T));
}
mInstance = ctor.Invoke(null) as T;
}
return mInstance;
}
}
private static T mInstance;
}
IOC 容器,大家可以理解为是一个字典,这个字典以 Type 为 key,以对象即 Instance 为 value,非常简单。
而 IOC 容器最少有两个核心的 API,即根据 Type 注册实例,根据 Type 获取实例。
实现一个简单的 IOC 容器
public class IOCContainer
{
///
/// 实例
///
public Dictionary mInstances = new Dictionary();
///
/// 注册
///
///
///
public void Register(T instance)
{
var key = typeof(T);
if (mInstances.ContainsKey(key))
{
mInstances[key] = instance;
}
else
{
mInstances.Add(key,instance);
}
}
///
/// 获取
///
public T Get() where T : class
{
var key = typeof(T);
object retObj;
if(mInstances.TryGetValue(key,out retObj))
{
return retObj as T;
}
return null;
}
}
将这个代码用起来:
我们先创建一个 CounterApp 类,用于注册全部模块,代码如下:
using FrameworkDesign;
namespace CounterApp
{
public class CounterApp
{
private static IOCContainer mContainer = null;
// 确保 Container 是有实例的
static void MakeSureContainer()
{
if (mContainer == null)
{
mContainer = new IOCContainer();
Init();
}
}
// 这里注册模块
private static void Init()
{
mContainer.Register(new CounterModel());
}
// 提供一个获取模块的 API
public static T Get() where T : class
{
MakeSureContainer();
return mContainer.Get();
}
}
}
接着我们把 CounterApp 类应用起来,代码如下:
using FrameworkDesign;
using UnityEngine;
using UnityEngine.UI;
namespace CounterApp
{
public class CounterViewController : MonoBehaviour
{
private CounterModel mCounterModel;
void Start()
{
// 获取
mCounterModel = CounterApp.Get();
// 注册
mCounterModel.Count.OnValueChanged += OnCountChanged;
transform.Find("BtnAdd").GetComponent
--dd,这就是框架吗 orz
以下代码容易重复
PiontGame.cs
namespace FrameworkDesign.Example
{
public class PointGame
{
private static IOCContainer mContainer = null;
// 确保 Container 是有实例的
static void MakeSureContainer()
{
if (mContainer == null)
{
mContainer = new IOCContainer();
Init();
}
}
// 这里注册模块
private static void Init()
{
mContainer.Register(new GameModel());
}
// 提供一个获取模块的 API
public static T Get() where T : class
{
MakeSureContainer();
return mContainer.Get();
}
}
}
优化一下:创建一个类,名字叫 Architecture.cs ,代码如下:
namespace FrameworkDesign
{
///
/// 架构
///
///
public abstract class Architecture where T : Architecture, new()
{
#region 类似单例模式 但是仅在内部课访问
private static T mArchitecture = null;
// 确保 Container 是有实例的
static void MakeSureArchitecture()
{
if (mArchitecture == null)
{
mArchitecture = new T();
mArchitecture.Init();
}
}
#endregion
private IOCContainer mContainer = new IOCContainer();
// 留给子类注册模块
protected abstract void Init();
// 提供一个注册模块的 API
public void Register(T instance)
{
MakeSureArchitecture();
mArchitecture.mContainer.Register(instance);
}
// 提供一个获取模块的 API
public static T Get() where T : class
{
MakeSureArchitecture();
return mArchitecture.mContainer.Get();
}
}
}