正しい設計と理想的なモデル良い設計モデルの作り方(前編)

» 2004年03月31日 12時00分 公開
[後藤啓ボーランド]

 現在では、開発現場で「UML」という言葉が一般化し、「オブジェクト指向」という言葉もごく当たり前のように使われるようになりました。実際にプロジェクトの中で、UMLを用いて分析/設計を行い、開発を進めている開発者や、これから始めようとしている開発者もかなりの数に上ると思います。

 しかし、「UMLの表記方法や、ビジネスモデルをオブジェクト指向で分析する方法は、ある程度理解できるのだが、実際に出来上がった分析モデルをどのように設計モデルにしていくか?」または「設計モデルは作成したが、出来上がった設計モデルは、実際にどれほど有効なのか?」という疑問を持っている方も多いと思います。本連載では「良い設計モデルとは何を意味するのか?」「どうすれば良い設計モデルを作ることができるのか?」というテーマについて、前後編の2回で皆さんと一緒に考えていきます。

 前半は分析/設計モデルに関する概念的な解説を行い、後半で具体的な手法、例えば「メトリックス」「リファクタリング」「デザインパターン」などを取り上げ、実際に“良いモデル”を作成するノウハウをお伝えしようと思います。それではまず、分析モデルと設計モデルの作成方法の違いについて解説しましょう。

目指すべき分析モデルの形

 分析モデルの作成方法については、多くの設計者や方法論者がさまざまな方法を主張しています。ここでは詳細には述べませんが、例えば、ワード・カニンガム(Ward Cunningham)による「CRC分析法(ブレーンストーミング)」(注1)や「名詞・動詞分析法」(注2)、ピーター・コード(Peter Coad)により『Java Modeling in Color with UML』(Prentice Hall刊)の中で紹介されている設計手法(注3)などがあります。

CRC分析法(ブレーンストーミング)

 クラス(Class)、債務(Responsibility)、協調(Collaboration)に分けられたCRCカードを用いて分析していく方法です。詳細は『Object Design: Roles, Responsibilities, and Collaborations』(Rebecca Wirfs-Brock/Alan McKean著、Addison-Wesley刊)をご参照ください。


名詞・動詞分析法

 ユースケースや用語集から、名詞、名詞句を探し出して、クラスの候補とし、動詞、動詞句を探し出して、メソッドの候補として作成していく方法。


『Java Modeling in Color with UML』(Prentice Hal刊)の中で紹介されている設計手法

 4つのアーキタイプ、瞬間―時間間隔、役割、パーティ/場所/物、説明に分類し、色分けして、パターンに当てはめて分析していく方法。特に分析モデルに限ったものではありませんが、最初のモデルを作成する場合の強力な手法となっています。



 つまり、当然分析モデルなのですから、現状のビジネス(これから作ろうとするシステム)を実現することが可能なオブジェクトモデルを分析し、作成することになります。分析モデル作成の段階では、それぞれのオブジェクトがどんなに複雑に関連していてもよいわけで(依存性が少ないに越したことはありませんが)、「継承関係」や「関連の誘導可能性」(Navigatablility)を厳密に定義することは、実はあまり重要な作業ではないのです。それよりも、実際のオブジェクトがどのような関連にあり、協調作業によって何を(どのような機能を)実現しなければならないかということを明らかにするのが、正確で、分かりやすいモデルというわけです。この段階では目指すべきモデルとはまさにそのようなモデルであるといえます。

設計モデルの理想

 設計モデルの段階における理想的なモデルとはどんなモデルでしょうか。設計段階のモデルに従って実装が行われ、その結果システムが出来上がるわけなので、生産性が高く、保守性や拡張性が考慮されていなければなりません。そのうえで、実装担当者にとって理解しやすいモデルが「良い設計モデル」ということになります。

 なお、ここでいう設計モデルとは、「実際に実装可能であり、(そのモデルから直接)コーディングが可能であるレベルに到達しているモデル」を指しています。設計モデルの在り方というのはかなりあいまいで、分析モデルを単に詳細化した状態が設計モデルであると考える人もいるのですが、本稿では、上記のように考えてください。

 また、最近のアジャイル系開発プロセスでは、モデリングを分析段階と設計段階に分けず、モデリング=設計(Design)と考えたり、XPのように、コーディング作業にモデリング作業を含んでしまうプロセスもありますが、本稿では、分析と設計の考え方の違いを解説するために、あえて分析モデルと設計モデルを分けて表現しています。

コラム

 アジャイル系開発プロセスが必ずしも、分析モデル(あるいはモデリング作業自体)を必要としていないわけではありません。

 

 実際、モデリングを設計と同義だと仮定してみましょうか。その場合でも、設計モデル自体は、業務分析(システム分析といってもよい)を行い、その分析モデルをインプットとして作成しなくてはなりません。XPの場合でも、要求仕様(XPの場合にはストーリー・カードといいます)を固めてから、直接コーディング作業に突入するわけではありません。アジャイル系開発プロセスはあくまで、「分析、設計といったフェイズに分け、成果物として膨大なダイアグラムを管理する行為は無駄である」という認識の下に立っているだけなのです。本稿とは直接関係がないので詳しくは述べませんが、興味のある方は、Scott W. Amblerの『Agile Modeling: Effective Practices for Extreme Programming and the Unified Process』(John Wiley & Sons Inc刊)をご参照ください。



オブジェクト指向技術の生い立ち

 さて、ここで、あえて「オブジェクト指向設計って何がいいの?」という話をします。そもそも現在のオブジェクト指向言語は、後のPascalやModula-2などの構造化言語のもととなるALGOL60から派生しています。ALGOL60はブロック構造言語で、変数をブロック上にしか持てなかったのですが、SIMULA67で現在のように、メモリ領域に変数を確保できるようになりました。その時点で、現在のクラスや継承、属性の可視性といった概念が加わり、データの抽象化が可能となりました。

 つまりオブジェクト指向とは「実世界を忠実に表現する」というよりは、構造化やデータの抽象化によってモジュール化を強化し、「プログラムの依存性を制御する」という概念から生まれています。『Agile Software Development: Principles, Patterns, and Practices』(Prentice Hall刊)の著者であるRobert C. Martinの言葉を借りるとオブジェクト指向技術とは「モジュール間の相互依存性を制御することができるソフトウェア開発のための設計手法であり、抽象化を用いることにより、依存性を制御する(モジュール間で、モジュール同士が直接依存することを避ける)壁を作り(独立性を高め)、ソフトウェアの陳腐化(保守や拡張ができなくなる状態)を防止する」となります。ここで、もう少しかみ砕いて、非常に簡単な例ですが、「抽象化を用いて、依存性を制御する」という具体的な例を紹介します。

ALT 図1 詳細に依存がある場合

 図1では、ButtonクラスはLightクラスに依存しています。この状態で、新たなボタンによる操作でOn/Offできるようなクラス(例えば、TelevisionとかStereoなど)を導入すると、Buttonクラスは、新たに追加されたクラスに常に依存することになります。この例は、非常に単純です。実際のシステムでは、依存性が高くなれば、拡張性や保守性がどんどん低下していく状況が発生します。

 次に、この例を「抽象化」を用いて、Buttonクラスが直接Lightクラスに依存しないようにしてみましょう。

ALT 図2 抽象化により依存性を制御した場合

 図2では、ISwitchableというインターフェイスを登場させ、抽象化することにより、ButtonクラスとLightクラスの依存性を排除しました。つまり、Buttonクラスは、Lightクラスの実装がどのように変更されても、LightクラスがISwitchableインターフェイスを実装している限り、影響を受けない(依存していない)わけです(実際には、Buttonクラス自体はLightクラスがISwitchableクラスを実装しようがしまいが、この時点では、影響を受けないのですが)。また、こうすることにより、Buttonクラスは、ISwitchableインターフェイスを実装している任意のクラス(例えば、TelevisionとかStereoなど)を同様に扱うことができるようになり、ISwitchableインターフェイスを「プラグイン・ポイント」として、Buttonクラス自体を全く変更することなく、新たなクラスを追加可能となります。

 これは、「Abstract Serverパターン」というもので、単純ですが「抽象化を用いて、依存性を制御する」ということを端的に示しています。

 この場合の「抽象化」は「依存性を制御する」ためのものだと考える方が理解しやすいと思います。また、こういった抽象化は、分析レベルでは、あまり必要がないのですが、設計レベルでは、実際にそのモデルをシステム化する必要があるため非常に重要な要素になってきます。つまり、「依存性を制御する」とは、「それぞれのクラスは、ほかのクラス、またはほかのクラスの詳細をできるだけ知らないように制御する」ということなのです。こう考えると、オブジェクト指向技術も、昔ながらの構造化プログラミングも、そんなに、かけ離れた技術ではないような気がしませんか?

設計の重要性

 ここで、設計がうまくなされていないシステムでは、どういった症状が起こるかを見ていきたいと思います。前述のRobert C. Martinによれば、

[融通が利かない]

システムの一部を変更しようとすると、その変更のために、システムの非常に多くのほかの部分の変更が余儀なくされるため、変更が行いにくい

[壊れやすい]

システムの一部の変更が、論理的には、まったく関連のないはずの部分を動かなくする(壊す)

[再利用性がない]

システムが複雑に絡み合っており、ほかのシステムに再利用するためのコンポーネント化をすることが難しい

[粘性が高い]

正しい(設計の見直しを含む)修正を行う方が、不正な(場当たり的な)修正を行うより難しい状況(環境や設計を含む)

[不必要な複雑さ]

システムに直接有益ではない、複雑なインフラストラクチャを含んでいる

[不必要な重複]

統一的な抽象化で置き換えが可能な、不必要な重複構造を含んでいる

[不透明]

システムが理解しにくい。やりたいことがうまく表現されていない

 結局、「上記のような症状があるシステムは、うまく設計されていない」となります。詳細は前述の『Agile Software Development: Principles, Patterns, and Practices』(Prentice Hall刊)をご参照ください。

 さて、皆さんも「1カ所を少し変更しただけなのに、その変更の影響が、どんどん波及して、なかなか修正が完了しない」という状況や「1つのバグを修正したら、全然関係ない個所が動かなくなり、そのバグの修正が、次々に新たなバグを生み出す」という状況に出くわした経験が少なからずあるのではないですか? これだけJ2EEベースのWebシステムが多い中で、Javaのシンタックスを使った、複雑に絡み合った(昔風にいうとスパゲティ・プログラムというやつです)手続き指向システムが少なからず存在することも事実ですね。また、再利用性とかコンポーネント化というのも同様で、「1つの部品(コンポーネント)をほかのプロジェクトで使用しようとしたら、あらゆる部分が関連していて、とてもその部分だけを切り出すことができず、仕方がないのでソースコードをコピーして、いらない部分を削って作り直した」という経験もあると思います。

 こういった現象は「正しい設計がなされていない」ために起きているわけで、「オブジェクト指向技術を使って、正しく設計を行えば、こういった事態を減少させることができる」ということになるわけです。しかし、ここで重要なのが「正しく設計を行えば」という部分です。つまり、当たり前ですが、オブジェクト指向技術で、カプセル化や抽象化を使って、積極的にモジュールの依存性を制御したり、複雑性を排除していかないと、システムが大規模になればなるほど、モジュール間の依存性が高まり、手の付けられない状況に簡単に陥ってしまうということです。

 ここで、よくあるWebシステムの例を考えてみましょう。「アーキテクチャにはMVCモデルを使用し、独自のフレームワークを作成して、データアクセスにはDAO(Data Access Object)を使用した」Webシステムです。これだけ聞くと、何か素晴らしい設計がなされているような気がしますね。

ALT 図3 リーズナブルなパッケージ依存性

 本来なら、図3のような、パッケージ間の依存性があり、ドメインオブジェクトはフレームワーク内の抽象化されたインターフェイスを通してのみ、DOAにアクセスし、DOAもドメインオブジェクトに直接依存しない形(同様にフレームワーク内のユーティリティ・クラスを使用するなど)が望ましいでしょう。

ALT 図4 複雑なパッケージ依存性

 ですが、何も考えないで開発を行うと、図4のように、ドメインオブジェクトとDOAの間に双方向の依存性ができたり、直接ActionクラスからDOAを呼び出したりと、せっかくのパッケージングやモデルを台無しにしてしまう可能性もあるわけです。このように依存性の上に成立しているシステムでは、それがたまたま正常に動いていたとしても、何かを変更したら、どんなことが起こっても不思議はない気がします。

 また、最近よく見掛けるのですが、「分析クラスを、そのままDBのスキーマに置き換え、それとマッピングしたJavaのクラスをValue Objectと称して、アクションクラスに直接ビジネス・ロジックを埋め込んでいるパターン」も、「正しく設計されている」とはいい難いと思います(そもそも、これでは、「正しいMVCモデル」ともいい難いのですが)。ビジネス・ロジックは、あくまで、ドメイン・モデル上にあり、コントローラは、それをViewと結び付けるために、ドメイン・モデル上のメソッドを呼び出すように設計すべきです。そうしないと、例えば、プレゼンテーション層のフレームワークが変わっただけで、システムの大部分を書き直さなければならないし、モデルの再利用性も極端に少なくなります。

 この辺も、結局、正しい設計が行われていない(もう一度いいますが、設計とは、そのモデルに従って、実際のコーディングが可能というレベルを指します)ために起こる現象の1つです。オブジェクト指向分析を行って、分析モデルを作成し、後は「設計をせず」、一気にコーディングしてしまう(または、アウトソースして、コーディングを依頼する)と、こういった状況からなかなか抜けきれないかもしれません。


 今回は、「良い設計モデルとは何か?」「どうすれば良い設計モデルを作ることができるのか?」について、概念的な話を中心に解説しました。当然のことですが、オブジェクト指向技術とは、ソフトウェアをより効率的に開発するために考え出された技術です。その技術を使ってより効率的にソフトウェア開発を進めることができなければ意味がないのです。次回は、もう少し具体的に、メトリックスにより、デザインパターンやリファクタリングを使って、設計を進める方法を解説します。

著者プロフィール

後藤啓(ごとうあきら)

 1990年Smalltalkに触れ、オブジェクト指向開発に興味を持ち、以降、C++による、OODB、CORBA関連の開発に従事、その後、J2EE関連のコンサルタントとして、多数のプロジェクトに参加。UMLやモデリングの重要性と、いかに効率よく開発を行うかを中心にコンサルティングに従事。


Copyright © ITmedia, Inc. All Rights Reserved.

注目のテーマ