制御の反転
制御の反転(Inversion of Control、IoC)は抽象化原則の一種で、手続き型プログラミングと比較したとき、システムの制御の流れが逆転しているソフトウェアアーキテクチャ設計を表している。
従来からのプログラミングでは、フローはコードの中核部分で制御されている。IoCを使うと、これが全く変わってくる。呼び出し側は応答を得るが、いつどのようにして応答を得るかは呼び出し側が制御できない。逆に呼び出された側がいつどのようにして応えるかを決定する。このため、制御の反転はハリウッドの原則 (Hollywood Principle) とも呼ばれる。設計ガイドラインとしてのIoCは以下のような目的で使われる。
- あるタスクの実行を実装から分離する場合。
- 全てのシステムが設計目的に集中できる場合。
- 全てのシステムが他のシステムが何をするかを仮定しない場合。
- システムの置換が他のシステムに全く副作用を与えないようにする場合。
例
public class ServerFacade {
public Object respondToRequest(Object pRequest) {
if (!businessLayer.validateRequest(pRequest)) {
return null;
}
DAO.getData(pRequest);
return aspect.convertData(pRequest);
}
}
この例は非常に恣意的に単純化してある。重要なのは、ServerFacade においてDAOオブジェクトがどういうデータを返してくるかについて、多くの仮定をしている点である。それらの仮定は全て妥当な場合もあるかもしれないが、ServerFacade と DAO の実装の結合度が高いことは否めない。「制御の反転」に従った設計では、制御を完全にDAOオブジェクトに渡してしまう。するとコードは次のようになる。
public class ServerFacade {
public Object respondToRequest(Object pRequest) {
return DAO.getData(pRequest);
}
}
この場合、両方のオブジェクトは互いが何をするかについて全く仮定を設けていない。メソッドを呼び出す側とメソッドの中身を提供する側は知っている必要があるが、serverFacade は知る必要がない。これも制御の反転の重要な効果の1つである。ServerFacade は純粋に設計されたタスクを実行する(おそらく、なんらかのシステムの接続)。
背景
「制御の反転」は計算機科学においては新しい用語ではない。マーティン・ファウラーによれば[1]、"Inversion of Control”という用語の起源は1988年に遡る。この考え方は、階層による抽象化とよく似ている。しかし、「制御の反転」という用語は何か新しいものとして大々的に流布された。これは、そのような設計原則に開発者の目を惹きつけ、その重要性を再認識させた。Javaの世界ではこの用語が一定の認知を得た。また、特定のプログラミング言語に依存しないアーキテクチャを述べる際にも好んで使われる。
「制御の反転」がデザインパターンなのか、それともアーキテクチャの原則なのかは議論が分かれている。Shivprasad Koirala の記事では[2]、「制御の反転」はデザインパターンとしての「依存性の注入」と組み合わせて語られており、そこでは依存性の注入がデザインパターンで、制御の反転は依存性の注入を使って実装されるとしている。一方 Mani Malarvannan の記事では[3]、「制御の反転」は文脈化された参照 (contextualized lookup) を使ったデザインパターンとして紹介されている。サービスロケータを使ったものも同じデザインパターンだとされている。
Robert C. Martin の記事 "The Dependency Inversion Principle" では[4]、階層による抽象化と共に論じている。彼がここで "inversion" という言葉を使ったのは、従来のソフトウェア開発手法との対比のためであった。彼が "Dependency Inversion" と呼んでいるのは、階層による抽象化でサービス群を分離することである。抽象化層を作る際には、システムの境界がどこにあるかを知ることが重要である。
「制御の反転」は「依存性の注入」と密接に関連している。依存性の注入は「制御の反転」を実装する主な手法である。
実装技法
実装技法は使用するプログラミング言語によって異なる。以下はJavaでIoCを実装する際の概要である。JavaにはIoC実装に利用できる6つの技法がある。
- Factory パターンを使用する。
- サービスロケータを使用する。
- コンストラクタ注入を使用する。
- セッター注入を使用する。
- インタフェース注入を使用する。
- 文脈化された参照 (contextualized lookup)
マーティン・ファウラーの最初の論文では[5]、上記の5番目までを論じていた。Inversion of Control types[6] では最後の技法も言及されている。通常、文脈化された参照はサービスロケータを使って実装される。技法よりも「制御の反転」をどういう目的で使うのかが重要である。「制御の反転」はこれらの技法に限ったものではない。
IoC の利点と欠点
「制御の反転」には他の抽象化技法と同じように利点と欠点がある。大まかに言えば、特定のタスクは単純化されるが、アプリケーションの協調動作はより複雑になる。デビッド・ホイーラーの有名な格言に「計算機科学の全ての問題は別のレベルへのインダイレクションで解決できる」という言葉がある[7]。この言葉の「インダイレクション」を「抽象化 (abstraction)」と間違って引用していることが多い。Kevlin Henney はこの格言の系として「…ただし、レイヤーが多すぎるせいで発生する問題を除く」という言葉もある。
同じことは「制御の反転」にも言える。Robert C. Martin の記事[4]にあるコードを例に説明すると、最終的なコードには5つのクラスが定義されているが、手続き型プログラミングならこれを1つのメソッド(ルーチン)で実装できる。「制御の反転」は2つの実装を互いに分離するという利点があるが、同時に全体として協調動作させるときに複雑さが増す。
関連項目
- 抽象化レイヤ
- 非同期I/O
- コールバック (情報工学)
- クロージャ
- 継続
- デリゲート (プログラミング)
- 割り込みハンドラ
- モナド (プログラミング)
- Observer パターン
- 出版-購読型モデル
- シグナル (ソフトウェア)
- Strategy パターン
- Visitor パターン
- XSLT - データ駆動型のスクリプト言語。入力データの内容によってどのテンプレート(メソッド)をどういう順序で実行するかが制御される。テンプレートは自動的に完了時に入力データに制御を戻すこともあるが、<xsl:apply-templates /> コマンドを使えばそれも制御できる。
脚注・出典
- ^ Inversion of Control on Martin Fowler's Bliki - Martin Fowler
- ^ Design pattern – Inversion of control and Dependency injection - Shivprasad Koirala
- ^ Design Better Software with the Inversion of Control Pattern - Mani Malarvannan
- ^ a b The Dependency Inversion principle - Robert C. Martin
- ^ Inversion of Control Containers and the Dependency Injection Pattern - Martin Fowler
- ^ IoC Types
- ^ Tanenbaum, Andrew S. (1979). Structured Computer Organization. Englewood Cliffs, New Jersey: Prentice-Hall. ISBN 0-13-148521-0