StrangeIoC官网文档摘要

在这里插入图片描述介绍Strange框架的基本方法使用与部署项目

  • 应用程序的入口点是一个叫ContextView类,这是一个简单的MonoBehaviour ,用来对MVCSContext实例化。
  • MVCSContext(实际上是MVCSContext的子类)是你设置绑定信息的位置
  • 调度程序是一个通信总线,允许您在您的应用程序中发送消息。在MVCSContext派发的对象是TmEvents,或者,您可以按照上面列出的步骤重新对上下文使用Signals进行重写
  • Commands是通过IEvents或者Signals来出发的。当命令执行时,它执行部分应用程序逻辑
  • Models 存储状态
  • Services和应用以外的部分沟通
  • Views 是MonoBehaviours附加在GameObjects:玩家实际看到和操作的地方
  • Mediators也是MonoBehaviours,但是功能非常明确,用来解耦View和应用程序的其他部分。

Binding

单一绑定。

  • 一个Strange的绑定由两个必须部分和一个可选部分组成。必须的部分是一个key和一个value。Key触发value,就像一个事件可以触发一个回调。或一个类的实例化可以导致另一个类的实例化。可选部分是一个名字。在一些情况下,我们会使用相同的键来限定不同两个的绑定。在这种情况下,这个名字作为一个鉴别器。
    Bind<Spaceship>().To<Liberator>();
    Bind(“MeaningOfLife”).To(42);
    Bind<Spaceship>().To(“Enterprise”);
    Bind<IComputer>().To<SuperComputer>().ToName(“DeepThought”);
  • 请注意以下的写法都是一样的:
Bind<IDrive>().To<WarpDrive>();
Bind(typeof(IDrive)).To(typeof(WarpDrive));
IBinding binding = Bind<IDrive>();
binding.To<WarpDrive>();

Extensions

框架的核心是绑定。

injection(注入)

和控制反转(IOC)最密切相关的。

  • 一个类永远不需要显式地完成另一个类的依赖关系。这种模式叫做依赖注入(DI)。在依赖注入模式(DI)下,一个类请求它所需要的(理想的是一个接口的形式)和一个被称为一个注入器的类提供了它的需要。通常,这是通过一种称为反射的机制来完成的。尽量少的使用反射,速度不是很快。
  • 反映出一个类时,我们可以检查它的方法和属性。我们可以看到函数签名是什么样子,他们需要什么参数。通过检索这些参数,我们可以推断出类的依赖关系是什么样子,然后返回给它。
    设置一个依赖项的代码如下:
[Inject] //属性标签
public IInterface myInstance {get;set;}

如果你不使用DI,属性标签完全是无害的。所以你可以把它添加到你的类,如果有一天你发现DI不适合你的项目你可以直接将属性的[inject]标签去掉,属性还是普通的get/set


Instantiating injectable instances (实例注入)

你需要做两件事:

  • 绑定类在上下文(Context)中
  • 从InjectionBinder实例化实例
    IClass myInstance = injectionBinder.GetInstance<IClass>() as IClass;	

Types of injection mapping(类型注入映射)

  • 比较常用的单例的绑定实现
injectionBinder.Bind<ISocialService>().To<TwitterService>().ToSingleton();

因为Strange的依赖仅仅是一个映射。一旦你使用了DI,你就不在需要使用单例,你只需要使用单例映射。


  • 第二种类型的映射:名称注入
injectionBinder.Bind<ISocialService>()
.To<TwitterService>().ToSingleton()
.ToName(ServiceTypes.PRIMARY);

injectionBinder.Bind<ISocialService>()
.To<TwitterService>().ToSingleton()
.ToName(ServiceTypes.SECONDARY);

injectionBinder.Bind<ISocialService>()
.To<TwitterService>().ToSingleton()
.ToName(ServiceTypes.TERTIARY);


[Inject (ServiceTypes.TERTIARY)] //We mapped TwitterService to TERTIARY
public ISocialService socialService{get;set;}

注入的名称可以是任何类型,但实际运用中枚举是一个不错的选择。建议谨慎使用此功能。


  • 值映射
    有时你想要确切地知道你的注入。也许你在正在不同的程序域中加载配置文件。
Configuration myConfig = loadConfiguration();
injectionBinder.Bind<IConfig>().ToValue(myConfig);

myConfig将加载一些配置文件的结果。在你需要的地方使用IConfig,您将收到myConfig值。再一次,请注意,客户端类不知道是否这是一个单例,一个值,等等。它的工作是使用IConfig,而不知道它从哪里来。


  • 无法控制一个类。也许它来自一个你下载的程序库,并且已经是一个单例模式的存在。可以使用ToValue映射来完成单例绑定。Get()取得实例(也许在上下文)然后映射结果。
TouchCommander instance = TouchCommander.Get();
injectionBinder.Bind<TouchCommander>().ToValue(instance);

注入类的同时你可以做一些事

  • 设置注入
//使用属性注入,使用[Inject] 属性标签。
[Inject]
public ICompensator compensator{get;set;}

//名称注入
[Inject(CompensatorTypes.HEISENBERG)]
public ICompensator compensator{get;set;}

//标记成一个类的类型
[Inject(typeof(HeisenbergMarker))]
public ICompensator compensator{get;set;}
  • [Construct]
    如果你的类有多个构造函数你可以用[Construct]来标记,让StrangeIoc执行的构造函数,如果你没有加[Construct]标签的话,StrangeIoc默认执行构造函数的参数列表参数最少的函数,如果你只有一个构造函数那么相应不需要加[Construct]标签
  • [PostConstruct]
    任何方法被[PostConstruct]标记,在setter(属性注入)注入完成之后调用,它允许你在注入工作完成之后调用,他是一个安全类型不会返回空指针。
    如果你有多个[PostConstruct]标签,你可以在参数列表内指定执行顺序
    [PostConstruct(1)]

    [PostConstruct(2)]

The reflector extension(反射扩展)

反射是在运行时分析类的过程。StrangeIoC通过这个过程来确定注入内容。

//反射类型列表
List<Type> list = new List<Type> ();
list.Add (typeof(Borg));
list.Add (typeof(DeathStar));
list.Add (typeof(Galactus));
list.Add (typeof(Berserker));
//count should equal 4, verifying that all four classes were reflected.
int count = injectionBinder.Reflect (list);
The

//一切都被映射到了InjectionBinder
injectionBinder.ReflectAll();

The dispatcher extension(调度器的扩展)

StrangeIoc规范的事件叫做TmEvent
EventDipatcher里你要做两个最起初的事情,分派事件与监听

  • 监听
dispatcher.AddListener("FIRE_MISSILE", onMissileFire);
//直到FIRE_MISSILE被调度器调用,OnMissileFire方法才被调用

这样不好,使用字符串作为Key会使代码变得很脆弱,换句话说就是,他们让代码很容易出错。在一个地方一个字符串可以改变代码的其余部分不知晓,这一定是一个灾难。用常量或者枚举会更好:

dispatcher.AddListener(AttackEvent.FIRE_MISSILE, onMissileFire);

  • 移除监听
dispatcher.RemoveListener(AttackEvent.FIRE_MISSILE, onMissileFire);

  • 基于bool变量更新Listener的方法
dispatcher.UpdateListener(true, AttackEvent.FIRE_MISSILE, onMissileFire);

The command extension

该命令是先注入,然后执行,最后释放。
CommandBinder 监听着每个调度,Signals也可以绑定到命令。

  • 一个简单的命令
using strange.extensions.command.impl;
using com.example.spacebattle.utils;
namespace com.example.spacebattle.controller
{
class StartGameCommand : EventCommand
{
[Inject]
public ITimer gameTimer{get;set;}
override public void Execute()
{
gameTimer.start();
dispatcher.dispatch(GameEvent.STARTED);
}
}
}

  • 异步调用命令如同调用一个WebService,我们有两个很简单的方法 Retain() 与 Release()
using strange.extensions.command.impl;
using com.example.spacebattle.service;
namespace com.example.spacebattle.controller
{
class PostScoreCommand : EventCommand
{
[Inject]
IServer gameServer{get;set;}
override public void Execute()
{
Retain();
int score = (int)evt.data;
gameServer.dispatcher.AddListener(ServerEvent.SUCCESS, onSuccess);
gameServer.dispatcher.AddListener(ServerEvent.FAILURE, onFailure);
gameServer.send(score);
}
private void onSuccess()
{
gameServer.dispatcher.RemoveListener(ServerEvent.SUCCESS, onSuccess);
gameServer.dispatcher.RemoveListener(ServerEvent.FAILURE, onFailure);
//...do something to report success...
Release();
}
private void onFailure(object payload)
{
gameServer.dispatcher.RemoveListener(ServerEvent.SUCCESS, onSuccess);
gameServer.dispatcher.RemoveListener(
ServerEvent.FAILURE, onFailure);
//...do something to report failure...
Release();
}
}
}

你调用了Retain该命名保持在内存中,如果你没有调用release可能会造成内存泄漏


Mapping commands

commandBinder.Bind(ServerEvent.POST_SCORE).To<PostScoreCommand>();
  • 将多个命令绑定到一个事件上
commandBinder.Bind(GameEvent.HIT).To<DestroyEnemyCommand>().To<UpdateScoreCommand>();
  • 取消绑定
commandBinder.Unbind(ServerEvent.POST_SCORE);
  • 仅执行一次的命令
commandBinder.Bind(GameEvent.HIT).To<DestroyEnemyCommand>().Once();

使用Once()声明可以保证在下次调用之前销毁这个命令

  • 命令组是一连串连贯的命令,命令组中命令一个接一个执行如果其中一个失败便终止立刻调用Fail()函数 ,命令组只需要调用InSequence()函数便可以使用
commandBinder.Bind(GameEvent.HIT).InSequence()
.To<CheckLevelClearedCommand>()
.To<EndLevelCommand>()
.To<GameOverCommand>();

The signal extension

请注意Strange目前支持事件或信号映射到命令,但不是两个同时一起映射。
Signal一种调度机制- EventDispatcher 的替代品。

  • Signal比起EventDispatcher有有两个主要优点。
  • Signal调度结果中没有创建新的对象,因此GC没有必要创建很多实例。
  • 更重要的是,Signal调度是类型安全的而且Signal和其映射的回调不匹配在编译时就会报错(编译器强类型检查)
  • 创建回调函数
Signal<int> signalDispatchesInt = new Signal<int>();
Signal<string> signalDispatchesString = new Signal<string>();
signalDispatchesInt.AddListener(callbackInt);//Add a callback with an int parameter
signalDispatchesString.AddListener(callbackString);//Add a callback with a string parameter
signalDispatchesInt.Dispatch(42);//dispatch an int
signalDispatchesString.Dispatch("Ender Wiggin");//dispatch a string
void callbackInt(int value)
{
//Do something with this int
}
void callbackString(string value)
{
//Do something with this string
}
  • Signal是类型安全的,而且是向下转型,它意味着每次赋值都是一次映射
//You can do this...
Signal<SuperClass> signal = new Signal<SuperClass>();
signal.Dispatch(instanceOfASubclass);
//...but never this
Signal<SubClass> signal = new Signal<SubClass>();
signal.Dispatch(instanceOfASuperclass);

  • Signal实现了最多4个参数的重载 如果有更多的参数你可以考虑包装成对象发送
//works
Signal signal0 = new Signal();
//works
Signal<SomeValueObject> signal1 = new Signal<SomeValueObject>();
//works
Signal<int, string> signal2 = new Signal<int, string>();
//works
Signal<int, int, int> signal3 = new Signal<int, int, int>();
//works
Signal<SomeValueObject, int, string, MonoBehaviour> signal4 = new Signal<SomeValueObject, int, string, MonoBehaviour>();
//FAILS!!!! Too many params.
Signal<int, string, float, Vector2, Rect> signal5 = new Signal<int, string, float, Vector2, Rect>();
  • 写Signal子类
using System;
using UnityEngine;
using strange.extensions.signal.impl;
namespace mynamespace
{
//We're typing this Signal's payloads to MonoBehaviour and int
public class ShipDestroyedSignal : Signal<MonoBehaviour, int>
{
}
}

  • 将Signal绑定到上下文(可以直接让你的Context继承SignalContext)
protected override void addCoreComponents()
{
base.addCoreComponents();
injectionBinder.Unbind<ICommandBinder>();
injectionBinder.Bind<ICommandBinder>().To<SignalCommandBinder>().ToSingleton();
}

  • 绑定Signals来处理Commands
commandBinder.Bind<SomeSignal>().To<SomeCommand>();

  • 创建注入映射它自动映射一个Signal到一个命令
[Inject]
public ShipDestroyedSignal shipDestroyedSignal{get; set;}

  • 演示Signal/ Command 映射是如何进行
//绑定Signal来启动上下文
commandBinder.Bind<ShipDestroyedSignal>().To<ShipDestroyedCommand>();

//ShipMediator中我们注入Signal然后调用它
[Inject]
public ShipDestroyedSignal shipDestroyedSignal{get; set;}
private int basePointValue; //imagining that the Mediator holds a value for this ship
//Something happened that resulted in destruction
private void OnShipDestroyed()
{
shipDestroyedSignal.Dispatch(view, basePointValue);
}

//通过Signal的Dispatching来调用ShipDestroyedCommand
using System;
using strange.extensions.command.impl;
using UnityEngine;
namespace mynamespace
{
//Note how we extend Command, not EventCommand
public class ShipDestroyedCommand : Command
{
[Inject]
public MonoBehaviour view{ get; set;}
[Inject]
public int basePointValue{ get; set;}
public override void Execute ()
{
//Do unspeakable things to the destroyed ship
}
}
}

  • 虽然信号支持相同类型的多个参数,注入不能这样做。因此不可能为具有相同类型的两个参数的信号映射到一个命令。
  • 建议使用自定义startsignal重写你的上下文启动方式:
override public void Launch()
{
base.Launch();
//Make sure you've mapped this to a StartCommand!
StartSignal startSignal= (StartSignal)injectionBinder.GetInstance<StartSignal>();
startSignal.Dispatch();
}

  • 不使用命令映射Signal,注入Signal并不去绑定到命令上
injectionBinder.Bind<ShipDestroyedSignal>().ToSingleton();

The mediation(中介) extension

建议您的视图中包含至少两个不同的组件对象:View and Mediator.

  • 一个Mediator的例子
using Strange.extensions.mediation.impl;
using com.example.spacebattle.events;
using com.example.spacebattle.model;
namespace com.example.spacebattle.view
{
class DashboardMediator : EventMediator
{
[Inject]
public DashboardView view{get;set;}
override public void OnRegister()
{
view.init();
dispatcher.AddListener
(ServiceEvent.FULFILL_ONLINE_PLAYERS, onPlayers);
dispatcher.Dispatch
(ServiceEvent.REQUEST_ONLINE_PLAYERS);
}
override public void OnRemove()
{
dispatcher.RemoveListener
(ServiceEvent.FULFILL_ONLINE_PLAYERS, onPlayers);
}
private void onPlayers(IEvent evt)
{
IPlayers[] playerList = evt.data as IPlayers[];
view.updatePlayerCount(playerList.Length);
}
}
}
  • OnRegister() 是注入后立即触发的方法。它就像一个构造函数,你可以使用它来初始化视图,并执行其他初始化过程,包括 — 正如我们正在做的 — 请求重要数据
  • OnRemove() 是为清理添加过的监听 ;刚好在一个视图销毁前。请记住删除所有已经添加的侦听器。

  • 绑定一个中介到视图上
mediationBinder.Bind<DashboardView>().To<DashboardMediator>();

前言 unity的框架,除了各大公司自己内部使用的,开源并好用的实际并不是很多,我会慢慢挖掘,依次写出自己的一点见解,错误的地方,望各路大神指正。 一、基本概念 控制反转(Inversion of Control,英文缩写为IOC),我的理解就是,原本A类要获取B类的对象,需要你在A类中自己New一个对象,那么是由A来获取并控制B的对象,IOC就是把对象获取的这个过程交给容器和依赖注入来处理,A类并不知道B的对象是哪里来的,对B对象的控制,由自己变成了其他类,官方一点的概念可以百度,这个还是蛮多的。 二、StrangeIOC基础类型 实际要理解一个框架的类型,还是要自己看源码,这里我只说一下几个重要类型的作用,这个看源码的时候有个印象,也方便理解,而且说这部分的帖子也很多,我就不再赘述了。 1.Context 上下文组件定义程序边界,也就是可以把一个程序定义成多上下文,让代码更加模块化 它提供了程序入口,也算是框架中耦合度最高的地方 2.Binder和Binding 这两个类是这个框架最重要的组成部分 Binding存储了对象的绑定关系,而Binder存储了Binding的对象 3.View和Mediator MVCS中的View层,View只用于显示,也就是View只负责管理UI,Mediator负责界面逻辑,事件响应等 4.Model MVCS中的Model层,负责数据部分 5.Command MVCS中的Control层,负责执行逻辑代码 6.Service MVCS中的Service层,负责与第三方交互,这个Service我理解的,并不是一定指代服务器,也可以是其他的软件,什么都可以,它就是我们程序对外的接口 7.Dispatcher 派发器是框架内通信主线的其中一种,用来派发消息,触发命令,从而进一步解耦 8.Signal 信号是框架内另外一种通信主线,它采用强类型,来绑定信号和命令之间的关系,实现消息响应的触发 9.ReflectionBinder 反射部分,通过binding来获取类的信息,存储在ReflectedClass中 10.injector 注入器,通过反射获取的信息,来实例化请求的对象 --------------------- 作者:蓝天小僧 来源:CSDN 原文:https://blog.csdn.net/zcaixzy5211314/article/details/80876228 版权声明:本文为博主原创文章,转载请附上博文链接!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值