連載
|
|
Page1
Page2
|
デザインパターン習得に必要なオブジェクト指向設計の基本
「デザインパターンは教科書だけを読んで学ぶものではない。実践を通して理解する必要がある」
デザインパターンを習得する最も良い方法は、実際に使ってみることである。実践を通して学ばなければ、決して身に付かないだろう。しかし、デザインパターンは一般的にオブジェクト指向設計に特化したパターンであり、やみくもに使ってみたところで理解しがたいパターンもある。まずはオブジェクト指向設計の基本を理解し、各デザインパターンがどのように良い設計なのかを判断できるようになっておくべきだ。
●オブジェクトへの責任割り当ての基本原則「GRASPパターン」
「オブジェクト指向設計の基本とは、適切なクラスに適切な責任を割り当てることである」
ここでいう責任とは、クラスが何らかの処理を実行する責任(実行責任)と、クラスが持つ情報を把握する責任(情報把握責任)のことである。
では、どのような方針に基づいて責任を割り当てればよいのであろうか。それを知るには、オブジェクト設計および責任割り当ての基本原則をパターンの形式で記述した「GRASPパターン」が良い指針となる。
GRASPパターンは、クレーグ・ラーマン氏による書籍『実践UML――パターンによる統一プロセスガイド』(以降、書籍『実践UML』)の中で、以下のように説明されている。
GRASPは、General(汎用)Responsibility(責任)Assignment(割り当て)Software(ソフトウェア)Patterns(パターン)を表す頭字語です。オブジェクト指向ソフトウェアの設計を成功に導くにはこれらの原則の「理解」(grasping)が重要であることを表すために、この GRASP という名前が選ばれました。 |
GRASPパターンに含まれている各パターンは、以下に示しているように、それぞれがシンプルな原則ばかりで、熟練したオブジェクト指向開発者であれば、その名前は知らなかったとしても、基本原則として無意識のうちに適用しているものばかりだろう。
「GoFのデザインパターン」の多くも、GRASPパターンに含まれている「多相性パターン」「間接化パターン」「純粋架空物パターン」「バリエーション防護パターン」などに基づいている。前回の記事で紹介したStateパターンの状態オブジェクトは「純粋架空物パターン」に基づいた便宜上のクラスであり、その振る舞いの変化は「多相性パターン」に基づいている。この例からも分かるように、GRASPパターンはデザインパターンのビルディング・ブロックとなる存在なのである。
もしGRASPパターンの基本原則に目新しさを覚えるようであれば、デザインパターンを学習する前に、GRASPパターンを理解することから始めてもらいたい。以下に書籍『実践UML』に記載されている9つのGRASPパターンを紹介する。
パターン名 | 問題 | 解決策 |
情報エキスパート(Information Expert) | クラスの責任割り当てに関する最も一般的な原則は何か | 責任を遂行するために必要な情報を保持しているクラスに対して責任を割り当てる |
生成者(Creator) | クラスのインスタンスを生成する責任は、どのようなクラスに割り当てるべきか | ・生成するクラスを集約する、または包含するクラスに対して責任を割り当てる ・生成するクラスを直接的に使用するクラスに対して責任を割り当てる ・生成するクラスを初期化するために必要な情報を持っているクラスに対して責任を割り当てる |
高凝集性(High Cohesion) | 複雑性を軽減し、コントロールしやすくするためには、どのようにすればよいか | 凝集度(クラスに含まれる機能性のまとまり)が高くなるように責任を配置する(凝集度について詳しくは後述する) |
疎結合性(Low Coupling) | クラス間の依存性を低くし、変更容易性を向上させるためには、どのようにすればよいか | 結合度(ほかのクラスとの結び付きの強さ)が低くなるように責任を配置する(結合度について詳しくは後述する) |
コントローラ(Controller) | システム・イベントを処理する責任は、どのようなクラスに割り当てるべきか | システム・イベント・メッセージを受け取ったり処理したりする責任は、コントローラ・クラスに割り当てる。コントローラ・クラスの候補として、システム全体またはサブシステムを表現するクラス、システム・イベントを駆動するユースケースを表現するクラスなどがある |
多相性(Polymorphism) | オブジェクトの振る舞いが型(クラス)によって変化するような場合は、どのようにすればよいか。また、新たな振る舞いを容易に追加できるようにするには、どのようにすればよいか | 選択肢や振る舞いがクラスによって変化する場合は、それらのクラスに対してポリモーフィックな操作を行い、クラスごとに異なる振る舞いのための責任を割り当てる |
間接化(Indirection) | クラス同士が直接的に結合しないようにするには、どのようなクラスに責任を割り当てればよいか。また、クラス同士が直接的に結合しないようにクラス分割を行うには、どのようにすればよいか | 2つ以上のクラスを仲介するような間接的なクラスを導入し、そのクラスに対して責任を割り当てる |
純粋架空物(Pure Fabrication) | 凝集度を高く、結合度を低くしたいが、情報エキスパート・パターンに基づいて問題領域内のクラスに責任を割り当てることが不適切な場合は、どのようなクラスに責任を割り当てればよいか | 問題領域の概念には含まれない人工的な便宜上のクラスを用意し、そのクラスに対して限定されたタスクを実行するための責任を割り当てる |
バリエーション防護(Protected Variations) | システム、サブシステム、オブジェクトなどの要素における不安定箇所やバリエーションの影響が、ほかの要素に悪影響を及ぼさないようにするには、どのようにすればよいか | 安定している個所と、不安定な個所またはバリエーションの接点を見つけて、その接点の周辺に安定したインターフェイスが作成されるようにする。ここでのインターフェイスは広い意味でのインターフェイスを指し、特定の言語が用意する型としてのインターフェイスだけを指すものではない |
9つのGRASPパターン |
●凝集度と結合度
GRASPパターンの核となる概念は、凝集度(Cohesion)と結合度(Coupling)である。オブジェクト指向の優れた設計は、「凝集度が高く、結合度が低い」と表現されることが多い。
凝集度とは、個々の部品内の機能的な結び付きの尺度である。例えば1つの部品が、互いに関連のある機能だけで構成されていれば、凝集度が高くなり、部品が持つ役割や意図が明確になる。逆に関連のない機能の寄せ集めで部品が構成されていれば、理解しにくく、要求の変更にも対処しづらいものとなる。
結合度とは、個々の部品間での結び付きの尺度である。ここでの結び付きとは、部品間の物理的、意味的な関連のことである。例えば2つの部品が強く関連し合っていると、結合度は高くなり、片方を変更すればその影響が他方にも及び、両方を変更せざるをえなくなる。そこで、ソフトウェアを構成する部品は、互いに低い結合度で関連し合っていることが望ましいとされるのである。
凝集度と結合度は、平澤 章氏による書籍『オブジェクト指向でなぜつくるのか』の中で、以下のように実に分かりやすく説明されている。
くだけた表現をするなら、個々の部品のまとまりは「ギュッ」と強くし、部品と部品の間のやりとりは「アッサリ」と弱くする、と言えるでしょう。よそ者は排除して身内の結束を固め、近所づきあいはできる限り少なくするイメージと覚えてもいいかもしれません。 |
GRASPパターンでは、これらの概念が「高凝集性(High Cohesion)パターン」および「疎結合性(Low Coupling)パターン」として、明示化されている。この2つのパターンはほかのGRASPパターンの根底にもなっている。例えば「情報エキスパート・パターン」に基づいて設計されたクラス群は、それぞれのクラスに必要な情報が集約されるので、「高凝集性」が支援される。また、オブジェクトが自身の情報のみを使用すればよいので、ほかのオブジェクトとの関連性が低くなり、「疎結合性」が支援される。
GRASPパターンを指針とする場合の注意点も挙げておこう。クラスに責任を割り当てる場合には、GRASPパターンの各パターンを単体で指針とするのではなく、複数の視点から考慮する必要がある。なぜなら、GRASPパターンの各パターンは、オブジェクト指向設計評価用のパターンであり、ほかのパターンと併用して評価すべきものであるためだ。つまり、1つのパターンの条件を満たしていたとしても、ほかのパターンの条件を満たしていないという場合が起こり得るのである。
例えば、エンティティ・クラスをデータベースに永続化する必要がある場合に、「情報エキスパート・パターン」にのみ基づけば、エンティティ・クラスに直接データアクセス・ロジックを記述してしまうことになるだろう。このような場合には、「高凝集性パターン」や「疎結合性パターン」の条件を満たしつつ「純粋架空物パターン」の適用を考えることで、ビジネス・ロジックとデータアクセス・ロジックが分離された、良い設計へと導くことができるのである(エンティティ・クラスやビジネス・ロジック、データアクセス・ロジックなどの分離については、「連載:アプリケーション・アーキテクチャ設計入門」が参考になる)。
■
今回は、デザインパターンとはどのようなものかという考察と、デザインパターンを習得するために必要なオブジェクト指向設計の基本となる責任の割り当てと、それを評価するパターンとしてのGRASPパターンを紹介した。
次回からは、リファクタリングからデザインパターンを導く方法を、具体的なコードを中心に説明していく予定だ。
INDEX | ||
.NETで始めるデザインパターン | ||
第2回 うまくデザインパターンを使うための心得 | ||
1.デザインパターンとは何か | ||
2.デザインパターン習得に必要なオブジェクト指向設計の基本 | ||
「.NETで始めるデザインパターン」 |
- 第2回 簡潔なコーディングのために (2017/7/26)
ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている - 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう - 第1回 明瞭なコーディングのために (2017/7/19)
C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える - Presentation Translator (2017/7/18)
Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
|
|