当我们需要在游戏运行过程中动态创建新对象(如动态生成新的“敌人”实例),同时又需要新生成的对象注入依赖关系,推荐的实现方式是使用工厂。
PlaceholderFactory
PlaceholderFactory是最简单也是最常用的一种工厂,它的基本用法如下:
public class Player
{
}
public class Enemy
{
// 当前类有外部依赖
private Player _player;
public Enemy(Player player)
{
_player = player;
}
// 工厂类,继承PlaceholderFactory
public class Factory : PlaceholderFactory<Enemy> {}
}
/**
* Enemy生成类
*/
public class EnemySpawner
{
private Enemy.Factory _factory;
public EnemySpawner(Enemy.Factory factory)
{
_factory = factory;
}
public Enemy CreateEnemy()
{
Debug.Log("CreateEnemy");
return _factory.Create();
}
}
public class TestInstaller : MonoInstaller
{
public override void InstallBindings()
{
Container.Bind<EnemySpawner>().AsSingle();
Container.Bind<Player>().AsSingle();
Container.BindFactory<Enemy, Enemy.Factory>();
Container.Resolve<EnemySpawner>().CreateEnemy();
}
}
上面通过使用Enemy.Factory,所有的依赖(Player)都将被自动注入。我们也可以为工厂添加参数,比如Enemy的构造函数中需要一个float类型的speed参数,我们只需要在继承PlaceholderFactory时扩展泛型列表即可:PlaceholderFactory<float,Enemy>,与之相对的,在绑定到容器时也需要有对应的泛型列表:Container.BindFactory<float,Enemy, Enemy.Factory>()。
抽象工厂
在某些情况下,我们不希望工厂直接依赖一个具体的类,而是希望它返回一个接口类型。这种工厂称为抽象工厂。抽象工厂的用法与前面的非抽象工厂并没有太大区别,只是把泛型换成了接口类型而已。
public interface IAbsFacExample{}
public class AbsFacExampleA:IAbsFacExample{}
public class AbsFacExampleB:IAbsFacExample{}
// 工厂类
public class AbstractFactory : PlaceholderFactory<IAbsFacExample> {}
public class AbsFacExampleSpawner
{
private AbstractFactory _factory;
// 通过构造方法注入工厂类
public AbsFacExampleSpawner(AbstractFactory factory)
{
_factory = factory;
}
public IAbsFacExample Create()
{
var example = _factory.Create();
Debug.Log(example.GetType());
return example;
}
}
public class TestInstaller : MonoInstaller
{
public bool isA;
public override void InstallBindings()
{
Container.Bind<AbsFacExampleSpawner>().AsSingle();
if (isA)
{
Container.BindFactory<IAbsFacExample, AbstractFactory>().To<AbsFacExampleA>();
}
else
{
Container.BindFactory<IAbsFacExample, AbstractFactory>().To<AbsFacExampleB>();
}
Container.Resolve<AbsFacExampleSpawner>().Create();
}
}
自定义工厂
如果我们在程序运行前并不知道想要创建什么类型的实例,或在创建实例时有特殊需求,而该类的构造方法又无法满足,又该怎么办呢?答案是使用自定义工厂。只需要让一个类继承IFactory<>并重写Create()方法,然后绑定到容器时使用FromFactory<>()即可。
public interface IEnemy{}
public class EnemyA:IEnemy{}
public class EnemyB:IEnemy{}
public class EnemyFactory:PlaceholderFactory<IEnemy>{}
// 自定义工厂
public class CustomEnemyFactory : IFactory<IEnemy>
{
private EnemyFactory _factory;
private DiContainer _container;
// 构造方法注入
public CustomEnemyFactory(EnemyFactory factory,DiContainer container)
{
_factory = factory;
_container = container;
}
public IEnemy Create()
{
int a = Random.Range(-10, 10);
if (a > 0)
{
Debug.Log("EnemyA");
return _container.Instantiate<EnemyA>();
}
Debug.Log("EnemyB");
return _container.Instantiate<EnemyB>();
}
}
public class TestInstaller3 : MonoInstaller
{
public override void InstallBindings()
{
Container.BindFactory<IEnemy, EnemyFactory>().FromFactory<CustomEnemyFactory>();
for (int i = 0; i < 20; i++)
{
Container.Resolve<EnemyFactory>().Create();
}
}
}
直接注入IFactory<>
如果我们不想额外定义任何工厂类,可以直接在任何类中注入IFactory<>,然后在绑定时使用BindIFactory<>方法链接到一个构造方法。
public interface IFacExample{}
public class IFacExampleA:IFacExample{}
public class IFacExampleB:IFacExample{}
public class IFacExampleSpawner
{
private IFactory<IFacExample> _factory;
// 通过构造方法注入工厂类
public IFacExampleSpawner(IFactory<IFacExample> factory)
{
_factory = factory;
}
public IFacExample Create()
{
var example = _factory.Create();
Debug.Log(example.GetType());
return example;
}
}
public class TestInstaller4 : MonoInstaller
{
public bool isA;
public override void InstallBindings()
{
Container.Bind<IFacExampleSpawner>().AsSingle();
if (isA)
{
Container.BindIFactory<IFacExample>().To<IFacExampleA>();
}
else
{
Container.BindIFactory<IFacExample>().To<IFacExampleB>();
}
Container.Resolve<IFacExampleSpawner>().Create();
}
}
自定义工厂接口
在某些情况下我们可能想避免直接与工厂类耦合而更想用基类或自定义接口。可以在绑定时使用BindFactoryCustomInterface方法。
public class Apple
{
public class Factory:PlaceholderFactory<Apple>,IMyAppleFactory {}
}
public interface IMyAppleFactory:IFactory<Apple>{}
public class AppleSpawner
{
// 使用自定义接口
private IMyAppleFactory _factory;
// 构造方法注入
public AppleSpawner(IMyAppleFactory factory)
{
_factory = factory;
}
public Apple Create()
{
var apple = _factory.Create();
Debug.Log(apple.GetType());
return apple;
}
}
public class TestInstaller5 : MonoInstaller
{
public override void InstallBindings()
{
Container.Bind<AppleSpawner>().AsSingle();
Container.BindFactoryCustomInterface<Apple, Apple.Factory, IMyAppleFactory>();
Container.Resolve<AppleSpawner>().Create();
}
}
预制体工厂
某些情况下我们可能希望在调用Create方法时也提供用于新对象的预制体。如果直接调用DiContainer.InstantiatePrefabForComponent会违反只将容器注入到安装器(Installer)或工厂(Factories) 的原则,因此更好的方式是写一个自定义工厂:
public class Orange
{
public class Factory:PlaceholderFactory<Object,Orange>{}
}
public class OrangeFactory : IFactory<Object, Orange>
{
private DiContainer _container;
public OrangeFactory(DiContainer container)
{
_container = container;
}
public Orange Create(Object prefab)
{
return _container.InstantiatePrefabForComponent<Orange>(prefab);
}
}
public class TestInstaller6 : MonoInstaller
{
public override void InstallBindings()
{
Container.BindFactory<Object, Orange, Orange.Factory>().FromFactory<OrangeFactory>();
}
}
事实上,这种自定义工厂类十分常见,因此Zenject将其做成了helper类,叫做PrefabFactory。所以上面的代码可以简化为:
public class Orange
{
public class Factory:PlaceholderFactory<Object,Orange>{}
}
public class TestInstaller6 : MonoInstaller
{
public override void InstallBindings()
{
Container.BindFactory<Object, Orange, Orange.Factory>().FromFactory<PrefabFactory<Orange>>();
}
}
当需要从指定资源路径实例化一个预制体时,可以将上面的Object替换为string类型的路径,然后使用PrefabResourceFactory<>。
本文介绍了Unity游戏开发中Zenject框架下如何使用工厂模式动态创建带有依赖的对象。包括简单工厂(PlaceholderFactory)、抽象工厂、自定义工厂以及直接注入IFactory的使用方法,详细阐述了每种工厂模式的实现和应用场景,帮助开发者更好地理解和应用依赖注入。
322

被折叠的 条评论
为什么被折叠?



