invert
またはcontrol
という単語は、私が見た定義で制御の反転を定義するためにまったく使用されていません。
ウィキペディア
制御の反転(IoC)はプログラミング手法であり、ここではオブジェクト指向プログラミングの観点から表現されます。オブジェクトの結合は、実行時にアセンブラーオブジェクトによってバインドされ、通常、静的分析を使用したコンパイル時にはわかりません。 〜 http://en.wikipedia.org/wiki/Inversion_of_control
マーティン・ファウラー
制御の反転は、Javaコミュニティの一般的なパターンであり、軽量コンテナーをワイヤリングしたり、さまざまなプロジェクトのコンポーネントをまとまりのあるアプリケーションに組み立てたりするのに役立ちます。〜基づいてhttp://www.martinfowler.com/articles/injection.html(言い換え)
では、なぜInversion of ControlはInversion of Controlと呼ばれるのですか?どのコントロールが何によって反転されますか?用語を使用して制御の反転を定義する方法はありますか:invertおよびcontrol?
ある種の「リポジトリ」クラスがあり、そのリポジトリがデータソースからデータを渡す責任があるとします。
リポジトリは、それ自体でデータソースへの接続を確立できます。しかし、リポジトリのコンストラクタを介してデータソースへの接続を渡すことができるとしたらどうでしょう。
呼び出し元が接続を提供できるようにすることで、データソース接続の依存関係をリポジトリクラスから切り離し、リポジトリが指定するものだけでなくanyデータソースがリポジトリを操作できるようにします。
リポジトリクラスから呼び出し元に接続を作成する責任を負うことにより、inverted controlがあります。
Martin Fowlerは、「依存性注入」という用語を使用してこのタイプの制御の反転を説明することをお勧めします。なぜなら、コンストラクターメソッドに依存性を注入するだけではなく、概念としての制御の反転を適用できるからです。
マーティンファウラーよりもさらに詳しく説明できる人はいないと思います。 リンクした記事 。
この新しい種類のコンテナーの場合、反転は、プラグイン実装のルックアップ方法です。私の素朴な例では、リスターはFinderの実装を直接インスタンス化することでそれを調べました。これにより、Finderがプラグインでなくなる。これらのコンテナーが使用するアプローチは、プラグインのすべてのユーザーが、別個のアセンブラーモジュールが実装をリスターに挿入できるようにするいくつかの規則に従うことを保証することです。
上記の段落で説明したように、これは「制御の逆転」という用語が生まれた理由とまったく同じではありません。
これらのコンテナーが「制御の反転」を実装しているためにこれらのコンテナーがいかに有用であるかについて話すとき、私は非常に困惑してしまいます。制御の反転はフレームワークの一般的な特性です。したがって、これらの軽量コンテナーは制御の反転を使用しているため特別であると言うことは、私の車には車輪があるため特別であると言うようなものです。
問題は、コントロールのどの側面が反転しているかということです。私が最初に制御の逆転に遭遇したとき、それはユーザーインターフェイスのメインコントロールにありました。初期のユーザーインターフェイスは、アプリケーションプログラムによって制御されていました。 「名前を入力」、「アドレスを入力」のような一連のコマンドがあります。あなたのプログラムはプロンプトを駆動し、それぞれへの応答をピックアップします。グラフィカル(または画面ベースの)UIの場合、UIフレームワークにはこのメインループが含まれ、プログラムは代わりに画面上のさまざまなフィールドのイベントハンドラーを提供します。プログラムのメインコントロールが逆になり、フレームワークに移動しました。
これが、彼が「依存性注入」という用語を作成して、制御の反転のこの特定の実装をカバーする理由です。
結果として、このパターンにはもっと具体的な名前が必要だと思います。制御の逆転は一般的すぎる用語なので、人々は混乱を招きます。その結果、さまざまなIoC擁護者と多くの議論を重ねた結果、依存性注入という名前を決めました。
少し明確にするために:制御の反転とは、プログラムの制御構造を従来の手続き型設計から反転させることを意味します。
昔、この重要な例は、UIを直接生成するためにコードを離れるのではなく、フレームワークにUIとコード間の通信を処理させることでした。
最近(そのようなフレームワークがかなり支配的であったため、問題はもはや関連していなかった)、オブジェクトのインスタンス化に対する制御を逆転させた例がありました。
ファウラーなどは、「制御の反転」という用語が多すぎる技術をカバーしていると判断し、オブジェクトのインスタンス化の特定の例(依存性注入)には新しい用語が必要でしたが、その合意がなされるまでに、「IoCコンテナー「離陸していた。
IoCコンテナは特定の種類の依存性注入ですが、依存性注入は特定の種類の制御の反転であるため、これは水を多く濁らせます。これが、どこを見ても混乱した答えを得る理由です。
以下は、通常に従う「通常の」制御フロープログラムです。
制御の反転は、その制御フローを「反転」します。つまり、制御を逆にします。
その最後の行は重要です。あなたがそれを感じたときに誰かに電話をかけるのではなく、誰かが彼らがそれを感じたときにあなたに電話をかけます。
この一般的な例は、RailsなどのWebフレームワークです。コントローラーを定義しますが、コントローラーが呼び出されるタイミングを実際に決定することはありません。 Rails必要があると判断すると、Railsはそれらを呼び出します。
依存関係のインスタンス化を誰が制御するかについてです。
従来、クラス/メソッドが別のクラス(依存関係)を使用する必要がある場合、クラス/メソッドによって直接インスタンス化されます。依存関係を制御します。
制御の反転(IoC)では、依存関係でcallerが渡されるため、依存関係がインスタンス化されます。呼び出し元は依存関係を制御します。
依存関係がインスタンス化される場所の制御が逆になりました。依存関係を必要とするコードが存在する「ボトム」ではなく、依存関係を必要とするコードが呼び出される「トップ」でインスタンス化されます。
通常、より高いレベルのコードは、より低いレベルのコードを呼び出します(つまり、コントロールします)。 Main()はfunction()を呼び出し、function()はlibraryFunction()を呼び出します。
これは逆にすることができるので、下部の低レベルライブラリ関数は高レベル関数を呼び出します。
どうしてそうするか?ミドルウェア。場合によっては、トップレベルとボトムレベルを制御する必要がありますが、途中でやりたくない多くの作業があります。 C stdlibでのクイックソートの実装 。トップレベルでクイックソートを呼び出します。 qsort()に、好きなようにコンパレーターを実装する独自の関数への関数ポインターを渡します。 qsort()が呼び出されると、このコンパレーター関数が呼び出されます。 qsort()は、高水準関数を制御/呼び出し/駆動しています。
簡単な概要は次のとおりです。
昔は、コントロールは最初にアプリケーションが所有し、次にユーザーが所有していました。アプリケーションがユーザーから何かを必要とする場合、アプリケーションは停止して要求し、次のタスクに進みます。ユーザーの対話は、アプリケーションが次に行うことを制御するのではなく、ほとんどの場合データを提供しました。このタイプの行動はあまり見られないため、これは最近の私たちにとって少し異質なものです。
これを切り替えてユーザーにプライマリコントロールを与えると、コントロールが反転します。これは、ユーザーがアプリケーションが何かをするのを待つのではなく、アプリケーションはユーザーが何かをするのを待つのを待つことを意味します。 GUIはこの良い例であり、イベントループを持つほとんどすべてのコントロールが逆になります。
私の例は最上位にあり、制御の反転のこの概念は、アプリケーション内の制御のさまざまな層(つまり、依存性注入)に抽象化できることに注意してください。これが正解を得るのが難しい理由かもしれません。
2つの例を通してそれを理解してみましょう。
例1
以前は、ユーザー入力を次々に受け入れるコマンドプロンプトを生成するためにアプリが使用されていました。今日、UIフレームワークはさまざまなUI要素をインスタンス化し、それらのUI要素のさまざまなイベント(マウスホバー、クリックなど)をループし、ユーザー/メインプログラムはそれらのイベントをリッスンするためのフック(JavaのUIイベントリスナーなど)を提供します。 したがって、メインのUI要素フローの「コントロール」は、ユーザープログラムからUIフレームワークに移動します。以前はユーザープログラムにありました。
例2
以下のクラスCustomerProcessor
を検討してください:
_class CustomerProcessor
{
SqlCustRepo custRepo = new SqlCustRepo();
private void processCustomers()
{
Customers[] custs = custRepo.getAllCusts();
}
}
_
SqlCustRepo
によって提供されるものだけでなく、processCustomer()
をgetAllCusts()
の実装から独立させたい場合は、次の行を削除する必要があります:SqlCustRepo custRepo = new SqlCustRepo()
を置き換えて、さまざまなタイプの実装を受け入れることができる、より一般的なものに置き換えます。これにより、processCustomers()
は、提供された実装で機能するようになります。上記のコード(メインプログラムロジックによって必要なクラスSqlCustRepo
をインスタンス化する)は従来の方法であり、processCustomers()
の実装からgetAllCusts()
を分離するというこの目標を達成しません。制御の反転では、コンテナーは必要な実装クラス(たとえば、xml構成で指定されたもの)をインスタンス化し、指定されたフック(たとえば、_@Autowired
_アノテーションまたはgetBean()
Springフレームワークのメソッド)。
これを行う方法を見てみましょう。以下のコードを検討してください。
Config.xml
_<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="custRepo" class="JsonCustRepo" />
</beans>
_
CustRepo.Java
_interface ICustRepo
{ ... }
_
JsonCustRepo.Java
_class JsonCustRepo implements CustRepo
{ ... }
_
App.Java
_class App
{
public static void main(String[] args)
{
ApplicationContext context = new ClassPathXmlApplicationContext("Config.xml");
ICustRepo custRepo = (JsonCustRepo) context.getBean("custRepo");
}
}
_
私たちも持つことができます
_class GraphCustRepo implements ICustRepo { ... }
_
そして
_<bean id="custRepo" class="GraphCustRepo">
_
app.Javaを変更する必要はありません。
コンテナー(Springフレームワーク)の上には、xmlファイルをスキャンし、特定のタイプのBeanをインスタンス化し、それをユーザープログラムに挿入する責任があります。ユーザープログラムは、インスタンス化されるクラスを制御できません。
PS:IoCは一般的な概念であり、さまざまな方法で実現されます。上記の例では、依存性注入によってそれを実現しています。
参照: Martin Fowlerの記事 。