..

简单介绍 QFramework

Intro

QFramework 是一个由凉鞋开发的轻量级的框架,狭义上的 QFramework 是指仓库里的那个 QFramework.cs 文件,提供的是一套数据管理的方式,他由 Architecture, System, Controller, Model 组成,辅以 TypeEventSystem, Command 等消息通知手段,以及可有可无,存在感比较低的 UtilityQuery

广义上的 QFramework 是一个包含了 QFramework.cs 以及相关配套插件的一套生态,包括了 UIKit, ResKit, AudioKit, SingletonKitFsmKit 等工具。

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 容器。

Reference

There is nothing new under the sun.