制御の反転

出典: フリー百科事典『ウィキペディア(Wikipedia)』
移動: 案内検索

ソフトウェア工学において、制御の反転(Inversion of Control、IoC)とは、コンピュータ・プログラムの中で、個別の目的のために書かれたコード部分が、一般的で再利用可能なライブラリによるフロー制御を受ける形の設計を指す。この設計を採用した ソフトウェアアーキテクチャは、伝統的な手続き型プログラミングと比べると制御の方向が反転している。すなわち、従来の手続き型プログラミングでは、個別に開発するコードが、そのプログラムの目的を表現しており、汎用的なタスクを行う場合に再利用可能なライブラリを呼び出す形で作られる。一方、制御を反転させたプログラミングでは、再利用可能なコードの側が、個別目的に特化したコードを制御する。

制御の反転は、プログラムのモジュール化を促進して、その拡張性を高めるために用いられ [1]オブジェクト指向プログラミングやその他のプログラミングパラダイムにおいて応用されている。「制御の反転」という用語は ロバート・マーティンマーティン・ファウラーによって広められた。この用語は依存性反転原則とは関係しているが異なるものである。依存性反転原則は、共有された抽象化を通じて、高次と低次の抽象化レイヤー間の結合度を下げることを示している。

従来からのプログラミングでは、フローはコードの中核部分で制御されている。IoCを使うと、これが全く変わってくる。呼び出し側は応答を得るが、いつどのようにして応答を得るかは呼び出し側が制御できない。逆に呼び出された側がいつどのようにして応えるかを決定する。

概要[編集]

従来式のプログラミングでは、例えば、あるアプリケーションのmain関数が、メニューライブラリ内の関数を呼び出して、利用可能なコマンドの一覧を表示し、その中のどれか一つの機能をユーザーに選ばせる[2]。するとそのライブラリはユーザが選んだ項目を、関数呼び出しに対する戻り値として返し、するとmain関数がその戻り値を使って、関係する命令を実行する。このスタイルはテキストユーザインタフェースでは一般的である。たとえば、電子メールクライアントであれば、スクリーン上に「新着メールを読み込む」、「このメールに返信する」、「新規メール作成」などのコマンドが表示されており、ユーザがいずれかのコマンドを選択するまで、プログラムの実行はブロックされた状態になる。

一方、制御の反転を使うと、このプログラムは、汎用的な振舞いやグラフィック要素を持っているソフトウェアフレームワークを使って書かれることになるだろう。そうしたフレームワークには、たとえばウィンドウシステムや、メニュー、マウス制御などが既に組み込まれている。個別に開発するコードは、フレームワークの「空白部分を埋める」ものになる。たとえば、メニュー項目の一覧を与えるとか、それぞれのメニュー項目に対応するサブルーチンを登録するといったものだ。一方、ユーザの操作を監視していて、ユーザがメニュー項目を選択したときに、それに対応するサブルーチンを呼び出すのはフレームワークの側だ。メールクライアントの例で言えば、フレームワークはキーボードとマウスの両方の入力を追う事が出来ていて、いずれかの方法によりユーザがコマンドを実行した場合に、その命令を呼び出す。それと同時に、新着メッセージがないかどうかを見るためにネットワーク・インターフェイスも監視しており、何らかのネットワーク活動を検知した場合には画面を更新する。それと同じフレームワークが、表計算プログラムやテキストエディターの骨組にも利用できる。逆に、フレームワークは電子メールクライアントや、表計算や、テキストエディターについては何も知らない。それらの機能の実現には個別に実装されたコードを使っているからである。

制御の反転ということは、再利用可能なコードと、個別目的のためのコードは、たとえそれらが一つのアプリケーションの中で一緒に動くものであるとしても、それぞれ独立したものとして開発されるということを暗黙的に言っている。ソフトウェアフレームワークコールバックスケジューライベントループ依存性の注入は、制御の反転の原則に従ったデザインパターンの例である。しかし、制御の反転という用語は、オブジェクト指向プログラミングの文脈の中で最もよく使われる。

制御の反転は以下のような設計目的のために使われる:

  • あるタスクの実行を実装から分離するため
  • あるモジュールを、目的とするタスクだけに集中させるため
  • モジュールを作る際に、他のシステムが何をどのようにするかについて仮定しながらコーディングすることから解放し、そのかわり契約に依拠してコーディングするため(契約プログラミング
  • モジュールを置き換える際の副作用を予防するため

制御の反転は冗談として時々「ハリウッドの原則」と呼ばれることもある。つまり「君の方から連絡してこないで。君が必要な時はこっちから連絡するから」ということである。

背景[編集]

「制御の反転」は計算機科学においては新しい用語ではない。マーティン・ファウラーによれば[3]、"Inversion of Control”という用語の起源は1988年に遡る。この考え方は、階層による抽象化とよく似ている。しかし、「制御の反転」という用語は何か新しいものとして大々的に流布された。これは、そのような設計原則に開発者の目を惹きつけ、その重要性を再認識させた。Javaの世界ではこの用語が一定の認知を得た。また、特定のプログラミング言語に依存しないアーキテクチャを述べる際にも好んで使われる。

「制御の反転」がデザインパターンなのか、それともアーキテクチャの原則なのかは議論が分かれている。Shivprasad Koirala の記事では[4]、「制御の反転」はデザインパターンとしての「依存性の注入」と組み合わせて語られており、そこでは依存性の注入がデザインパターンで、制御の反転は依存性の注入を使って実装されるとしている。一方 Mani Malarvannan の記事では[5]、「制御の反転」は文脈化された参照 (contextualized lookup) を使ったデザインパターンとして紹介されている。サービスロケータを使ったものも同じデザインパターンだとされている。

Robert C. Martin の記事 "The Dependency Inversion Principle" では[6]、階層による抽象化と共に論じている。彼がここで "inversion" という言葉を使ったのは、従来のソフトウェア開発手法との対比のためであった。彼が "Dependency Inversion" と呼んでいるのは、階層による抽象化でサービス群を分離することである。抽象化層を作る際には、システムの境界がどこにあるかを知ることが重要である。

「制御の反転」は「依存性の注入」と密接に関連している。依存性の注入は「制御の反転」を実装する主な手法である。

説明[編集]

従来のプログラミングでは、ビジネスロジックフロー制御は、互いに静的結合したオブジェクト群により決められる。制御の反転を使った場合、フロー制御はプログラム実行中に構築されたオブジェクト・グラフに依存して決まる。抽象化を媒介としてオブジェクト間の相互作用の関係を定義することによって、そのような動的なフロー制御が可能となっている。この実行時結合(遅延結合)は依存性の注入あるいはサービス・ロケータ・パターンのような仕組みにより実現される。但し、制御の反転を使う場合においても、コードを直接参照する代わりに外部の設定ファイルを読んで実行すべきコードを探す仕組みにするのであれば、コンパイル中にコードを静的に関連づけることはありうる。

依存性の注入において、依存する側のオブジェクトあるいはモジュールは、その依存先のオブジェクトと、実行時において結合される。特定のどのオブジェクトがプログラム実行中にその依存関係を満たすことになるのかは、静的コード解析を行うコンパイル時には知りようがない。ここではオブジェクト間の相互作用を題材に説明したが、この原則はオブジェクト指向プログラミング以外の他のプログラミング手法にもあてはまる。

実行中のプログラムでオブジェクトどうしを相互に結び付け合うためには、結び付けられるオブジェクトどうしが互換性のあるインターフェースを持っていなければならない。例えば、クラスAはその振舞いをインターフェースIに委任しており、IはクラスBにより実装されているとする。このようにしてあれば、プログラムはABをインスタンス化した上で、BAに注入できるのである。

実装技法[編集]

オブジェクト指向プログラミングでは、制御の反転を実装するには幾つかの基本的な技法がある。それらを次に列挙する:

  • Factory パターンを使用する。
  • サービスロケータパターンを使用する。
  • 依存性の注入を使用する。たとえば、
    • コンストラクタ注入
    • パラメータ注入
    • セッター注入
    • インタフェース注入
  • テンプレートメソッドパターンを利用する
  • ストラテジーパターンを利用する
  • 文脈化された参照 (contextualized lookup)

マーティン・ファウラーの最初の論文では[7]、上記の3番目までを論じていた。制御の反転の種類に関する記述の中で[8] では最後の技法も言及されている。通常、文脈化された参照はサービスロケータを使って実装される。

技法よりも「制御の反転」をどういう目的で使うのかが重要である。「制御の反転」はこれらの技法に限ったものではない。

IoC の利点と欠点[編集]

「制御の反転」には他の抽象化技法と同じように利点と欠点がある。大まかに言えば、特定のタスクは単純化されるが、アプリケーションの協調動作はより複雑になる。デビッド・ホイーラーの有名な格言に「計算機科学の全ての問題は別のレベルへのインダイレクションで解決できる」という言葉がある[9]。この言葉の「インダイレクション」を「抽象化 (abstraction)」と間違って引用していることが多い。Kevlin Henney はこの格言の系として「…ただし、レイヤーが多すぎるせいで発生する問題を除く」という言葉もある。

同じことは「制御の反転」にも言える。Robert C. Martin の記事[6]にあるコードを例に説明すると、最終的なコードには5つのクラスが定義されているが、手続き型プログラミングならこれを1つのメソッド(ルーチン)で実装できる。「制御の反転」は2つの実装を互いに分離するという利点があるが、同時に全体として協調動作させるときに複雑さが増す。

[編集]

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. ^ Ralph E. Johnson & Brian Foote (June?July 1988). “Designing Reusable Classes”. Journal of Object-Oriented Programming, Volume 1, Number 2. Department of Computer Science University of Illinois at Urbana-Champaign. pp. 22?35. 2014年4月29日閲覧。
  2. ^ Dependency Injection.
  3. ^ Inversion of Control on Martin Fowler's Bliki - Martin Fowler
  4. ^ Design pattern – Inversion of control and Dependency injection - Shivprasad Koirala
  5. ^ Design Better Software with the Inversion of Control Pattern - Mani Malarvannan
  6. ^ a b The Dependency Inversion principle - Robert C. Martin
  7. ^ Inversion of Control Containers and the Dependency Injection Pattern - Martin Fowler
  8. ^ IoC Types
  9. ^ Tanenbaum, Andrew S. (1979). Structured Computer Organization. Englewood Cliffs, New Jersey: Prentice-Hall. ISBN 0-13-148521-0. 

外部リンク[編集]