オタク時間-デザインパターンの美しさ理論10:制御の反転、依存関係の反転、依存関係の注入3つの違いと関係は何ですか?

依存関係の逆転の原則

●「依存関係の逆転」の概念は、「誰と誰」の「どのような依存関係」が逆転したかを指しますか?「リバース」という言葉を理解するには?

●他に、「制御の反転」と「依存関係の注入」という2つの概念をよく耳にします。これら2つの概念と「依存関係の逆転」の違いと関係は何ですか?彼らは同じことについて話しているのですか?

●Java言語に精通している場合、SpringフレームワークのIOCはこれらの概念とどのような関係がありますか。

制御の反転(IOC)

「依存性反転原理」について説明する前に、まず「制御の反転」について説明しましょう。Inversion Of Controlの英語訳は、Inversion Of Controlであり、IOCと略されます。ここで強調したいのは、Javaエンジニアの場合、当面はこの「IOC」をSpringフレームワークのIOCに関連付けないでください。SpringのIOCについては、後で説明します。

例を見て、制御の反転とは何かを見てみましょう。


public class UserServiceTest {
    
    
  public static boolean doTest() {
    
    
    // ... 
  }
  
  public static void main(String[] args) {
    
    //这部分逻辑可以放到框架中
    if (doTest()) {
    
    
      System.out.println("Test succeed.");
    } else {
    
    
      System.out.println("Test failed.");
    }
  }
}

上記のコードでは、すべてのプロセスがプログラマーによって制御されています。次のようなフレームワークを抽象化する場合、フレームワークを使用して同じ機能を実現する方法を見てみましょう。具体的なコードの実装は次のとおりです。


public abstract class TestCase {
    
    
  public void run() {
    
    
    if (doTest()) {
    
    
      System.out.println("Test succeed.");
    } else {
    
    
      System.out.println("Test failed.");
    }
  }
  
  public abstract boolean doTest();
}

public class JunitApplication {
    
    
  private static final List<TestCase> testCases = new ArrayList<>();
  
  public static void register(TestCase testCase) {
    
    
    testCases.add(testCase);
  }
  
  public static final void main(String[] args) {
    
    
    for (TestCase case: testCases) {
    
    
      case.run();
    }
  }

この簡略化されたバージョンのテストフレームワークをプロジェクトに導入した後、フレームワークによって予約された拡張ポイントに特定のテストコードを入力するだけで済みます。これは、TestCaseクラスのdoTest()抽象関数であり、前の関数を実現します。プロセスの実行を担当するmain()関数を作成する必要はまったくありません。具体的なコードは次のとおりです。


public class UserServiceTest extends TestCase {
    
    
  @Override
  public boolean doTest() {
    
    
    // ... 
  }
}

// 注册操作还可以通过配置的方式来实现,不需要程序员显示调用register()
JunitApplication.register(new UserServiceTest();

先ほど引用した例は、フ​​レームワークを介して「制御の反転」を実装する典型的な例です。フレームワークは、オブジェクトをアセンブルし、実行プロセス全体を管理するための拡張可能なコードスケルトンを提供します。プログラマーが開発にフレームワークを使用する場合、予約された拡張ポイントに自分のビジネスに関連するコードを追加するだけで済み、フレームワークを使用してプログラムフロー全体の実行を促進できます。

ここで、「制御」とは、プログラム実行フローの制御を指し、「反転」とは、フレームワークが使用される前のプログラム全体の実行に対するプログラマー自身の制御を指します。フレームワークを使用した後、プログラム全体の実行フローをフレームワークで制御できます。プロセスの制御は、プログラマーからフレームワークに「逆」になっています。

実際、制御の反転を実現する方法はたくさんありますが、今例のテンプレートデザインパターンと同様の方法に加えて、依存関係注入などの方法もありますので、制御の反転は一種ではありません。特定の実装スキルは比較的一般的な設計アイデアであり、フレームワークレベルの設計をガイドするために一般的に使用されます。

ディペンデンシーインジェクション(DI)

次に、依存関係の注入を見てみましょう。依存関係の注入は、制御の反転の反対であり、特定のコーディング手法です。依存関係インジェクションの英語訳は、依存関係インジェクションであり、DIと略されます。この概念には、非常に鮮明なステートメントがあります。つまり、依存関係の注入は、25ドルの値札を持つ概念であり、実際には5セントの価値しかありません。言い換えれば、この概念は非常に「背が高い」ように聞こえますが、実際には、理解して適用するのは非常に簡単です。

では、依存関係の注入とは正確には何ですか?要約すると、1つの文を使用します。new()を使用してクラス内に依存クラスオブジェクトを作成する代わりに、外部に依存クラスオブジェクトを作成した後、コンストラクター、関数パラメーターなどを介してそれらを渡します(または挿入します)。クラスの使用。

例を通して説明しましょう。この例では、Notificationクラスがメッセージのプッシュを担当し、MessageSenderクラスに依存して、製品のプロモーション、検証コード、およびその他のメッセージをユーザーにプッシュします。それを達成するために依存性注入と非依存性注入を使用しましょう。具体的な実装コードは次のとおりです。


// 非依赖注入实现方式
public class Notification {
    
    
  private MessageSender messageSender;
  
  public Notification() {
    
    
    this.messageSender = new MessageSender(); //此处有点像hardcode
  }
  
  public void sendMessage(String cellphone, String message) {
    
    
    //...省略校验逻辑等...
    this.messageSender.send(cellphone, message);
  }
}

public class MessageSender {
    
    
  public void send(String cellphone, String message) {
    
    
    //....
  }
}
// 使用Notification
Notification notification = new Notification();

// 依赖注入的实现方式
public class Notification {
    
    
  private MessageSender messageSender;
  
  // 通过构造函数将messageSender传递进来
  public Notification(MessageSender messageSender) {
    
    
    this.messageSender = messageSender;
  }
  
  public void sendMessage(String cellphone, String message) {
    
    
    //...省略校验逻辑等...
    this.messageSender.send(cellphone, message);
  }
}
//使用Notification
MessageSender messageSender = new MessageSender();
Notification notification = new Notification(messageSender);

依存クラスオブジェクトは、依存関係の挿入によって渡されます。これにより、コードのスケーラビリティが向上し、依存クラスを柔軟に置き換えることができます。この点については、先ほど「開閉の原則」についてお話しました。もちろん、上記のコードにはさらに最適化の余地があります。プログラミングを実装する代わりに、インターフェイスに基づいてMessageSenderをインターフェイスとして定義することもできます。変換されたコードは次のとおりです。


public class Notification {
    
    
  private MessageSender messageSender;
  
  public Notification(MessageSender messageSender) {
    
    
    this.messageSender = messageSender;
  }
  
  public void sendMessage(String cellphone, String message) {
    
    
    this.messageSender.send(cellphone, message);
  }
}

public interface MessageSender {
    
    
  void send(String cellphone, String message);
}

// 短信发送类
public class SmsSender implements MessageSender {
    
    
  @Override
  public void send(String cellphone, String message) {
    
    
    //....
  }
}

// 站内信发送类
public class InboxSender implements MessageSender {
    
    
  @Override
  public void send(String cellphone, String message) {
    
    
    //....
  }
}

//使用Notification
MessageSender messageSender = new SmsSender();
Notification notification = new Notification(messageSender);

実際、上記の例をマスターするだけで済みます。これは、依存関係の注入を完全にマスターすることと同じです。依存関係の挿入は非常に単純ですが、非常に便利です。後の章では、テスト可能なコードを作成するための最も効果的な方法として、依存関係の挿入について説明します。

依存関係インジェクションフレームワーク(DIフレームワーク)

「依存性注入」とは何かを理解した後、「依存性注入フレームワーク」とは何かを見てみましょう。説明のために、今でも例を借りています。

依存関係の挿入によって実装されたNotificationクラスでは、ハードコードのようなアプローチを使用して、クラス内の新しいものを介してMessageSenderオブジェクトを作成する必要はありませんが、オブジェクトの作成とアセンブル(または挿入)の作業は次の場所に移動します。高レベルのコードはただであり、それを実装するにはプログラマーが必要です。具体的なコードは次のとおりです。


public class Demo {
    
    
  public static final void main(String args[]) {
    
    
    MessageSender sender = new SmsSender(); //创建对象
    Notification notification = new Notification(sender);//依赖注入
    notification.sendMessage("13918942177", "短信验证码:2346");
  }
}

実際のソフトウェア開発では、プロジェクトによっては数十、数百、さらには数百のクラスが含まれる場合があり、クラスオブジェクトの作成と依存関係の挿入は非常に複雑になります。作業のこの部分がプログラマーが独自のコードを作成することによって行われる場合、エラーが発生しやすく、開発コストが比較的高くなります。オブジェクトの作成と依存関係の挿入の作業は、特定のビジネスとは関係ありません。フレームワークに抽象化して、自動的に完了することができます。

ご想像のとおり、このフレームワークは「依存関係注入フレームワーク」です。依存関係インジェクションフレームワークが提供する拡張ポイントを使用するだけで、作成する必要のあるすべてのクラスオブジェクト、クラスとクラス間の依存関係を構成するだけで、フレームワークは自動的にオブジェクトを作成し、オブジェクトのライフサイクルを管理し、依存関係インジェクションなどを行うことができます。もともとプログラマーにやらなければならなかったこと。

実際、Google Guice、Java Spring、Pico Container、Butterfly Containerなど、多くの既存の依存関係インジェクションフレームワークがあります。ただし、Java Springフレームワークに精通している場合は、Spring Framework自体がコントロールコンテナの反転(コントロールコンテナ反転)であると主張していると言うかもしれません

実際、どちらの記述も間違っていません。制御コンテナの反転の表現が非常に広い説明であり、DI依存関係注入フレームワークの表現がより具体的で対象を絞っているだけです。先に述べたように、依存関係の注入に加えて、制御の反転を実現する方法はたくさんあり、テンプレートパターンなどがあり、Springフレームワークの制御の反転は主に依存関係の注入によって実現されます。ただし、この区別はあまり明白ではなく、それほど重要ではありません。少し理解する必要があります。

依存関係反転原理(DIP)

制御の反転、依存関係の挿入、および依存関係の挿入のフレームワークについて説明しました。次に、今日の主役である依存関係の反転の原則について説明します。Dependency Inversion Principleの英語訳は、Dependency Inversion Principleであり、DIPと略されます。中国の翻訳は、依存関係の逆転の原則と呼ばれることもあります。

起源をたどるために、私は最初にこの原則の最も独創的な英語の説明を与えます:

高レベルのモジュールは、低レベルのモジュールに依存するべきではありません。両方のモジュール
は抽象化に依存する必要があります。さらに、抽象化は
詳細に依存するべきではありません詳細は抽象化によって異なります。

これを中国語に翻訳します。つまり、大まかに言うと、高レベルのモジュールは低レベルのモジュールに依存しません。高レベルのモジュールと低レベルのモジュールは、抽象化を通じて相互に依存する必要があります。さらに、抽象化は特定の実装の詳細(詳細)に依存するべきではなく、特定の実装の詳細(詳細)は抽象化に依存します。

高レベルモジュールと低レベルモジュールのいわゆる分割は、コールチェーンでは、呼び出し元が高レベルに属し、呼び出し先が低レベルに属するということです。通常のビジネスコード開発では、低レベルのモジュールに依存する高レベルのモジュールに問題はありません。実際、この原則は、前述の制御の反転と同様に、主にフレームワークレベルの設計をガイドするために使用されます。説明する例として、TomcatServletコンテナを取り上げましょう。

Tomcatは、JavaWebアプリケーションを実行するためのコンテナーです。私たちが書いたWebアプリケーションコードは、Tomcatコンテナの下にデプロイするだけでよく、Tomcatコンテナによって呼び出されて実行されます。前の分割原則によれば、Tomcatは高レベルのモジュールであり、私たちが作成するWebアプリケーションコードは低レベルのモジュールです。Tomcatとアプリケーションコードの間に直接の依存関係はありません。どちらも、サーブレット仕様である同じ「抽象化」に依存しています。サーブレットの仕様は、特定のTomcatコンテナとアプリケーションの実装の詳細に依存しませんが、Tomcatのコンテナとアプリケーションはサーブレットの仕様に依存します。

キーレビュー

1.制御の反転

実際、制御の反転は、特定の実装方法ではなく、比較的一般的な設計アイデアであり、一般に、フレームワークレベルの設計をガイドするために使用されます。ここでいう「制御」とは、プログラム実行フローの制御を指し、「反転」とは、フレームワークを使用する前に、プログラマー自身がプログラム全体の実行を制御することを指します。フレームワークを使用した後、プログラム全体の実行フローはフレームワークによって制御されます。プロセスの制御は、プログラマーからフレームワークに「逆」になります。

2.依存関係の注入

依存関係の注入と制御の反転は反対であり、特定のコーディング手法です。新しいメソッドを使用してクラス内に依存クラスオブジェクトを作成することはありませんが、依存クラスオブジェクトを外部で作成した後、コンストラクターや関数パラメーターなどを介して使用するためにクラスに渡されます(または挿入されます)。

3.依存関係インジェクションフレームワーク

依存関係インジェクションフレームワークが提供する拡張ポイントを通じて、必要なすべてのクラスとクラス間の依存関係を構成するだけで、フレームワークは自動的にオブジェクトを作成し、オブジェクトのライフサイクル、依存関係インジェクションなどを管理できます。やる事。

4.依存関係の逆転の原則

依存関係の反転の原則は、依存関係の反転の原則とも呼ばれます。この原則は、制御の反転に似ており、主にフレームワークレベルの設計をガイドするために使用されます。高レベルのモジュールは低レベルのモジュールに依存せず、すべて同じ抽象化に依存します。抽象化は特定の実装の詳細に依存するべきではありません。特定の実装の詳細は抽象化に依存します。

おすすめ

転載: blog.csdn.net/zhujiangtaotaise/article/details/110439769