简单介绍 QFramework
Intro
QFramework 是一个由凉鞋开发的轻量级的框架,狭义上的 QFramework 是指仓库里的那个 QFramework.cs 文件,提供的是一套数据管理的方式,他由 Architecture, System, Controller, Model 组成,辅以 TypeEventSystem, Command 等消息通知手段,以及可有可无,存在感比较低的 Utility 和 Query。
广义上的 QFramework 是一个包含了 QFramework.cs 以及相关配套插件的一套生态,包括了 UIKit, ResKit, AudioKit, SingletonKit 和 FsmKit 等工具。
QFramework 的架构
QFramework 在使用上的架构,大致分为了四层。分别是表现层,系统层,数据层和工具层。
在这些层级中,表现层(Controller)居于最顶层,可以获取 System 层、Model 层的引用,也可以发送事件和监听 Event。
System 位于表现层和数据层之间,可以获取其他的 System 层、Model 层的引用,也可以发送事件和监听 Event。
Model 几乎位于系统的最底层,他不能去影响系统层和表现层的任何东西,因此它只能发送和监听 Event。
Utility 是系统的最最最底层,只提供一些运行上的工具支撑,除了被其他层调用外,无法对系统做任何的影响。
block-beta
columns 4
Controlle:4
BattleSystem NetworkSystem StoreSystem AchievementSystem
PlayerModel GameModel:2 ConfigModel
ResUtility ParserUtility DeviceUtility SdkUtility
通过阅读源代码,我们可以清晰地画出 QFramework 的类图。
classDiagram
direction TB
class IBelongToArchitecture {
- GetArchitecture()* IArchitecture
}
ICanInit --|> IBelongToArchitecture
class ICanInit {
- Initialized: bool
- Init()* void
- Deinit()* void
}
class ICanRegisterEvent
ICanRegisterEvent --|> IBelongToArchitecture
class ICanGetModel
ICanGetModel --|> IBelongToArchitecture
class ICanGetUtility
ICanGetUtility --|> IBelongToArchitecture
class ICanSetArchitecture
ICanSetArchitecture --|> IBelongToArchitecture
class ICanSendEvent
ICanSendEvent --|> IBelongToArchitecture
class ICanSendQuery
ICanSendQuery --|> IBelongToArchitecture
class ICanSendCommand
ICanSendCommand --|> IBelongToArchitecture
class ICanRegisterEvent
ICanRegisterEvent --|> IBelongToArchitecture
class ICanGetModel
ICanGetModel --|> IBelongToArchitecture
class ICanGetUtility
ICanGetUtility --|> IBelongToArchitecture
class ICanSetArchitecture
ICanSetArchitecture --|> IBelongToArchitecture
class ICanSendEvent
ICanSendEvent --|> IBelongToArchitecture
class ICanSendQuery
ICanSendQuery --|> IBelongToArchitecture
class ICanSendCommand
ICanSendCommand --|> IBelongToArchitecture
class IModel
IModel --|> ICanSetArchitecture
IModel --|> ICanSendEvent
IModel --|> ICanInit
IModel --|> IBelongToArchitecture
class ICanGetSystem
ICanGetSystem --|> IBelongToArchitecture
class ISystem
ISystem --|> ICanSetArchitecture
ISystem --|> ICanGetModel
ISystem --|> ICanGetUtility
ISystem --|> ICanRegisterEvent
ISystem --|> ICanSendEvent
ISystem --|> ICanGetSystem
ISystem --|> ICanInit
ISystem --|> IBelongToArchitecture
class IController
IController --|> IBelongToArchitecture
IController --|> ICanSendCommand
IController --|> ICanGetSystem
IController --|> ICanGetModel
IController --|> ICanRegisterEvent
IController --|> ICanSendQuery
IController --|> ICanGetUtility
class AbstractSystem {
- mArchitecture: IArchitecture
- GetArchitecture()* IArchitecture
- SetArchitecture(architecture: IArchitecture)
+ Initialized: bool
- Init()
- DeInit()
}
AbstractSystem --|> ISystem
class AbstractModel {
- mArchitecture: IArchitecture
- GetArchitecture()
- SetArchitecture(architecture: IArchitecture)
+ Initialized: bool
- Init()
- DeInit()
}
AbstractModel --|> IModel
通过抽象出各种行为的接口(如 ICanSendEvent, ICaGetModel, ICanGetSystem 等),QFramework 实现了各个层次的对象的行为控制。
并且我们可以清晰地看到,所有的层级控制都需要继承 IBelongToArchitecture 接口,这也是 QFramework 能够返回 System 层和 Model 层对象引用的核心。
根据QFramework的教程,继承了 IBelongToArchitecture 接口的对象需要返回一个IArchitecture 接口,通常来说我们将调用某个继承了Architecture<T>的类,并返回它的Interface 属性。
而这个接口将在 Architecture<T> 未初始化时自动调用 Architecture<T> 抽象类的 MakeSureArchitecture() 方法,该方法将为 Architecture<T> 创建一个 mArchitecture 实例,并调用 Init 方法进行框架初始化。
说的简单一些,Architecture<T> 就是一个懒汉模式的单例,当外部调用时该单例就会实例化。
以下是 IController 对象初始化整个 QFramework 架构的例子。
graph TD
IController对象被实例化 -->
IController中调用了QFramework相关的方法 -->
相关方法调用IController的GetArchitecture方法获取相关Architecture的引用 -->
Architecture调用MakeSureArchitecture方法创建引用并返回IArchitecture接口 -->
相关方法通过IArchitecture接口获取Architecture所有注册的对象 -->
获取到资源的相关方法开始运行
TypeEventSystem
TypeEventSystem 是一个基于类型系统实现的一个事件收发器。核心的事件注册与触发依赖于 EasyEvents 类的 mGlobalEvents 字典。
classDiagram
direction TD
class IUnRegister
class IEasyEvent {
+ Register(Action onEvent) IUnRegister
}
IEasyEvent -- IUnRegister
class EasyEvent {
- mOnEvent: Action
+ Register(Action onEvent) IUnRegister
+ RegisterWithACall(Action onEvent) IUnRegister
+ UnRegister(Action onEvent) void
+ Trigger() void
}
EasyEvent --|> IEasyEvent
class EasyEvents {
- s_mGlobalEvents: EasyEvents
+ s_Get~T~() T
+ s_Register~T~()
- mTypeEvents: Dictionary~Type, IEasyEvent~
+ AddEvent~T~() void
+ GetEvent~T~() T
+ GetOrAddEvent~T~() T
}
EasyEvents -- IEasyEvent
class TypeEventSystem {
- mEvents: EasyEvents
+ s_Global: TypeEventSystem
+ Send~T~()
+ Register~T~(Action onEvent) IUnRegister
+ UnRegister~T~(Action onEvent) void
}
TypeEventSystem -- EasyEvent
TypeEventSystem -- EasyEvents
QFramework 使用 TypeEventSystem 有两种方法,一种是继承了 QFramework 的接口后调用事件的发送与监听(即 this.SendEvent 调用),一种是直接使用 TypeEventSystem 的静态方法调用事件的发送与监听(即TypeEventSystem.Global.SendEvent )。
Architecture 中的事件调用使用的是在 Architecture 中实例化的 TypeEventSystem。
// Architecture
private TypeEventSystem mTypeEventSystem = new TypeEventSystem();
public void SendEvent<TEvent>() where TEvent : new() => mTypeEventSystem.Send<TEvent>();
public void SendEvent<TEvent>(TEvent e) => mTypeEventSystem.Send<TEvent>(e);
public IUnRegister RegisterEvent<TEvent>(Action<TEvent> onEvent) => mTypeEventSystem.Register<TEvent>(onEvent);
public void UnRegisterEvent<TEvent>(Action<TEvent> onEvent) => mTypeEventSystem.UnRegister<TEvent>(onEvent);
TypeEventSystem 中的事件调用使用的是 TypeEventSystem 自己的单例。
public class TypeEventSystem
{
private readonly EasyEvents mEvents = new EasyEvents();
public static readonly TypeEventSystem Global = new TypeEventSystem();
public void Send<T>() where T : new() => mEvents.GetEvent<EasyEvent<T>>()?.Trigger(new T());
public void Send<T>(T e) => mEvents.GetEvent<EasyEvent<T>>()?.Trigger(e);
public IUnRegister Register<T>(Action<T> onEvent) => mEvents.GetOrAddEvent<EasyEvent<T>>().Register(onEvent);
public void UnRegister<T>(Action<T> onEvent)
{
var e = mEvents.GetEvent<EasyEvent<T>>();
e?.UnRegister(onEvent);
}
}
AbstractCommand
QFramework 在执行命令时,首先为 Command 设置 Architecture,然后调用 ICommand 的 Execute 方法。
// Architecture 执行 Command
public TResult SendCommand<TResult>(ICommand<TResult> command) => ExecuteCommand(command);
public void SendCommand<TCommand>(TCommand command) where TCommand : ICommand => ExecuteCommand(command);
protected virtual TResult ExecuteCommand<TResult>(ICommand<TResult> command)
{
command.SetArchitecture(this);
return command.Execute();
}
protected virtual void ExecuteCommand(ICommand command)
{
command.SetArchitecture(this);
command.Execute();
}
BindableProperty
BP 的实现相当简单,只要 Value 被重新赋值了,就会触发 mOnValueChanged。
但局限性也很明显,那就是只能处理值类型的数据,引用类型的数据(比如 Dictionary)一般不会被重新赋值,外界使用的时候也只是修改引用类型数据的内部数据。
public class BindableProperty<T> : IBindableProperty<T>
{
public BindableProperty(T defaultValue = default) => mValue = defaultValue;
protected T mValue;
public static Func<T, T, bool> Comparer { get; set; } = (a, b) => a.Equals(b);
public BindableProperty<T> WithComparer(Func<T, T, bool> comparer)
{
Comparer = comparer;
return this;
}
public T Value
{
get => GetValue();
set
{
if (value == null && mValue == null) return;
if (value != null && Comparer(value, mValue)) return;
SetValue(value);
mOnValueChanged.Trigger(value);
}
}
protected virtual void SetValue(T newValue) => mValue = newValue;
protected virtual T GetValue() => mValue;
public void SetValueWithoutEvent(T newValue) => mValue = newValue;
private EasyEvent<T> mOnValueChanged = new EasyEvent<T>();
public IUnRegister Register(Action<T> onValueChanged)
{
return mOnValueChanged.Register(onValueChanged);
}
public IUnRegister RegisterWithInitValue(Action<T> onValueChanged)
{
onValueChanged(mValue);
return Register(onValueChanged);
}
public void UnRegister(Action<T> onValueChanged) => mOnValueChanged.UnRegister(onValueChanged);
IUnRegister IEasyEvent.Register(Action onEvent)
{
return Register(Action);
void Action(T _) => onEvent();
}
public override string ToString() => Value.ToString();
}
总结
如果需要用非常非常简洁的话来描述 QFramework,它是一个有着良好权限控制的 IOC 容器。