Swiftで見るDI(依存性注入)とは?実装方法やサンプルコード
この記事からわかること
- DI(Dependency Injection:依存性注入)とは?
- Swiftで見るDIについて
- DIの種類
- 依存関係とは?
- Interface Injection/Constructor Injection/Setter Injectionとは?
index
[open]
\ アプリをリリースしました /
友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-
posted withアプリーチ
アプリ開発で「DI」と言う言葉に触れるようになり、実際に意識した開発を心掛けたいと思ったので自分なりにまとめていきたいと思います。
間違っている点や誤解を招く表現があるかも知れませんが、何かありましたら教えていただけると嬉しいです。
DI(Dependency Injection:依存性注入)とは?
DIはDependency Injectionの略称であり日本語では依存性注入という意味で、アプリ開発などのオブジェクト指向型のソフトウェア開発における基本的な考え方や設計原則を表現しパターン化したデザインパターンの1つになるようです。
デザインパターンにも様々な種類がありますがその中でもDIの特徴は「依存関係をオブジェクト外に持たせて外部から注入するような設計にする」です。
依存関係とは?
そもそも依存関係とはオブジェクト同士が依存している関係のことです。ここでいうオブジェクトはクラスや構造体、メソッドなども当てはまります。そして結合が強い状態のことを密結合と呼んだりします。
一度例を見てみます。以下にはUser
クラスとActionGame
クラスが定義されていますがここで依存関係が生まれています。
class User {
var stamina:Int = 20
func playGame(){
let actionGame = ActionGame()
if actionGame.requiredStamina <= self.stamina {
print("Playing")
stamina -= actionGame.requiredStamina
}else{
print("Out of Stamina...")
}
}
}
class ActionGame {
var requiredStamina:Int = 10
}
let user = User()
user.playGame()
print(user.stamina)
// Playing
// 10
Userクラスの中でActionGameクラスがインスタンス化されているため、UserクラスはActionGameクラスが存在しないとクラスとしての機能を保てません。これがまさしく依存している状態です。これでもコードは動作するし問題がないといえばそれまでです。
では新しくRacingGame
を増やすことを考えてみます。今のままだとUser
クラス内に新しくメソッドを追加するしかなくなってしまいます。これでは拡張性がありません。なのでprotocol
を使用して抽象的なオブジェクトを作り、それをUserクラスに注入するように変更してみます。
class User {
var stamina:Int = 20
func playGame(game: Game){
if game.requiredStamina <= self.stamina {
print("Playing")
stamina -= game.requiredStamina
}else{
print("Out of Stamina...")
}
}
}
class ActionGame: Game {
let requiredStamina:Int = 10
}
class RacingGame: Game {
let requiredStamina:Int = 5
}
protocol Game {
var requiredStamina:Int { get }
}
let user = User()
user.playGame(game: ActionGame())
user.playGame(game: RacingGame())
print(user.stamina)
// Playing
// Playing
// 5
依存がなくなったわけではないですが依存度はだいぶ軽減されたかと思います。これが完璧にDIのサンプルとして正しいわけではないと思いますがDIの雰囲気は掴めたかと思います。
導入するメリット
DIを導入すれば必ずメリットが生まれる訳ではありません。規模や環境によって導入するメリットがあるかないかは考える必要があるかと思います。そこはさておき、DIを導入することで生まれるメリットをまとめてみます。
メリット
- 結合度が低下(疎結合)
- コードの柔軟性と拡張性が向上
- 単体テストの効率化
オブジェクト同士の結合度が低下することで再利用性や拡張性がと高まります。また依存関係を外部から受け取る方式にすることでモックやスタブへの置き換えがしやすくなり、結果テストの効率性が向上します。
DIの種類
プログラムに依存性を注入する方法としてDIでは3種類の方法が挙げられています。
Interface Injection(インターフェイスインジェクション)
インターフェイス(プロトコル)を用意しそれに準拠させることで専用のインターフェースを介して注入。最初にやったのはこれに近いです。
Constructor Injection(コンストラクタインジェクション)
コンストラクタ(イニシャライザ)としてクラスをインスタンス化して注入。プロパティとして保持する
Setter Injection(セッターインジェクション) or Field Injection(フィールドインジェクション)
プロパティ内に変数を定義しておきsetterメソッド経由でインスタンス化して注入。オブジェクトのインスタンスが作成された後に依存関係を注入できる
DIを意識した開発
DIについて長々と色々な説明がありましたが、結局のところなんなのか分かりにくい部分が多いです。私的にとりあえず以下のポイントに気をつけていればDIを意識した開発(あくまで意識したです)ができると思います。
- オブジェクトは外部から受け取る
- オブジェクト内でインスタンス化をしない
この記事を書くにあたって wikipediaの記事を参考にしましたが意外と分かりやすかったです。
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。