简单介绍 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。
1// Architecture
2private TypeEventSystem mTypeEventSystem = new TypeEventSystem();
3
4public void SendEvent<TEvent>() where TEvent : new() => mTypeEventSystem.Send<TEvent>();
5
6public void SendEvent<TEvent>(TEvent e) => mTypeEventSystem.Send<TEvent>(e);
7
8public IUnRegister RegisterEvent<TEvent>(Action<TEvent> onEvent) => mTypeEventSystem.Register<TEvent>(onEvent);
9
10public void UnRegisterEvent<TEvent>(Action<TEvent> onEvent) => mTypeEventSystem.UnRegister<TEvent>(onEvent);
TypeEventSystem 中的事件调用使用的是 TypeEventSystem 自己的单例。
1public class TypeEventSystem
2{
3 private readonly EasyEvents mEvents = new EasyEvents();
4
5 public static readonly TypeEventSystem Global = new TypeEventSystem();
6
7 public void Send<T>() where T : new() => mEvents.GetEvent<EasyEvent<T>>()?.Trigger(new T());
8
9 public void Send<T>(T e) => mEvents.GetEvent<EasyEvent<T>>()?.Trigger(e);
10
11 public IUnRegister Register<T>(Action<T> onEvent) => mEvents.GetOrAddEvent<EasyEvent<T>>().Register(onEvent);
12
13 public void UnRegister<T>(Action<T> onEvent)
14 {
15 var e = mEvents.GetEvent<EasyEvent<T>>();
16 e?.UnRegister(onEvent);
17 }
18}
AbstractCommand
QFramework 在执行命令时,首先为 Command 设置 Architecture,然后调用 ICommand
的 Execute 方法。
1// Architecture 执行 Command
2public TResult SendCommand<TResult>(ICommand<TResult> command) => ExecuteCommand(command);
3
4public void SendCommand<TCommand>(TCommand command) where TCommand : ICommand => ExecuteCommand(command);
5
6protected virtual TResult ExecuteCommand<TResult>(ICommand<TResult> command)
7{
8 command.SetArchitecture(this);
9 return command.Execute();
10}
11
12protected virtual void ExecuteCommand(ICommand command)
13{
14 command.SetArchitecture(this);
15 command.Execute();
16}
BindableProperty
BP 的实现相当简单,只要 Value 被重新赋值了,就会触发 mOnValueChanged。
但局限性也很明显,那就是只能处理值类型的数据,引用类型的数据(比如 Dictionary)一般不会被重新赋值,外界使用的时候也只是修改引用类型数据的内部数据。
1public class BindableProperty<T> : IBindableProperty<T>
2{
3 public BindableProperty(T defaultValue = default) => mValue = defaultValue;
4
5 protected T mValue;
6
7 public static Func<T, T, bool> Comparer { get; set; } = (a, b) => a.Equals(b);
8
9 public BindableProperty<T> WithComparer(Func<T, T, bool> comparer)
10 {
11 Comparer = comparer;
12 return this;
13 }
14
15 public T Value
16 {
17 get => GetValue();
18 set
19 {
20 if (value == null && mValue == null) return;
21 if (value != null && Comparer(value, mValue)) return;
22
23 SetValue(value);
24 mOnValueChanged.Trigger(value);
25 }
26 }
27
28 protected virtual void SetValue(T newValue) => mValue = newValue;
29
30 protected virtual T GetValue() => mValue;
31
32 public void SetValueWithoutEvent(T newValue) => mValue = newValue;
33
34 private EasyEvent<T> mOnValueChanged = new EasyEvent<T>();
35
36 public IUnRegister Register(Action<T> onValueChanged)
37 {
38 return mOnValueChanged.Register(onValueChanged);
39 }
40
41 public IUnRegister RegisterWithInitValue(Action<T> onValueChanged)
42 {
43 onValueChanged(mValue);
44 return Register(onValueChanged);
45 }
46
47 public void UnRegister(Action<T> onValueChanged) => mOnValueChanged.UnRegister(onValueChanged);
48
49 IUnRegister IEasyEvent.Register(Action onEvent)
50 {
51 return Register(Action);
52 void Action(T _) => onEvent();
53 }
54
55 public override string ToString() => Value.ToString();
56}
总结
如果需要用非常非常简洁的话来描述 QFramework,它是一个有着良好权限控制的 IOC 容器。