34.8K Views
April 01, 23
スライド概要
「持続可能なソフトウェア」の探求がライフワーク。C#、.NET、WPFあたりが住処。Microsoft MVP for Development Technologies(2017/01〜)。
「関心の分離」と「疎結合」 ソフトウェアアーキテクチャのひとかけら Atsushi Nakamura
About Me 中村 充志 / Atsushi Nakamura • • • • リコージャパン株式会社 金融事業部所属 Enterprise系のITアーキテクト JavaからC#へ渡り歩く 趣味はXamarin • Blog http://www.nuits.jp • Blog(英語)https://blog.nuits.jp • Twitter @nuits_jp Copyright 2017 @nuits_jp
「関心の分離」と「疎結合」 Today’s Goal
Today’s Goal 1. ソフトウェア アーキテクチャの概略を理解いただく 2. 特に理解いただきたいこと 1. 関心(関心事)の分離 2. カプセル化 3. 密結合と疎結合 4. 疎結合を実現する代表的な手段 Xamarin.Forms Navigation Overview Slide 4
「関心の分離」と「疎結合」 アーキテクトとは?
アーキテクトとはなにか? class Class Model IT アーキテクチャ ≒ システム アーキテクチャ アプリケーション アーキテクチャ (≒ソフトウェア アーキテクチャ) 設計・構築する インフラストラクチャ アーキテクチャ ITアーキテクト インテグレーション アーキテクチャ Copyright 2017 @nuits_jp Slide 6
アーキテクトとはなにか? class Class Model IT アーキテクチャ ≒ システム アーキテクチャ アプリケーション アーキテクチャ (≒ソフトウェア アーキテクチャ) 設計・構築する インフラストラクチャ アーキテクチャ ITアーキテクト インテグレーション アーキテクチャ Copyright 2017 @nuits_jp Slide 7
「関心の分離」と「疎結合」 ソフトウェア アーキテクチャといえば何を思い浮かべますか? Copyright 2017 @nuits_jp
ソフトウェア アーキテクチャといえば何を思い浮かべますか? 1. MVC 2. MVP 3. MVVM 4. MVPVM 5. クリーン アーキテクチャ 6. レイヤー アーキテクチャ 7. オニオン アーキテクチャ などなど これらはソフトウェア アーキテクチャの類型的なパターン群です Copyright 2017 @nuits_jp Slide 9
ソフトウェア アーキテクチャとは何か? ソフトウェアアーキテクチャでは、ソフトウェア システムの構成に関する一連 の重要な判断を網羅しています。これには、システムを構成する要素とイン ターフェイスの選択、要素間のコラボレーションとして指定される動作、この ような構成と動作の要素のより大きなサブシステムに対する構成、この構成の 指針となるアーキテクチャスタイルが含まれます。また、機能性、ユーザビリ ティ、復元性、パフォーマンス、再利用性、理解できること、経済的な制約、 テクノロジの制約、トレードオフ、および外観への配慮も必要です。 by Philippe Kruchten, Grady Booch, Kurt Bittner, Rich Reitman Microsoft 「アプリケーション アーキテクチャ ガイド2.0」より引用 Copyright 2017 @nuits_jp Slide 10
ソフトウェア アーキテクチャとは何か? アーキテクチャは、システムを大きなレベルで分解したもので、決定事項の変 更は困難です。システムには複数のアーキテクチャが存在し、アーキテクチャ にとって重要な事項はシステムの運用中に変化します。要するに、重要な要素 は、すべてアーキテクチャということになります。 by Martin Fowler Microsoft 「アプリケーション アーキテクチャ ガイド2.0」より引用 Copyright 2017 @nuits_jp Slide 11
ソフトウェア アーキテクチャとは何か? ソフトウェアアーキテクチャとは、抽象化と問題の分割によって複雑性を減ら すことを主に念頭に置いたものである。ただし、今までのところ、「ソフト ウェアアーキテクチャ」という用語に関して、万人が合意した厳密な定義は存 在しない Wikipedia「ソフトウェアアーキテクチャ」より引用 Copyright 2017 @nuits_jp Slide 12
とは言え、おおよその共通認識はある Copyright 2017 @nuits_jp Slide 13
ソフトウェア アーキテクチャとは何か?かみ砕くと… 1. ソフトウェアアーキテクチャとは システムアーキテクチャのうち、ソフトウェア領域のアーキテク チャである この時「システム」とは「IT」だけではなく、それらを取り巻く 社会やビジネスを含めた「仕組み」を指すことも多い ※ 元来「System」とは制度・組織・体系・系統などのこと Copyright 2017 @nuits_jp
ソフトウェア アーキテクチャとは何か?かみ砕くと… 2. ソフトウェア アーキテクチャとは 1. ソフトウェアにおける重要な決定事項全てである 2. その中でも特につぎの2点が重要 1. ソフトウェア全体を、どのように分割するか 2. 分割した部分同士を、どのように相互作用させるか Copyright 2017 @nuits_jp
ソフトウェア アーキテクチャとは何か?かみ砕くと… 3. ソフトウェアアーキテクチャを構築するのはなぜか? • 「システムの実現をサポートする」 • 「持続可能なソフトウェア」を • 「バランスよく」構築するため 4. ソフトウェアアーキテクチャのバランスとは? 1. Quality(機能・非機能) 2. Cost 3. Delivery(開発期間) 特にアーキテクチャは非機能要求から受ける影響が大きい Copyright 2017 @nuits_jp
「関心の分離」と「疎結合」 Again, today’s Goal Copyright 2017 @nuits_jp
Today’s Goal 1. ソフトウェア アーキテクチャの概略を理解いただく 2. 特に理解いただきたいこと 1. 関心(関心事)の分離 2. カプセル化 3. 密結合と疎結合 4. 疎結合を実現する代表的な手段 Xamarin.Forms Navigation Overview Slide 18
Today’s Goal 1. ソフトウェア アーキテクチャの概略を理解いただく 2. 特に理解いただきたいこと 1. 関心(関心事)の分離 2. カプセル化 3. 密結合と疎結合 4. 疎結合を実現する代表的な手段 Xamarin.Forms Navigation Overview Slide 19
「関心の分離」と「疎結合」 関心(関心事)の分離 Copyright 2017 @nuits_jp
Separation of Concerns さて、アーキテクチャとは「どう分割し、どう結合するか」が重要 だとお話ししました。 その観点で非常に重要な概念として Separation of Concerns(SoC):関心(関心事)の分離 という考え方があります。 プログラムは異なる関心事は、異なる部分に分離せよという設計原 則です。 Copyright 2017 @nuits_jp
MVC, MVP, MVVM, MVPVM, … たとえば、MVC・MVP・MVVMなども、ある「何かと何か」の関心 を分離するための仕組みです。 Binding & Command Update Notification Notification Copyright 2017 @nuits_jp Slide 22
MVC, MVP, MVVM, MVPVM, … MVC・MVP・MVVMなどはつぎの二つの関心事を分離するものです。 • プレゼンテーション • プレゼンテーション以外 Binding & Command Update Notification Notification プレゼンテーション Copyright 2017 @nuits_jp その他 Slide 23
なぜプレゼンテーションを分離するのか? いくつか明確な理由があります。 1. プレゼンテーションは専門性が高い →プレゼンテーションのみを分業できる仕組みを用意したい 2. 一般的にコードによるテストが困難である →プレゼンテーション以外はコードによるテストを行いたい こう言った理由から、プレゼンテーションとそれ以外を分離して設計する 考え方を「Presentation Domain Separation(PDS)」と言います。 Copyright 2017 @nuits_jp Slide 24
MVC, MVP, MVVM, MVPVM, … is PDS Presentation Domain Separation:PDS Copyright 2017 @nuits_jp Slide 25
MVC, MVP, MVVM, MVPVM, … is PDS 目的 Presentation Domain Separation:PDS 手段 Copyright 2017 @nuits_jp Slide 26
PDS is SoC Separation of Concerns:SoC Presentation Domain Separation:PDS Copyright 2017 @nuits_jp Slide 27
PDS is SoC 目的 Separation of Concerns:SoC Presentation Domain Separation:PDS 手段 Copyright 2017 @nuits_jp Slide 28
プレゼンテーション以外の関心は? Copyright 2017 @nuits_jp Slide 29
ところで 多くのソフトウェアのプレゼンテーションと、それ以外の比率は プレゼンテーション < それ以外 です。 Binding & Command Update Notification Notification その他 プレゼンテーション Copyright 2017 @nuits_jp Slide 30
これらの概念だけでは足りない 目的 Separation of Concerns:SoC Presentation Domain Separation:PDS 手段 何かがここに必要! 何が必要なのか? Copyright 2017 @nuits_jp Slide 31
ケースバイケースで銀の弾丸はない Copyright 2017 @nuits_jp Slide 32
とはいえ参考となるものは存在する 1. Domain Driven Design(ドメイン駆動設計) 2. レイヤー アーキテクチャ 3. クリーン アーキテクチャ 4. Microsoft アプリケーション アーキテクチャ ガイド2.0 など Copyright 2017 @nuits_jp Slide 33
ここからはモデルケースを見ながら進めます Copyright 2017 @nuits_jp Slide 34
題材 【要求】 • 端末の位置情報を利用し、周辺のレストラン一覧を表示する • レストラン情報は「リクルートWEBサービス」のグルメサー チAPIを利用させていただく https://webservice.recruit.co.jp/hotpepper/reference.html • コンソールアプリケーションとして実装する 【制限事項】 • Unit Testの実装例はごく一部に限らせていただきます 【ソースコード】 https://github.com/nuitsjp/SoC-and-Loosely-Coupled Copyright 2017 @nuits_jp Slide 35
関心の分離方針 全体としてはレイヤー アーキテクチャを採用 Presentation • プレゼンテーション層 • ユーザーインターフェース(コンソール)を実現する Usecase • ビジネスロジック層 • ユースケース単位でクラスを分割 • 今回は「周辺のレストランを探す」ユースケースのみ Integration • プログラム外との統合するための層 • 現在地座標の取得と、指定座標のレストラン情報の取 得の2種類に分割 Copyright 2017 @nuits_jp Slide 36
それではコードを見てみましょう! Copyright 2017 @nuits_jp Slide 37
現在の構造 class Class Model Presentation RestauranListConsole Usecase FindRestaurants + FindNearbyRestaurantsAsync(): GourmetSearchResult Integration GourmetSearchResult GourmetService + GeoCoordinator SearchGourmetInfosAsync(): GourmetSearchResult Copyright 2017 @nuits_jp Slide 38
NGポイント:関心の未分離 Copyright 2017 @nuits_jp Slide 39
NGポイント:関心の未分離 class Class Model Presentation RestauranListConsole Usecase FindRestaurants + FindNearbyRestaurantsAsync(): GourmetSearchResult Integration GourmetSearchResult GourmetService + GeoCoordinator SearchGourmetInfosAsync(): GourmetSearchResult Copyright 2017 @nuits_jp Slide 40
関心をカプセル化する Copyright 2017 @nuits_jp Slide 41
めざす姿 class Class Model Presentation RestauranListConsole ③ Usecase Restaurant FindRestaurants + FindNearbyRestaurantsAsync(): IList<Restaurant> ② Integration GourmetInfo GourmetService + GeoCoordinator SearchGourmetInfosAsync(): IList<GourmetInfo> ① GourmetSearchResult Copyright 2017 @nuits_jp Slide 42
現在の構造 class Class Model Presentation RestauranListConsole Usecase Restaurant FindRestaurants + FindNearbyRestaurantsAsync(): IList<Restaurant> Integration GourmetInfo GourmetService + GeoCoordinator SearchGourmetInfosAsync(): IList<GourmetInfo> GourmetSearchResult Copyright 2017 @nuits_jp Slide 43
NGポイント:密結合 Copyright 2017 @nuits_jp Slide 44
NGポイント:密結合 class Class Model Presentation RestauranListConsole Usecase Restaurant FindRestaurants + FindNearbyRestaurantsAsync(): IList<Restaurant> Integration GourmetInfo GourmetService + GeoCoordinator SearchGourmetInfosAsync(): IList<GourmetInfo> GourmetSearchResult Copyright 2017 @nuits_jp Slide 45
NGポイント:密結合 class Class Model new FindRestaurants + FindNearbyRestaurantsAsync(): IList<Restaurant> GourmetService + SearchGourmetInfosAsync(): IList<GourmetInfo> use • クラスとクラスが直接依存関係にある • 各レイヤー間が密結合状態となっている Copyright 2017 @nuits_jp Slide 46
NGポイント:密結合 class Class Model Presentation RestauranListConsole Usecase Restaurant FindRestaurants + FindNearbyRestaurantsAsync(): IList<Restaurant> Integration GourmetInfo GourmetService + GeoCoordinator SearchGourmetInfosAsync(): IList<GourmetInfo> GourmetSearchResult Copyright 2017 @nuits_jp Slide 47
なぜ密結合が悪いのか? 一般論 • 利用者側が、依存先の影響を強く受ける • 結果、保守性・拡張性が低下する Copyright 2017 @nuits_jp Slide 48
NGポイント:密結合 class Class Model Presentation RestauranListConsole Usecase Restaurant FindRestaurants + FindNearbyRestaurantsAsync(): IList<Restaurant> Integration GourmetInfo GourmetService + GeoCoordinator SearchGourmetInfosAsync(): IList<GourmetInfo> GourmetSearchResult Copyright 2017 @nuits_jp Slide 49
NGポイント:密結合 class Class Model Presentation RestauranListConsole Usecase Restaurant FindRestaurants + FindNearbyRestaurantsAsync(): IList<Restaurant> Integration GourmetInfo GourmetService + GeoCoordinator SearchGourmetInfosAsync(): IList<GourmetInfo> GourmetSearchResult Copyright 2017 @nuits_jp Slide 50
閑話:Unit Test Copyright 2017 @nuits_jp Slide 51
テストが難しいクラス class Class Model テストコード テスト対象 Copyright 2017 @nuits_jp 依存先 Slide 52
テストが難しいクラス class Class Model テストコード テスト対象 Copyright 2017 @nuits_jp 依存先 Slide 53
テストが難しいクラス class Class Model テストコード テスト対象 依存先 • 依存先がテストを困難にする要素を持っている • 時間 • 位置情報 • ネットワーク • 大量の再依存クラス • など Copyright 2017 @nuits_jp Slide 54
依存先を置き換え可能にする class Class Model テストコード «interface» 依存先 テスト対象 依存先 Copyright 2017 @nuits_jp テスト用の偽物 Slide 55
依存先を置き換え可能にする class Class Model テストコード «interface» 依存先 テスト対象 依存先 Copyright 2017 @nuits_jp テスト用の偽物 Slide 56
依存先を置き換え可能にする class Class Model テストコード Driver «interface» 依存先 テスト対象 依存先 Copyright 2017 @nuits_jp テスト用の偽物 Slide 57
依存先を置き換え可能にする class Class Model テストコード «interface» 依存先 テスト対象 依存先 テスト用の偽物 Stub Fake Mock Copyright 2017 @nuits_jp Slide 58
閑話終了 疎結合を目指す Copyright 2017 @nuits_jp Slide 59
疎結合を実現する class Class Model FindRestaurants + «interface» IGourmetService FindNearbyRestaurantsAsync() + 利用者がインターフェースにのみ 依存している状態を目指す SearchGourmetInfosAsync() GourmetService + Copyright 2017 @nuits_jp SearchGourmetInfosAsync() Slide 60
疎結合の実現 その1 インターフェースの抽出 Copyright 2017 @nuits_jp Slide 61
インターフェースの抽出結果 class Class Model FindRestaurants + FindNearbyRestaurantsAsync() «interface» IGourmetService use + SearchGourmetInfosAsync() new 利用箇所の依存性は分離できたが インスタンス生成箇所がクラスに 依存している。 GourmetService + Copyright 2017 @nuits_jp SearchGourmetInfosAsync() Slide 62
解決方法は二つ 1. Dependency Injectionパターン 2. Service Locatorパターン Copyright 2017 @nuits_jp Slide 63
解決方法は二つ 1. Dependency Injectionパターン 2. Service Locatorパターン Copyright 2017 @nuits_jp Slide 64
インターフェースの抽出結果 class Class Model FindRestaurants + FindNearbyRestaurantsAsync() «interface» IGourmetService use + SearchGourmetInfosAsync() new 利用箇所の依存性は分離できたが インスタンス生成箇所がクラスに 依存している。 GourmetService + Copyright 2017 @nuits_jp SearchGourmetInfosAsync() Slide 65
疎結合の実現 その2 依存性の注入 Copyright 2017 @nuits_jp Slide 66
疎結合の実現 class Class Model FindRestaurants + FindNearbyRestaurantsAsync() «interface» IGourmetService use + SearchGourmetInfosAsync() injection Program GourmetService new Copyright 2017 @nuits_jp + SearchGourmetInfosAsync() Slide 67
Unit Testを書いてみよう! Copyright 2017 @nuits_jp Slide 68
Dependency Injection 面倒ですよね? Copyright 2017 @nuits_jp Slide 69
Dependency Injection Containerを使おう! Copyright 2017 @nuits_jp Slide 70
「関心の分離」と「疎結合」 まとめ Copyright 2017 @nuits_jp
まとめ ① 1. ソフトウェア アーキテクチャとは 1. 重要な決定事項の全てがアーキテクチャ 2. 特にその中でも次の2点が重要 1. 全体をどう部分に分割するか 2. ソフトウェア内部と外部、部分と部分をどう結合し、どう 相互作用させるか Copyright 2017 @nuits_jp
まとめ ② 1. 関心の分離 分割する際「関心の分離(SoC)」を実現すること 2. カプセル化 分割された対象は、依存先の関心を内部にカプセル化すること 3. 密結合と疎結合 分割された対象間は、密結合しないよう疎結合を保つこと 4. 疎結合を実現する代表的な手段 インターフェースを活用し疎結合を実現すること その際、結合はDependency Injection Patternなどを利用すること Copyright 2017 @nuits_jp
まとめ ③ これらを実践するにあたり、いくつかのツールを紹介した • Unit Testの実施 → Unit Test Frameworkを利用しよう • Unit Test時のMockについて → Mock生成をサポートするライブラリを使おう • Dependency Injection Pattern → Dependency Injection Containerを使うと良い Copyright 2017 @nuits_jp Slide 74
Thank You! Any Questions?