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().To();
    Bind(“MeaningOfLife”).To(42);
    Bind().To(“Enterprise”);
    Bind().To().ToName(“DeepThought”);
  • 请注意以下的写法都是一样的:
Bind().To();
Bind(typeof(IDrive)).To(typeof(WarpDrive));
IBinding binding = Bind();
binding.To();

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() as IClass;	

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

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

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


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

injectionBinder.Bind()
.To().ToSingleton()
.ToName(ServiceTypes.SECONDARY);

injectionBinder.Bind()
.To().ToSingleton()
.ToName(ServiceTypes.TERTIARY);


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

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


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

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


  • 无法控制一个类。也许它来自一个你下载的程序库,并且已经是一个单例模式的存在。可以使用ToValue映射来完成单例绑定。Get()取得实例(也许在上下文)然后映射结果。
TouchCommander instance = TouchCommander.Get();
injectionBinder.Bind().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 list = new List ();
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();
  • 将多个命令绑定到一个事件上
commandBinder.Bind(GameEvent.HIT).To().To();
  • 取消绑定
commandBinder.Unbind(ServerEvent.POST_SCORE);
  • 仅执行一次的命令
commandBinder.Bind(GameEvent.HIT).To().Once();

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

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

The signal extension

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

  • Signal比起EventDispatcher有有两个主要优点。
  • Signal调度结果中没有创建新的对象,因此GC没有必要创建很多实例。
  • 更重要的是,Signal调度是类型安全的而且Signal和其映射的回调不匹配在编译时就会报错(编译器强类型检查)
  • 创建回调函数
Signal signalDispatchesInt = new Signal();
Signal signalDispatchesString = new Signal();
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 signal = new Signal();
signal.Dispatch(instanceOfASubclass);
//...but never this
Signal signal = new Signal();
signal.Dispatch(instanceOfASuperclass);

  • Signal实现了最多4个参数的重载 如果有更多的参数你可以考虑包装成对象发送
//works
Signal signal0 = new Signal();
//works
Signal signal1 = new Signal();
//works
Signal signal2 = new Signal();
//works
Signal signal3 = new Signal();
//works
Signal signal4 = new Signal();
//FAILS!!!! Too many params.
Signal signal5 = new Signal();
  • 写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
{
}
}

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

  • 绑定Signals来处理Commands
commandBinder.Bind().To();

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

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

//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.Dispatch();
}

  • 不使用命令映射Signal,注入Signal并不去绑定到命令上
injectionBinder.Bind().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().To();

你可能感兴趣的:(U3D)