網站搜尋

typescript - Inversify 部分注入


如何使用 Inversify 進行部分屬性注入?

假設我們有一個類

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

如果在編譯時已知 b 的所有可能值,我如何在其他類中使用這個 MyClass 與 inversify。所以,我只需要 MyClass 的兩個實例,一個 b="a",另一個 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。我該如何實現這個目標?

答案#1

您可以使用 toFactory 將工廠綁定到您的 inversify 容器。請注意,您的工廠可以接受參數。

請參見 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)
      }
    })

所以我們將一個根據你傳入的參數返回 MyClass 實例的函數,綁定到標識符 "MyClassFactory" 上。

由於我們是透過 inversify 從容器中獲取 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

答案#2

嗨,

恐怕沒有完美的解決方案。在這些情況下,我通常會使用帶有 set 或 setup 方法的工廠模式。

範例:

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