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

Reference