50573


# 参考ソース

<https://github.com/Swinject/Swinject>  
のREADMEに下記のソースがあります。  
（ライブラリにかかわらず、DIコンテナ特有の問題として語れると考えています）

[code]

    protocol Animal {
        var name: String? { get }
    }
    
    class Cat: Animal {
        let name: String?
    
        init(name: String?) {
            self.name = name
        }
    }
    
    protocol Person {
        func play()
    }
    
    class PetOwner: Person {
        let pet: Animal
    
        init(pet: Animal) {
            self.pet = pet
        }
    
        func play() {
            let name = pet.name ?? "someone"
            print("I'm playing with \(name).")
        }
    }
    
    
    let container = Container()
    container.register(Animal.self) { _ in Cat(name: "Mimi") }
    container.register(Person.self) { r in
        PetOwner(pet: r.resolve(Animal.self)!)
    }
    
    let person = container.resolve(Person.self)!
    person.play() // prints "I'm playing with Mimi."
    
[/code]

# PetOwnerは引数にAnimalをもらっているがもらう必要があるのだろうか？？

`PetOwner`はイニシャライザで`Animal`をもらっています。  
そのため `PetOwner(pet: r.resolve(Animal.self)!)`と外側から注入しています。  
これは冗長に感じます（確かにDIは外から注入するものなのかもしれないですが...）。

以下のように書き換えて内部で注入してしまえばいいように感じます。  
テストで注入するデータを入れ替えたいのであれば、テスト用の `register` を書けば注入されるインスタンスは変わるはずです。

[code]

    let container = Container() // アプリであれば、シングルトンなどとしてずっと生きているようにする
    
    protocol Animal {
        var name: String? { get }
    }
    
    class Cat: Animal {
        let name: String?
    
        init(name: String?) {
            self.name = name
        }
    }
    
    protocol Person {
        func play()
    }
    
    class PetOwner: Person {
        let pet: Animal
    
        init() {
            self.pet = container.resolve(Animal.self)! // 内部で注入
        }
    
        func play() {
            let name = pet.name ?? "someone"
            print("I'm playing with \(name).")
        }
    }
    
    
    container.register(Animal.self) { _ in Cat(name: "Mimi") }
    container.register(Person.self) { r in
        PetOwner()
    }
    
    let person = container.resolve(Person.self)!
    person.play() // prints "I'm playing with Mimi."
    
[/code]

## 質問

テストで注入するデータを入れ替えたいのであれば、テスト用の register を書けば注入されるインスタンスは変わるはずです。

コンテナから直接取り出せるのに、外側から（引数で）渡す（注入する）必要があるのでしょうか？


CC BY-SA 4.0
2018-11-09T10:24:08.970
50125
2019-06-29T08:35:16.943
2019-06-29T08:35:16.943
9008
9008
question
swift テスト dependency-injection swinject
DIコンテナがあるのに依存を外から注入する必要はあるのか？


「必要があるのか」という観点ではないように思います。  
DIコンテナを利用したとしてもイニシャライザでDIを行うことで、  
依存関係が明示的になるというメリットがあります。

DIコンテナ側の実装を見る限りでは、PetOwnerとAnimalの依存関係は暗黙的となり、  
PetOwnerの実装を見ない限り依存関係を把握することができません。


CC BY-SA 4.0
2018-11-23T12:59:50.220
50573
2018-11-23T13:05:27.860
2018-11-23T13:05:27.860
19889
19889
50125
answer