Unity轻量级开源框架StrangeIoc

StrangeIoc

依赖注入

1. 什么是依赖关系:

  • 依赖可以被认为是一个对象想要执行其功能,需要另外一个对象的相应功能。

  • 假设A类的变化引起了B类的变化,则说明B类依赖于A类。

  • uml图(Diver依赖于Car):

image

public class Driver  
{  
    public void drive(Car car)  
    {  
        car.move();  
    }  
    ……  
}  
public class Car  
{  
    public void move()  
    {  
        ......  
    }  
    ……  
}
  • 依赖关系有如下三种情况:

1、A类是B类中的(某中方法的)局部变量;

2、A类是B类方法当中的一个参数;

3、A类向B类发送消息,从而影响B类发生变化;

2.什么是组合关系:

  • 组合关系也是整体与部分的关系,但是整体与部分不可以分开。

  • 组合关系中部分和整体具有统一的生存期。一旦整体对象不存在,部分对象也将不存在。即成员类的存在依赖于整体类

  • uml图:

image

public class Head  
{  
    private Mouth mouth;  
    public Head()  
    {  
    mouth = new Mouth();  
    }  
    ……  
}  
  
public class Mouth  
{  
    ……  
}

3.Unity的框架

Unity的框架是基于组合,没有入口控制

4.反转控制IOC

  • 单件最大的问题:
    • 不能设计成反转控制方式
    • 存在内存泄漏
  • 依赖注入 = 不要直接绑定
  • 例子:
class 宇宙飞船
{
    if (InputMana.getbutton(键盘空格))
    {
        Fire();
    }
}

问题:当需求改变时,比如现在不用键盘空格,改为鼠标点击,为什么要在飞船类修改呢?
这是一个修改控制的问题,为什么要找飞船类解决。

  • 改进:
class InputManger
{
    if(键盘空格)
    {
        SendEvent(键盘空格被按下了);
    }
}
class Meditor
{
    监听事件;
    收到不同消息采取不同措施(分发给需要听到该消息的人);
}

这样做的好处是依赖倒置,控制反转;在input类里面我们只关心记录发生了什么Input事件,不关心input事件触发了什么。

这样有点类似观察者模式,但是观察者模式也是直接注册,strangeioc框架在里面加入了一个中介层


SrangeIoc的用法

ContextView

是游戏的顶层,游戏入口,继承于Monobehaviour的,需要挂载在场景的GameObject中

class GameRoot : ContextView
{
    context = new GameContext(this);
}

MVCSContext

在里面bing绑定各个模块:view,meditor,command,event

public class GameContext : MVCSContext
{

    public GameContext(MonoBehaviour view) : base(view)
    {
    }

    public GameContext(MonoBehaviour view, ContextStartupFlags flags) : base(view, flags)
    {
    }

    protected override void mapBindings()
    {
       

        //View/Mediator binding
        //每当作为ExampleView时,这个绑定实例化一个新的ExampleMediator
        //Fires its Awake method. The Mediator communicates to/from the View
        //and to/from the App. This keeps dependencies between the view and the app
        //separated. 中介和View通信,和app通信,使得视图和应用程序依赖

        mediationBinder.Bind<InputView>().To<InputMeditor>();

        //Event/Command binding
        
        commandBinder.Bind(ExampleEvent.REQUEST_WEB_SERVICE).To<CallWebServiceCommand>();
        
        //一旦映射完成,START事件就会被触发。
        //请注意我们是如何将它绑定到“Once”的。这意味着一旦命令触发,映射就会消失。

        commandBinder.Bind(ContextEvent.START).To<StartCommand>().Once();

    }
}

View

负责监管表现层,如果发生变化,就用dispatcher.Dispatch(EVENT, Data);
继承于Monobehaviour的,需要挂载在场景的GameObject中。

应该由Meditor发送全局事件,View不能,View只能发送自己的事件给Meditor(语法上是可以直接发全局事件,为了统一好管理,最好是不发全局事件)

Meditor

监听event事件(来自view或者全局),继承于Monobehaviour的,需要挂载在场景的GameObject中

Meditor可以直接dispacher,addListener,监听全局事件。没有必要发送到Service再Meditor→View,只有要发送到Service的需要用Command

多个View之间沟通示意图:

image

/// <summary>
    /// 注册后执行函数
    /// </summary>
    public override void OnRegister()
    {

        //Listen to the view for an event
        //view.dispatcher.AddListener(InputManager.CLICK_EVENT, onViewClicked);
        view.dispatcher.AddListener(InputView.TRIGGER_LEFTJOYSTICK_EVENT, onTriggerLeftJoystick);

        //Listen to the global event bus for events
        //dispatcher.AddListener(InputManager.SCORE_CHANGE, onScoreChange);

        view.init();
    }

Command

放置业务逻辑的地方;

默认情况下,在Execute()方法完成后立即清除命令。
对于异步命令(例如,呼叫服务并等待响应),在Execute()方法的顶部调用Retain(),这将防止过早清理。
但是请记住,一旦完成了命令,那么调用Release()是您的责任。

只有Command能够获得service和modle的单例,是可以直接注入model和service的;

[Inject]
public IExampleModel model{get;set;}
		
[Inject]
public IExampleService service{get;set;}

Command可以发送事件,这时meditor就会收到。Execute()是放置命令逻辑的地,在实例化的时候执行

public override void Execute()
{
           
    Retain();
			
	//Call the service. Listen for a response 告诉service监听这个事件,并且设置需要作出的回应
	service.dispatcher.AddListener(ExampleEvent.FULFILL_SERVICE_REQUEST, onComplete);
	
	service.Request("http://www.thirdmotion.com/ ::: " + counter.ToString());
}

相当于:

meditor
command
service

在绑定好的事件出现后,生成一个new Command(),记得Command完成后销毁:Release()

Service

只跟command沟通,全局唯一(单例),放公用的方法

Models

相互沟通的示意图

image