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
