最近、私は常にIOC(コントロールの反転)、DI(依存噴射)などのプログラミング原則やパターンにさらされており、これらは有名なJavaフレームワークスプリング、ストラットなどの中核です。それらを読んだ後、私にはいくつかの理解があります。次に、本の説明と私自身の処理を組み合わせて、次のように整理します。
EG1
問題の説明:
毎日のレポート、毎月のレポートなど、さまざまな要件に従ってExcelまたはPDF形式でレポートを生成できるシステムを開発します。
解決:
「インターフェイス指向プログラミング」の原則に従って、インターフェイスと実装を分離する必要があります。つまり、レポートを生成する機能は一般的なインターフェイスレポートゲネレーターに抽出され、ExcelおよびPDF形式でExcelとPDFレポートを生成する2つの実装が提供されます。次に、クライアントは、サービスプロバイダーレポートサービスを介して対応するレポート印刷機能を取得します。
実装方法:
上記によると、次のクラス図が取得されます。
コード実装:
Interface ReportGenerator {public void generate(テーブルテーブル); } class excelgeneratorを実装したReportgenerator {public void generate(table table){system.out.println( "Excelレポートを生成..."); }} class pdfgeneratorを実装したReportgenerator {public void generate(table table){system.out.println( "PDFレポートを生成..."); }} class Reportservice {//特定のニーズのレポートジェネレーターを作成する責任プライベートレポートGenerator Generator = new PDFGenerator(); // private static ReportGenerator Generator = new Excelgenerator(); public void getDailyReport(日付){table.setDate(date); // ... generator.generate(表); } public void getMonthLyReport(月月){table.setmonth(月); // ... generator.generate(表); }} public class client {public static void main(string [] args){Reportservice Reportservice = new Reportservice(); Reportservice.getDailyReport(new Date()); //ReportService.getMonthLyReport(New Date()); }}
EG2
問題の説明:
上記のコードのコメントに示されているように、特定のレポートジェネレーターは、Reporterviceクラスで内部的にハードコードされているため、ReporterviceがPDFGeneratorまたはExcelgeneratorに直接依存しているため、この明らかな緊密な結合を排除する必要があります。
解決策:コンテナを導入して、レポートシステムやさまざまなXXGeneratorを含むレポートシステムに関係するオブジェクトを均一に管理する中間マネージャー、つまりコンテナ(コンテナ)を均一に管理します(コンテナ)。ここでは、これらの豆を保存するために、キー価値ペアの形のハッシュマップインスタンスを使用します。
実装方法:
クラス図は次のように取得されます。
コード実装:
クラスコンテナ{//キー値ペアに必要なさまざまなコンポーネントを保存しますbean private static map <string、object> bean; public container(){beans = new hashmap <string、object>(); //特定のレポートGenerator ReportGenerator ReportGenerator = new PDFGenerator(); Beans.put( "ReportGenerator"、ReportGenerator); // Reportservice Reportservice Reportservice = new Reportservice()の参照を取得して管理します。 Beans.put( "Reportservice"、Reportservice); } public staticオブジェクトgetBean(string id){return beans.get(id); }} class Reportservice {//タイトな結合関係を排除し、コンテナに置き換えます// private static ReportGenerator Generator = new PDFGenerator(); private ReportGenerator Generator Generator =(ReportGenerator)container.getBean( "ReportGenerator"); public void getDailyReport(日付){table.setDate(date); generator.generate(表); } public void getMonthLyReport(月月){table.setmonth(月); generator.generate(表); }} public class client {public static void main(string [] args){container container = new container(); Reportservice Reportservice =(Reportservice)container.getBean( "Reportservice"); Reportservice.getDailyReport(new Date()); //ReportService.getMonthLyReport(New Date()); }}
タイミングチャートはおおよそ次のとおりです。
効果:
上記のように、Reporterviceは特定のReportGeneratorに直接関連しなくなります。コンテナを使用してインターフェイスと実装を分離し、システムコンポーネント豆の再利用性を改善しました。現時点では、構成ファイルを使用して、コンテナ内の特定のコンポーネントの定義をリアルタイムで取得することもできます。
EG3
問題の説明:
ただし、上記のクラス図を見ると、レポートサービスとコンテナの間に双方向の関係があり、相互依存関係があることを簡単に見つけることができます。また、レポートサービスを再利用する場合は、単一のコンテナの特定の検索ロジックにも直接依存します。他のコンテナに異なるコンポーネント検索メカニズム(JNDIなど)がある場合、この時点でレポートサービスを再利用することは、コンテナの内部検索ロジックを変更する必要があることを意味します。
解決策:サービスロケーターの紹介
間接レイヤーサービスロケーターを再導入することは、コンポーネント検索ロジックのインターフェイスを提供するために使用されます。ウィキペディアの説明またはJava EE 1および2による説明をご覧ください。これにより、可能な変更の可能な分離が可能になります。
実装方法:
クラス図は次のとおりです。
コード実装:
//実際のアプリケーションでは、インターフェイスを使用して、統一されたインターフェイスクラスServiceLocatorを提供できます{private static container container = new Container(); public static ReportGenerator getReportGenerator(){return(ReportGenerator)container.getBean( "ReportGeneraator"); }} class Reportservice {private ReportGenerator ReportGenerator = servicelocator.getReportGenerator(); // ...}EG4
問題の説明:
ただし、コンテナの導入であろうとサービスロケーターを使用している場合でも、Reportserviceは特定のコンポーネントの検索と作成において「アクティブ」です。つまり、顧客として、レポートサービスは必要なもの、取得できる場所、および取得方法について明確にする必要があります。何、どこ、どのように、どのように、突然、具体的な論理の詳細を追加する必要があります。
たとえば、「コンテナの導入」の以前の実装方法には、次のコードがあります。
class Reportservice {//タイトな結合関係を排除し、コンテナに置き換えます// private private ReportGenerator Generator =(reportGenerator)container .getBean( "ReportGenerator");
「サービスロケーターの導入」の実装方法には、次のコードがあります。
class serviceLocator {privatestatic container container = new Container(); publicStatic ReportGenerator getReportGenerator(){//それはまだcontainer.getBean()であり、デリゲートのみがreturn(reportgenerator)container.getBean( "ReportGeneraator"); }} class Reportservice {// Reportervice最終的には、依然として「積極的に」検索し、ServiceLocator Private ReportGenerator ReportGenerator = servicelocator.getReportGenerator(); }
解決:
この場合、「アクティブ」を「パッシブ」に変更すると、Reportserviceの内部知識(つまり、コンポーネントを見つけるロジック)が減少します。コントロールの反転(IOC)の原則によれば、このプル(プル、アクティブ)は、プッシュ(プッシュ、パッシブ)モードに変換されます。
たとえば、通常使用するRSSサブスクリプションはプッシュアプリケーションであり、1日に数回お気に入りのサイトにログインする手間を省いて、記事の更新を積極的に取得します。
依存関係注射(DI)この種の受動的受容を達成し、複雑なロジックを含み、知識が多すぎる顧客(ここ、レポートサービス)自体の問題を軽減します。
実装方法:
「パッシブ」レセプションになりたいので、サービスロケーターモードを使用せずにコンテナの例に戻る必要があります。変更されたクラス図は、次のようにこれから取得されます。
元のクラス図は次のとおりです。あなたはそれをチェックして、コメントのプロンプトに注意を払うことができます:
コード実装:
例をコンパイルして実行できるようにし、追跡コードの実行結果を使用してクラス図全体を明示的にインスタンス化し、順番に互いに協力します。
Import java.util.date; import java.util.hashmap; import java.util.map; //コンパイルして実行できるようにするために、さらに2つの違うクラスがあります。クラス月{} class table {publicVoid setDate(日付){} publicVoid setMonth(月){}} // ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- System.out.println("2...start initialization Excelgeneratorの... "); } publicVoid generate(テーブルテーブル){system.out.println( "Excelレポートを生成..."); }} class pdfgeneratorを実装したReportgenerator {public Pdfgenerator(){system.out.println( "2 ... pdfgenerator ..."); } publicVoid generate(テーブルテーブル){system.out.println( "pdfレポートを生成..."); }}//----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Reportservice Reportservice Reportservice = new Reportservice()の参照を取得して管理します。 //上記のReportservice.setReportGenerator(ReportGenerator)の上に作成された特定のReportGeneratorインスタンスを挿入します。 Beans.put( "Reportservice"、Reportservice); System.out.println( "5 ... end initialization contaent ..."); } publicStatic Object getBean(string id){system.out.println( "last get service component ... getbean() - >" + id + "..."); returnbeans.get(id); }} class Reportservice {// private static ReportGenerator Generator = new PDFGenerator(); //上記のタイトな結合関係を排除し、コンテナに置き換えます//上記の「アクティブ」ルックアップを削除し、外部に注入されたオブジェクトのプライベートレポートGeneratorジェネレーターを保存するためのプライベートフィールドを提供します。 // setter form setreportgenerator(reportgenerator generator){system.out.println( "4 ... ReportGenerator ...を開始..."); this.generator = generator; }プライベートテーブルテーブル= new Table(); public Reportservice(){system.out.println( "3 ...初期化Reportserviceを開始..."); } publicVoid getDailyReport(日付){table.setDate(date); generator.generate(表); } publicVoid getMonthLyReport(月月){table.setmonth(月); generator.generate(表); }} publicClass client {publicStaticVoid main(string [] args){//コンテナの初期化new Container(); Reportservice Reportservice =(Reportservice)container .getBean( "Reportservice"); Reportservice.getDailyReport(new Date()); // Reportservice.getMonthlyReport(new Date()); }}
実行結果:
1 ...初期化コンテナを開始... 2 ...初期化PDFGeneratorを開始... 3 ...初期化レポートサービスを開始... 4 ... 5 ...終了初期化コンテナ...
知らせ:
1。上記の実行結果の印刷順序によると、コードに追加された特定の数値が合理的であることがわかります。これはプログラム実行のプロセスをシミュレートしているため、シーケンス図を描画しなくなります。
2。この例でのIOCとDIの使用は、クライアントとしてのReportervice(つまり、コンポーネントデマテルター)に基づいており、コードのクライアントクラスMain()のテストコードはサービスコンポーネントのエンドユーザーですが、必要なのはコンポーネントではなく、コンポーネントが持っているサービスです。
3.実際、スプリングボックスクリップでは、コンテナの初期化は明らかにエンドユーザークライアントがすべきことではなく、サービスプロバイダーが事前に開始する必要があります。
4.エンドユーザークライアントでは、コンテナ.getBean( "Reportervice")を使用して、コンテナコンストラクターに事前にインスタンス化されたサービスコンポーネントを取得します。特定のアプリケーションでは、利用可能なサービスコンポーネントは通常、XMLおよびその他の構成ファイルを使用してサーバーに展開され、構成ファイルはコンテナによって読み取り、反射テクノロジーと組み合わせて特定のサービスコンポーネントを作成および挿入します。
分析:
以前は、レポートサービスはコンテナからサービスコンポーネントを積極的に要求していましたが、今ではコンテナ噴射(注入、つまりプッシュ)サービスコンポーネントを受動的に待っていました。制御は明らかに基礎となるモジュール(Reporterviceはコンポーネントの要求者)から高レベルモジュール(コンテナがコンポーネントプロバイダーです)に転送されます。つまり、コントロールが逆転します。