うまくデザインパターンを使うための心得


デザインパターン

デザインパターンがリファクタリングの結果として生まれたものであるとすると、それを最初か

  らシステムに適用するのは容易ではない。それができるようになるには、それなりの経験やス
  キルが必要となる。そのため、デザインパターンを念頭に置いての設計しか考えないというのも
  お勧めできるものではない。知っているデザインパターンをシステムに当てはめるだけでは、た
  だの「ワンパターン」になってしまう。
  • やみくもにデザインパターンを使用せず、リファクタリングからデザインパターンを導こう。その
      結果として、必要な場所に必要なだけのデザインパターンが使われるはずだ。これがうまくデ
      ザインパターンを使いこなすための1つの解となるのではないだろうか。

オブジェクトへの責任割り当ての基本原則「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)システム、サブシステム、オブジェクトなどの要素における不安定箇所やバリエーションの影響が、ほかの要素に悪影響を及ぼさないようにするには、どのようにすればよいか安定している個所と、不安定な個所またはバリエーションの接点を見つけて、その接点の周辺に安定したインターフェイスが作成されるようにする。ここでのインターフェイスは広い意味でのインターフェイスを指し、特定の言語が用意する型としてのインターフェイスだけを指すものではない