反转部分注射

时间:2018-05-08 14:36:36

标签: typescript dependency-injection inversion-of-control inversifyjs

如何使用Inversify完成部分属性注入?

我们说我们上课了

class MyClass {
    constructor(
        @inject(EXTERNAL_A) private externalA: ExternalClassA,
        private b: string
    ) {}
}

如果在编译时知道b的所有可能值,如何在其他类中使用此MyClass和inversify。因此,我只需要b = "a"的MyClass实例和b = "b"的另一个实例。

我目前找到的唯一解决方案是为其定义两个不同的绑定或使用工厂直接调用new MyClass

在第一种情况下,我需要写一些类似的东西

container.bind<MyClass>(CLASS_A).toConstantValue(
    new MyClass(container.get<ExternalClassA>(EXTERNAL_A), "a")
);

container.bind<MyClass>(CLASS_B).toConstantValue(
    new MyClass(container.get<ExternalClassA>(EXTERNAL_A), "b")
);

看起来非常混乱,没有解决下一个问题,以及工厂没有解决它。如果我在这种情况下有深层对象层次结构,我需要通过这个手动对象构造链构建它们。

这里最好的方法是什么?

使用星号的任务,如果可以解决一些依赖关系树,并用提供的相同的单个依赖关系替换?就像,我们可以有像

这样的东西
 |-----B-----e
A|-----C-----e
 |-----D
 |-----e

所以我不想用我构造的e替换//this.http.get("https://example.com/_api/web/lists/getbytitle('ImagingDocumentTypes')/items?$select=Title,Image,Source/Title,System/Title&$orderBy=Title&$expand=Source,System&$filter=System/Title eq '" + this.item + "'").subscribe(data => { //this.doctypes = data['value']; //EXAMPLE OUTPUT IS: this.doctypes = [{ "Source": [{ "Title": "Anchor (MN)" }, { "Title": "United Bancorp" }, { "Title": "Lafayette Savings Bank" }], "Title": "Commercial Loans", }, { "Source": [{ "Title": "Anchor (WI)" }, { "Title": "United Bancorp" }, { "Title": "Old National" }], "Title": "Checks", }, { "Source": [{ "Title": "Anchor (MN)" }, { "Title": "Anchor (WI)" }, { "Title": "Old National" }], "Title": "HR Documents", }] for (let i = 0; i < this.doctypes.length; i++) { let sources = this.doctypes[i].Source; let sourcesarr = []; for (let i = 0; i < sources.length; i++) { console.log(sources[i].Title) sourcesarr.push(sources[i].Title) console.log(sourcesarr) } sourcesarr.sort() } //})依赖。我怎样才能做到这一点?

2 个答案:

答案 0 :(得分:0)

嗨,

恐怕没有完美的解决方案。在这种情况下,我通常使用带有setsetup方法的工厂模式。

示例:

interface MyInterface {
     myPublicMethod();
}

type MyInterfaceFactoryA = () => MyInterface;
type MyInterfaceFactoryB = () => MyInterface;

class MyClass extends MyInterface {

     private b: string;

     constructor(
          @inject(EXTERNAL_A) private externalA: ExternalClassA,
     ) {}

     public setup(b: string): void {
          this.b = b;
     }

}

通常在ContainerModule内设置容器时,您需要执行以下操作:

 bind<MyClass>("MyClass").to(MyClass);

 container.bind<MyInterface>(CLASS_A).toConstantValue(
      const myClass = context.container.get<MyClass>("MyClass");
      return myClass.setup("a");
 );

 container.bind<MyInterface>(CLASS_B).toConstantValue(
     const myClass = context.container.get<MyClass>("MyClass");
     return myClass.setup("a");
 );

然后使用它:

 class MyOtherClass {

      constructor(
          @inject(CLASS_A) private _myInterfaceInstanceA: MyInterface,
          @inject(CLASS_B) private _myInterfaceInstanceB: MyInterface) {

      }

 }

希望它对您有帮助。您可以在https://github.com/inversify/InversifyJS/issues/530

上找到有关此信息的更多信息。

答案 1 :(得分:0)

您可以使用toFactory将工厂绑定到逆容器。请注意,您的工厂可以接受参数。

请参见https://github.com/inversify/InversifyJS/blob/master/wiki/factory_injection.md

以下是您的用例的具体示例。

container
    .bind<(b: string) => MyClass>("MyClassFactory")
    .toFactory<MyClass>((context: interfaces.Context) => {
      return (b: string) => {
        const classA = context.container.get<ExternalClassA>("EXTERNAL_A")

        return new MyClass(classA, b)
      }
    })

因此,我们将标识符"MyClassFactory"绑定到一个函数,该函数根据您传递的args返回MyClass的实例。

由于我们是通过inverseify从容器中获得EXTERNAL_A的,因此我们不会自己实例化它,因此我们不必担心它本身具有什么依赖性。

要使用您的工厂...

@injectable()
class SomeClass {
  @inject("MyClassFactory") private myClassFactory!: (b: string) => MyClass

  someMethod() {
    const myClassA = this.myClassFactory('a')
    const myClassB = this.myClassFactory('b')
  }
} 

要注意的一件事是,在这个问题中,您正在使用toConstantValue,因此您的类被构造并绑定为单例。我不知道这是否是故意的,但如果是这样,您仍然可以使用上面的工厂来做到这一点...

container.bind<MyClass>("CLASS_A").toDynamicValue((context: interfaces.Context) => { 
  const myClassFactory = context.container.get<(b: string) => MyClass>("MyClassFactory")
  return myClassFactory('a')
})

container.bind<MyClass>("CLASS_B").toDynamicValue((context: interfaces.Context) => { 
  const myClassFactory = context.container.get<(b: string) => MyClass>("MyClassFactory")
  return myClassFactory('b')
})

您也可以通过编程方式创建单例。如果您有一些{identifier: "CLASS_A", factoryArg: 'a'}类型的对象数组,则可以对其进行循环并如上所述创建动态值。

关于您的上一个问题,我的答案现在可能太长了,但是请查看文档中的本节,可能会有所帮助! https://github.com/inversify/InversifyJS/blob/master/wiki/recipes.md#overriding-bindings-on-unit-tests