Di入門

7,274 views

Published on

0 Comments
17 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
7,274
On SlideShare
0
From Embeds
0
Number of Embeds
96
Actions
Shares
0
Downloads
56
Comments
0
Likes
17
Embeds 0
No embeds

No notes for slide

Di入門

  1. 1. DI 入門 DI コンテナを導入する意義 について理解する
  2. 2. 構造化設計とオブジェクト指向設計の思想の違いを理解する <ul><li>そもそも、 DI の考え方を理解する前提として、 オブジェクト指向的なプログラム設計 について理解しておかなくてはならない </li></ul><ul><li>伝統的な構造化型プログラミングではプログラムを処理の固まり単位に分割し、メインから順次下位のサブルーチンをコールする </li></ul><ul><li>一方、オブジェクト指向できちんと設計されたシステムであれば、 「特定の責務」を持ったオブジェクト が互いにメッセージをやりとりすることで特定の機能が実現される </li></ul>
  3. 3. 構造化型アーキテクチャ <ul><li>プログラムを順次細分化された 処理の集合 として構築 </li></ul><ul><li>データはパラメータやグローバル変数として処理とは別に管理 </li></ul><ul><li>プログラムの状態としてのデータがカプセル化されていないため、上位関数は下位関数の処理の細部を原則知っていることが前提(実質的に巨大なアルゴリズム) </li></ul><ul><li>バッチなどの定型処理や小規模スクリプトなど単純なケースを除いて、 GUI や分散処理など、 今日の複雑なプログラムの作成には向かない </li></ul>main 関数 初期処理 データ更新処理 DB 接続オープン処理 SQL 文発行処理 DB 接続クローズ処理 更新件数表示処理 グローバルデータ パラメータ
  4. 4. オブジェクト指向アーキテクチャ <ul><li>責務 が明確に分かれた オブジェクト単位 に分割 </li></ul><ul><li>担当オブジェクトに順次処理を委譲 </li></ul><ul><li>ロジックとデータはオブジェクト内部に カプセル化 </li></ul><ul><li>外から呼び出される public なインタフェース と 内部の実装 とが明確に分離される </li></ul>コントローラ サービス DAO エンティティマネージャ ビュー エンティティ DTO 接続プール データソース 画面描画 画面遷移ロジック ビジネスロジック バリデータ データ転送 業務固有データ 永続処理 入力チェック
  5. 5. オブジェクト指向のメリット <ul><li>オブジェクトを部品として 再利用 できる </li></ul><ul><li>基本的な 部品を組み合わせる ことで複雑で大規模なプログラムを構築できる </li></ul><ul><li>継承、動的バインディングなどのプログラミング手法を利用することにより、元のソースコードに手を加えることなく 拡張可能なフレームワーク を構築できる </li></ul><ul><li>もともとは GUI やシミュレーションなどごく限られた目的に利用されていたが、最近ではスクリプト言語などを含めてあらゆる分野でオブジェクト技術がとりれられるようになってきている </li></ul>
  6. 6. オブジェクト指向プログラミングの基本的な手順 <ul><li>個々のオブジェクトが保持するデータの内容( フィールド )や振る舞い( メソッド )を クラス として定義する( クラス図 を使ってモデリング) </li></ul><ul><li>クラスを インスタンス化 することでオブジェクトをメモリ上に生成する </li></ul><ul><li>生成したオブジェクトに メッセージ を投げる (public メソッドを呼び出す)ことで特定の処理の実行を依頼する </li></ul><ul><li>あるオブジェクトは通常は 別のオブジェクトと連携 して 特定の責務を実行 する( シーケンス図 を使ってモデリング) </li></ul>
  7. 7. オブジェクトが「使える」ように するまでに必要な準備 <ul><li>OOP では、いったん必要なオブジェクトの生成が行われれば、責務を持ったオブジェクトに単にメッセージ(メソッド)を呼び出すだけなので非常に簡単だが。。。 </li></ul><ul><li>あるオブジェクトが適切に動作するためには、すべて依存オブジェクトが生成され、適切に初期化されてされている必要がある </li></ul><ul><li>以上の問題点は OOP の最初の説明では無視されることが多いが、実際にモックなしで JUnit を書いてみれば、その大変さが実感できる </li></ul>
  8. 8. オブジェクト生成のプログラミングは意外に奥が深い <ul><li>Java では単に new 演算子を使ってオブジェクトが生成できると教わるが。。。 </li></ul><ul><li>実際のアプリケーション設計では、オブジェクト生成に関して以下のような問題を考慮しなくてはならない </li></ul><ul><ul><li>課題1:オブジェクト間の複雑な依存関係の適切な初期化 </li></ul></ul><ul><ul><ul><li>依存オブジェクトのまたその先の依存オブジェクトを正しい順序で初期化する必要性 </li></ul></ul></ul><ul><ul><li>課題2:設定情報や DB 接続など、グローバルに共通のオブジェクトを共有する必要性 </li></ul></ul><ul><ul><li>課題3:テスト容易性や拡張性を向上させるため、オブジェクト同士がインタフェースのみに依存するようにする必要性 </li></ul></ul>
  9. 9. 課題1:複雑な依存関係の初期化 <ul><li>コントローラが動作するためには、サービスが、サービスが動作するためには DAO がという具合に依存するオブジェクトを適切な順序で生成、初期化しておく必要がある </li></ul><ul><li>業務ロジック中で Java の new を直接使って初期化処理をハードコードすると、以下のような問題点がある </li></ul><ul><ul><li>オブジェクトの関連を拡張したり、変更したりすることが難しくなる </li></ul></ul><ul><ul><li>オブジェクトの準備のための配管コードが多数を占めるようになり、本質的に重要な業務処理の記述が埋もれてしまう </li></ul></ul>
  10. 10. 伝統的な解決策: 手製ファクトリの利用 <ul><li>DI 発明以前は、 デザインパターン を駆使することで、生成ロジックを「 ファクトリ 」と呼ばれる特定のクラスやメソッドにカプセル化する </li></ul><ul><li>有名な GoF のデザインパターンの中でもオブジェクトの生成は重要なテーマの一つ </li></ul><ul><ul><li>Factory Method パターン </li></ul></ul><ul><ul><ul><li>抽象メソッドにより実際の生成対象のオブジェクトをサブクラスで決める </li></ul></ul></ul><ul><ul><li>Abstract Factory パターン </li></ul></ul><ul><ul><ul><li>関連するオブジェクトの生成を抽象化するインタフェースを定義する </li></ul></ul></ul><ul><ul><li>Protytype パターン </li></ul></ul><ul><ul><ul><li>雛型となるオブジェクトをクローンすることで個別のインスタンスを生成する </li></ul></ul></ul>
  11. 11. 手製ファクトリ利用の問題点 <ul><li>デザインパターンなどの考え方を利用できるとはいえ、プロジェクトごとに独自にファクトリを設計するのは敷居が高い </li></ul><ul><li>業務ロジック意外に、ファクトリや付随するインタフェースなどたくさん書かなくてはならない </li></ul>
  12. 12. 課題2:グローバルにオブジェクトを共有 <ul><li>システムの中では使うたびに new するのではなく、特定のオブジェクトをグローバルに共有したいことが多い </li></ul><ul><li>共有化したいオブジェクトの例 </li></ul><ul><ul><li>毎回生成するのが重たいリソースオブジェクト </li></ul></ul><ul><ul><ul><li>DB やメッセージキューに対する接続先 </li></ul></ul></ul><ul><ul><ul><li>O/R マップ FW のコンテキストファクトリ </li></ul></ul></ul><ul><ul><ul><li>リモートサービスに対する参照 </li></ul></ul></ul><ul><ul><ul><li>ログ出力先 </li></ul></ul></ul><ul><ul><li>共通情報 </li></ul></ul><ul><ul><ul><li>システム設定プロパティ </li></ul></ul></ul><ul><ul><ul><li>エラーメッセージ文字列リソース </li></ul></ul></ul><ul><ul><ul><li>システムステータス </li></ul></ul></ul>
  13. 13. 伝統的な解決策: 独自 Singleton パターンの利用 <ul><li>グローバル情報のアクセスのために、独自に Singleton デザインパターンや Service Locator パターンを利用する </li></ul>public class AppConfig { private static final INSTANCE= new AppConfig(); public static AppConfig getInstance() { return INSTANCE; }    // コンストラクタを隠蔽 private AppConfig () {} … } public class SomeServiceImpl { public void userAppConfig() { int val = AppConfig.getInstance() .getSomeProperty(); } } Static メソッド経由で、特定のインスタンスを共有
  14. 14. Singleton パターンは 大部分はアンチパターン? <ul><li>GoF の本来の Singleton パターンは「 唯一のオブジェクトに限定 」というのがもともとの使用目的という思想だったが、たいていは グローバル変数の代用 として 気軽に利用され過ぎる傾向 がある </li></ul><ul><li>Singleton パターンは static メソッドに依存しているため、単体試験の際にモックのメソッドに置き換えて試験することが難しい </li></ul><ul><li>特に、 データソースや EJB 参照の取得を Singleton を使って実装すると呼び出し側のプログラムが JNDI などコンテナ環境に依存するため、単体テストが非常に難しくなる </li></ul>
  15. 15. 課題3:オブジェクト同士がインタフェースのみに依存するようにする <ul><li>オブジェクト指向プログラミングの最も重要な設計方針 </li></ul><ul><li>「実装ではなく、インタフェースに対してプログラミングすべし」 </li></ul><ul><li>依存元が依存先オブジェクトの実装ではなくインタフェースのみに依存することで以下のメリットが得られる </li></ul><ul><ul><li>共通のインタフェースを実装した別のサブクラスに 実装を自由に置き換えられる ( Strategy 、 Command 、 State など大部分のデザインパターンの基礎原理) </li></ul></ul><ul><ul><li>対象オブジェクトをラップすることで 容易に機能を拡張できる ( Proxy 、 Decorator パターン) </li></ul></ul>
  16. 16. Strategy パターンの例
  17. 17. Proxy パターンの例
  18. 18. IF のみに依存するコードを書くことは実は結構難しい <ul><li>単にインタフェース型を定義して、 implements すればよいわけでない! </li></ul><ul><li>インタフェースに対して new を呼び出すことはできないため、結局 new をハードコードしたら特定の具象サブクラスに依存してしまう </li></ul>public class CustomerService { private CustomerDao customerDao = new JpaCustomerDao(); } 直接 new したら、結局インタフェース型のフィールドを宣言している意味がなくなる!!
  19. 19. 課題 1 ~ 3 すべてに対する解決策⇒ Dependency Injection <ul><li>Dependency Injection( 依存性の注入) </li></ul><ul><li>従来はアプリケーションごとに独自のファクトリやシングルトンを駆使して設計していたため、正しく設計するためには相当敷居が高かった </li></ul><ul><li>一方、 DI では DI コンテナフレームワークが汎用のファクトリとして機能する </li></ul><ul><li>DI によりオブジェクトの生成や依存関係の設定が自動化されるため、プログラム中では生成済みのオブジェクトを使えばよい </li></ul><ul><li>結果として、インタフェースのみに依存するプログラムを作成することが非常に簡単になる </li></ul>
  20. 20. 伝統的な setter インジェクション <ul><li>Spring FW では伝統的に setter メソッドを使って依存オブジェクトを設定する </li></ul>public class CustomerService { private CustomerDao customerDao; public void setCustomerDao( CustomerDao customerDao) { this.customerDao = customerDao; } } <bean id=“customerService&quot; class=“…CustomerImpl”> <property name=“customerDao“ ref=“customerDao”/> </bean> XML ファイル中でセッターに対応するプロパティに依存関係をインジェクションする
  21. 21. Java コードが XML に移動しただけでは? <ul><li>オブジェクト生成、関連付けの Java コードは不要になった一方で、 XML の設定ファイルに生成に必要なメタ情報を記述する必要がある </li></ul><ul><li>確かに XML を含めた全体のコード量は変わらないが、以下の点で多大なメリットがある </li></ul><ul><ul><li>サービスや接続ファクトリなどのオブジェクトを簡単に共有できる </li></ul></ul><ul><ul><li>インタフェースのみに依存するようにすることが簡単に実現できる </li></ul></ul><ul><li>通常はサービスや DAO など粒度の大きなコンポーネントのみを DI 管理する(newの使用をすべて禁止するわけではない!) </li></ul>
  22. 22. 設定ファイルを少なくする工夫 ⇒アノテーションインジェクション <ul><li>以前から「自動ワイヤリング機能」といって同一の型あるいは Bean 名の一致により自動インジェクションする機能はあったが、一致ロジックが固定的なため、実際使える場面は限定的 </li></ul><ul><li>Spring2.5 ではアノテーションを使った自動インジェクション機能がサポートされる </li></ul>public class CustomerService { @Autowired private CustomerDao customerDao; }
  23. 23. アノテーションインジェクションは JavaEE 標準でも採用されている <ul><li>@EJB⇒EJB 参照をインジェクション </li></ul><ul><li>@Resource⇒ データソースなどのリソースをインジェクション </li></ul><ul><li>@PersistenceContext⇒JPA のエンティティマネージャをインジェクション </li></ul>DI スタイルプログラミング技法の発見は、 Java プログラミングの世界では OO 自体の発見と同じくらい重要な出来事であり、「パラダイムシフト」と呼んでいる人もいるくらいである。 最近はさまざまな FW や標準仕様が DI の考え方を採用している。
  24. 24. DI コンテナのその他の便利機能 <ul><li>オブジェクト生成スコープの調整機能 </li></ul><ul><ul><li>デフォルトでは DI コンテナで生成されるインスタンスは宣言につき 1 個( Singleton ) </li></ul></ul><ul><ul><li>毎回生成( Protytype )、 Web リクエストごとに生成、セッションごとに生成などに変更可 </li></ul></ul><ul><li>オブジェクトライフサイクルコールバック </li></ul><ul><ul><li>生成時、廃棄時に特定の処理を記述 </li></ul></ul><ul><li>AOP 機能との連携 </li></ul><ul><ul><li>プロキシ生成機能 </li></ul></ul><ul><ul><li>ロードタイムウィービング機能 </li></ul></ul>
  25. 25. 他のソリューションと比較して Spring FW の優れている点 <ul><li>Web 層、サービス層、 DAO 層すべてにわたって共通フォーマット設定ファイル、共通の IF が利用できるため、設計に統一感が出る </li></ul><ul><li>世界中で利用実績のあるデファクトスタンダード </li></ul><ul><li>今のところ、 JDK1.4 や EJB2.0 などのレガシー技術を見捨てていないため、幅広いプロジェクトで活用できる (Seam や Seasar などの tiger や JavaEE5 前提の FW と対照的) </li></ul>

×