14.Unity Zenject高级编程(Sub-Containers子容器)


简介

一些情况下,使用多个容器在同一个应用里面非常有帮助;
例如,如果你创建一个文字处理,你可能需要多个子容器为每个标签代表一个隔离的文档;
这种处理方式,你可能使用子容器绑定一堆类成AsSingle()
并且这些如果全部是单例容器可以容易相互引用;然后你可能为每个文档实例多个子容器,
每个子容器拥有唯一的实例,所有的类在每个指定文档处理

另外一个实例你可能设计一个开放世界的太空船游戏,你可能想每个太空船拥有自己的容器,
包含所有的类实例承担运行指定的飞船

这也是ProjectContext的实际绑定工作方式;整个项目有一个容器,当unity场景启动,
容器内部的每个SceneContextProjectContext容器之下创建;
所有你添加到场景里面的MonoInstaller绑定到你的SceneContext容器里面;这将允许
你场景的引用自动获得ProjectContext绑定注入,因为子容器自动关联他们父容器所有绑定

一个通用的设计模式是我们喜欢联系子容器使用外观模式(门面模式);
这个模式通常抽象隔离引用关系组,因此可以用作更高级模块当通过其他模块作为基础编码;
这里是相关的,因为你会经常定义子容器到你的应用里面,并且非常有帮助,
定义外观类通常关联这个子容器的全部;因此,应用这些到上面的飞船游戏里面,
你可能需要一个SpaceshipFacade类代表非常高等级太空船操作,
比如开启引擎、收到伤害、飞到目的地等等;SpaceshipFacade类可以代理特殊的处理,
所有的这些部分请求相关的 单一职责 引用在子容器里面存在

子容器也可以非常有效的方式组织大部分的代码到区分合并的模块;
你可以在高层管理引用,使用模块之间的引用替代代码;
没有子容器,你的基础代码越写越多,所有的存在作为单例在同一个层级,加剧不方便,
一旦每个类可以依赖每个其他的类,仅仅是添加构建参数;
替代成长的关系类到他们自己的子容器并强制其他需求类通过外观类来交互,
这样更容易管理和理解整个部分代码引用;
你可以想象,每个外观类为整个子容器作为一个具体类型,他们的具体实现在子容器里面不可见,
你可以有多个不同低层类拥有他们自己的单一职责;
结果将会低耦合代码,这样将来重构、维护、测试、阅读更简单

Hello World Example For Sub-Containers/Facade
public class Greeter
{
   
    readonly string _message;

    public Greeter(string message)
    {
   
        _message = message;
    }

    public void DisplayGreeting()
    {
   
        Debug.Log(_message);
    }
}

public class GameController : IInitializable
{
   
    readonly Greeter _greeter;

    public GameController(Greeter greeter)
    {
   
        _greeter = greeter;
    }

    public void Initialize()
    {
   
        _greeter.DisplayGreeting();
    }
}

public class TestInstaller : MonoInstaller
{
   
    public override void InstallBindings()
    {
   
        Container.BindInterfacesTo<GameController>().AsSingle();
        Container.Bind<Greeter>().FromSubContainerResolve().ByMethod(InstallGreeter).AsSingle();
    }

    void InstallGreeter(DiContainer subContainer)
    {
   
        subContainer.Bind<Greeter>().AsSingle();
        subContainer.BindInstance("Hello world!");
    }
}

重要的事情是理解这里任何绑定我们添加到InstallGreeter方法将只会在子容器里面是可见的;
仅有的异常是外观类一旦绑定到父容器,使用FromSubContainerResolve来绑定;
这个例子里面,Hello Wrold只对Greeter类可见

注意以下事项:

  • 你应该总是添加绑定语句,无论类是否指定FromSubContainerResolve,在子容器里面的方法,
    如果你没有添加本体的绑定信息到你的子容器里面,在验证的时候你会得到异常
  • 不要使用上面示例的方法,通常更好是使用ByInstaller替代ByMethod;这是因为当你使用ByMethod
    容易发生引用容器替代子容器,同样,通过使用ByInstaller你可以给安装器自己传递参数
  • 子容器也可以使用在类似动态生产外观,使用BindFactoryFromSubContainerResolve
  • 这种方法有一些弊端,当使用MonoBehaviour’s或者使用IInitializable/ITicakable/IDisposable接口
Creating Sub-Containers on GameObject’s by using Game Object Context

上面的示例不能够很好的为MonoBehaviour类运行的一个问题;
没有什么阻止我们添加MonoBehaviour绑定,例如
FromConponentInNewPrefab,FromNewComponentOnNewGameObject,等等;
直到我们的的ByInstaller/ByMethod子容器 - 然而这些将会转换这些新游戏对象添加到层级视图的根节点,
因此我们将必须手动跟踪这些对象的生命周期,
通过调用在他们上面GameObject.Destroy,当他们的外观类销毁了;
同样,没有方式保证场景中存在的GameObject在启动的存在,但在子容器里面退出;
还有,使用ByInstaller和ByMethod像上面那样不支持使用这些接口,比如IInitializable/
ITickable/IDisposable在子容器里面;这些问题可以通过使用GameObject Context来解决
开放世界太空船例子:

  • 创建一个新场景
  • 添加以下文件到你的项目里面
using Zenject;
using UnityEngine;

public class Ship : MonoBehaviour
{
   
    ShipHealthHandler _healthHandler;

    [Inject]
    public void Construct(ShipHealthHandler healthHandler)
    {
   
        _healthHandler = healthHandler;
    }

    public void TakeDamage(float damage)
    {
   
        _healthHandler.TakeDamage(damage);
    }
}
using UnityEngine;
using Zenject;

public class GameRunner : ITickable
{
   
    readonly Ship _ship;

    public GameRunner(Ship ship)
    {
   
        _ship = ship;
    }

    public void Tick()
    {
   
        if (Input.GetKeyDown(KeyCode.Space))
        {
   
            _ship.TakeDamage(10);
        }
    }
}
using Zenject;

public class GameInstaller : MonoInstaller
{
   
    public override void InstallBindings()
    {
   
        Container.BindInterfacesTo<GameRunner>().AsSingle();
    }
}
using Zenject;
using UnityEngine;

public class ShipHealthHandler : MonoBehaviour
{
   
    float _health = 100;

    public void OnGUI()
    {
   
        GUI.Label(new Rect(Screen.width / 2, Screen.height / 2, 200, 100), "Health: " + _health);
    }

    public void TakeDamage(float damage)
    {
   
        _health -= damage;
    }
}
using UnityEngine;
using System.Collections;

public class ShipInputHandler : MonoBehaviour
{
   
    [SerializeField]
    float _speed = 2;

    public void Update()
    {
   
        if (Input.GetKey(KeyCode.UpArrow))
        {
   
            this
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值