第5回 少しだけ高度なモデリング技術(その2)クラスの依存関係と実現関係【改訂版】初歩のUML

 今回も「第4回 少しだけ高度なモデリング技術(その1)」に引き続き、高度なモデリングの考え方についてクラス図を取り上げて説明していきます。今回取り上げるのはクラスの依存関係と実現関係です。

» 2012年10月01日 17時47分 公開
[萩本順三株式会社豆蔵]

依存関係(dependency)とは

 2つのクラスの間に、なんらかの関係があり、その2つのクラス間の依存度合いが非常に弱い場合(疎結合)は、関連よりも依存関係を使うとよいでしょう。依存関係の具体的な例を図1に示します。この例は、会社には複数の部門があり、各部門には複数の社員がいるという、前回紹介したモデルに、会社が社員を雇って部門に登録するという操作と、その操作を実現するための関連を追加したものです。

ALT 図1 「雇う」という操作を追加したモデル

 図1は会社から出ている関連の線が2種類あるので意味的に分かりにくくなってしまいました。この対策を講じたモデルを作成してみましょう(図2図3)。図2は、集約と関連名にて関連の意味を深める方法です。もう一方の図3は依存関係を使っています。両者とも会社-部門-社員という構成・管理のための関連と、「雇う」という操作を実現する関連を分ける目的は同じなのですが、少々モデルの意味合いが異なっています。

 依存関係を使った図3は、関係によって「雇う()」という操作が行われるという点では図2と同様となります。しかし、会社と社員は一時的なつながりであることが依存関係によって強調されていることに注意してください。会社と社員の関係は、会社が「雇う()」という操作の中で、社員インスタンスを生成(create)するだけという意味になります。

 依存関係とは別名、「使用関係」とも呼びます。

ALT 図2 集約と関連名を使って分かりやすくする
ALT 図3 依存関係を使う

 このように、依存関係は一時的に相手オブジェクトを使用する際の関係を表すものです。依存はクラス間に点線矢印を書きます。また線上には、ステレオタイプ(注1)により、依存の意味を書くこともできます。ここでは、“<>”というステレオタイプを使いました。さて、この依存関係における一時的という意味はどういうことなのでしょうか?

これは、2つのインスタンスが関係しあう期間が関連よりも短いということです。

 このことを図3で説明しましょう。会社と部門の関連は、具体的には会社クラスの属性として部門リストがあることを関連で表しています。しかし、依存関係の場合は、社員クラスを属性として保有しません。会社クラスの「雇う()」という操作の際に、社員が受け渡されるものと考えましょう。ですから会社クラスの構造の中枢に社員クラスの関係があるのではなく、会社クラスの一操作の中で一時的に結ばれる関係ということになり、関連よりも両クラスの依存度が低いわけです。依存関係は、名前からしてほかのどの関係より依存度が高そうなのですけどね(笑)。

【注1】ステレオタイプについて

 ステレオタイプとは、UMLの表現を拡張する際に使用されるものです。ステレオタイプは、UMLのさまざまな部分に利用できます。ここでは依存関係における依存の意味をステレオタイプとして表現していますが、クラス図におけるクラスの種別などにも利用できます。依存関係においてよく使われる一般的なステレオタイプとして、「<>

使用する」「<>アクセスする」などがあります。



 依存関係の使われ方としては次のようなケースがあります。

  • モデリングの初期段階(例えば分析時)に関連として表していたが、設計段階に移って、実装にかかわるシンプルな構造に洗練させていく過程で、極めて一時的な関係(引数や戻り値として渡されたオブジェクトにメッセージを送る等)として設計・実装すべきと判断したときに、関連を依存関係に変更する
  • 分析時にクラス図が関連だらけになってしまい非常に分かりにくいモデルになりそうなときに、意味的に考えて一時的な関係と判断したものだけを依存関係に置き換えることで、本質的なクラス構造間の関係(関連)と区別する

 依存関係で注意すべき点があります。依存関係は多重度を表せないため、1対Nの関係を依存関係にすると意味が欠落してしまうということです。あくまでオブジェクト同士の関係が1対1で対応しており、かつ一時的なものを依存関係にすべきでしょう。

 最後に付け加えますが、図2図3をミックスして図4のように表現しても問題ありません。ちなみに筆者はこのケースでは図4を好みます。

ALT 図4 図2と図3をミックス

実現関係(Realization)とは

 実現関係とは、「仕様」と「実現」を表現するための関係です。図5は電話という仕様と、その仕様を実現する携帯電話、PHS、テレビ電話、卓上電話の関係を実現関係で表しているものです。また、人というクラスは電話を使うことを依存関係によって示しています。このモデルが意味するのは「人は、電話という規格を通して具体的な電話を利用する。よって電話のメーカーやタイプが多少異なっていても操作できる」ということです。

ALT 図5 実現関係の例

 さて、ここで「仕様」のことを「電話の規格」といいましたが、具体的にはどんなことを意味しているのでしょうか? つまり、それはオブジェクトの一連の操作インターフェイスだけを定義したもののことです。クラス名の上にステレオタイプを利用して「Interface」と記入します。このステレオタイプは先ほど注釈で説明したようにクラスの種別にステレオタイプを使用した例です。Interfaceというステレオタイプが記入されたクラスは、基本的に操作インターフェイス(関数の宣言)だけが並べられていて、その具体的な実現方法はサブクラスで行われます。Javaを学んでいる方は、もうお分かりですね。そうですJavaではinterfaceがこの実現関係を実現するものです。実現関係は「第3回 モデリングにおける『汎化』と『特化』」で説明した汎化関係の一種と考えることができます。さて、汎化関係の例で挙げた「社員」と「技術者」、「営業員」の関係と、この例の実現関係の違いは何であるのかここで説明することにしましょう。

 汎化関係のモデルとしての「社員」は、「技術者」と「営業員」の共通的な振る舞いの実現方法が組み込まれていますが、実現関係のモデルとしての「電話」は、「携帯電話」や「PHS」の共通的な振る舞いの実現方法が組み込まれていません。なぜなら、操作のインターフェイスが定義されているだけで、具体的な振る舞いはサブクラスで実現するからです。このように、振る舞いの実現方法が組み込まれているか否かで汎化関係と実現関係は区別されますので、実現関係は主に設計段階で多用されます。

Javaで表現する実現関係

 Javaでは、実現関係はinterfaceとclassで表現されます。interfaceとはメソッドの型だけが定義されており、実際のコードを定義することができない特殊なクラスです。interfaceに定義されたメソッドの実際のコードは、interfaceを実現(implements)するクラスによって定義されます。

 ここからは私の考えですが、Javaにおけるinterfaceの利用目的として下記の2つの方法に分類しています。

ケース1:実現関係を表現する

 仕様の固まり(interface)と、実現の固まり(class)に分類する設計です。この効果は、interface(仕様)だけを知っていれば、その実現クラスを知る必要はなくなるので、具体的なクラスを後で組み替えることができるようになるということです。これによりクラスライブラリやアプリケーションに拡張性がもたらされるわけです。この利用方法としては、仕様(interface)だけを提示し、実装クラスは後で(interface提供後に)別の人にやらせるということができるようになります。

 つまり、提供するクラスライブラリに、将来的に導入されるであろう実現クラスを受け入れるための「拡張性の枠組み」が装備されるのです。図6は、電話インターフェイスを実現する携帯電話クラスと卓上電話クラスという関係を示していますが、携帯電話クラスが後で作成されても、電話インターフェイスに従っていれば、利用者は電話としての基本的な使い方を理解できます。例えば、JavaではRDBアクセスでおなじみのjava.sql.Driver、java.sql.Connection、java.sql.ResultSetがinterfaceとして提供されています。データベースベンダが独自のRDB仕様に合わせて、これらのinterfaceを実現するクラスを提供することで、利用者は、データベースベンダの違いを意識せずにRDBを利用できるようになっています。

ALT 図6 実現関係を表現する

ケース2:ロール(役割)を表現する

 ロール(役割)とは、クラスの実現すべき操作の一部を抽出してinterfaceとしたものです。例えば、Javaのjava.lang.Threadクラスとjava.lang.Runnableインターフェイスのような関係です。Threadクラスは、Runnableインターフェイス中のrunメソッドを実現しているオブジェクトであることを条件に、VMから生成されたスレッドコンテキストを使ってrunを呼び出します。すなわちRunnableインターフェイスを実装するクラスは、「VMから生成された新たなスレッドに呼び出される」というロールを持つクラスとして実現されなければならないのです。その際に、そのクラスが具体的にどのようなクラス(どこから継承したかなど)であるかはThread利用においては問われません。

 このようにロールを表現するためにinterfaceを使っているほかの例として、Swingのイベントの受け先というロールを表すjava.util.EventListenerなどがあります。これらは、役割を視点にした抽象化(ポリモルフィズム)を実現するために使われます。これによりクラス間の関係を疎結合にすることができます。例えばThreadの例では、ThreadクラスとRunnableインターフェイスを実現するクラスの関係は疎結合になっています。唯一強制されることは、Threadクラスから呼び出されるクラスは、Runnableインターフェイスで定義されたrunメソッドを実現することです。

ALT 図7 ロール(役割)を表現する

 このようなロールを表現した実現関係では、利用されるクラスは、インターフェイスを介して利用されるクラスとの関係を持つことになります。この点を強調する方法として図8のような省略表記が提供されています。インターフェイスを○(ロリポップ)で表します。

ALT 図8 実現関係の省略表記

 この表記の意味を図9で説明します。インターフェイス(Runnable)を実現するクラス(ユーザーが実装するクラス)は、インターフェイスで定義されたメソッドを実現する責務があります。インターフェイス(Runnable)を利用するクラス(Thread)は、この責務を守るクラス(runメソッドを実装するクラス)ならば、特に相手のクラスが何であるか知らなくても呼び出せます。つまりインターフェイス(Runnable)という情報を唯一の契約条項として、両者(Threadとユーザーが実装するクラス)が結び付いているというのが分かりやすく表現されています(図9)。

ALT 図9 契約条項としてインターフェイスを使う

 なお、この契約が守られているか否かは、インターフェイス(Runnable)を実現(implements)しているクラスコードをコンパイルによってチェックされるので安心できます。具体的には、もしRunnableをimplementsするクラスのソース中のrunメソッドをオーバーライドしていない場合、コンパイルエラーになります。

 図8の省略表記は、「ケース1:実現関係を表現する」よりも「ケース2:ロール(役割)を表現する」方によく使われるでしょう。ただし、インターフェイスで定義された操作リストは表現しづらい面があるので、あくまで図7のクラス図の中にあるインターフェイスの実現と利用の関係を強調する補足図として利用されることをお勧めします。

 さて、ここから話が混乱しそうで躊躇しますが、思い切って話します。実は、ケース1もよく考えるとロールを表現していると見なせます。つまりクラスは何らかの意図を持って作られるのですから、その意図をロールと考えると、仕様はクラスの全体的なロールと見ることもできるのです。では、なぜケース1とケース2を私は分けているのでしょうか。

 それは、設計目的が異なるからです。ケース1は、クラスの実現すべき仕様と実装方法を分けることが設計目的となります。ケース2は、クラス全体の仕様の検討とは別に、他のオブジェクトの契約事項の仕様をinterfaceとして取りまとめ、自クラスの利用者に提供したり、逆にinterfaceとして他のクラス提供者から提供されたりしたものを実現するという設計の観点が必要とされます。

 私が、ケース1とケース2を分けている理由は、微妙な差ではありますが両者の設計目的の違いを意識しておくことが重要だと思うからです。例えば、ケース1の場合は、クラス名やクラスの役割については、仕様(interface)に沿った定義をすべきでしょう。ケース2の場合は、提供または提供される仕様(interface)とは別の観点で、クラス名やクラスの役割について検討すべきという事を意識しておいた方が、設計に誤りが少なくなると考えています。もちろん、両方の設計目的が混在するケースもあるのですが、両方の違いを意識する必要性があると思うのです。

 いかがでしょうか、Javaにおけるinterfaceの2つの利用目的の違いがお分かりでしょうか。それとも両者の違いはなしとお考えになりますか?

もしそうだとしてもあなたは間違っているわけではありません(コラム参照)。

(コラム)現実世界の認識と仮想世界のモデル認識

 最近思うのは、モデルの解釈・意味論の世界は文化・経験によって大きく異なってしまうものだということです。もし皆さんがJavaにおけるinterfaceの2つの利用目的について同様の認識を持っていただけるならば、この概念は共通認識になりやすいものでしょうから、ほかの方にも広めていってください。このようなモデルの解釈は、時代とともに共通認識となり広められ、いつのまにか当たり前の概念となりますが、時代の変化・技術の変化によって微妙に解釈が変化するものなのです。

 こんなことをいうと、「そんな解釈が異なるモデリングの認識などソフトウェア開発で頼れるわけがないじゃないか」とおしかりを受けるかもしれませんが、現実世界も目を瞑(つむ)ればイメージの世界です。果たしてみんなが同じ現実世界を見て、同じイメージを見ているのでしょうか?

人は、現実世界の認識も仮想世界のモデルの認識も、目を瞑(つむ)ってイメージ処理を行う際には同じように行っているように思います。仮想世界のモデルについて共通認識ができていると思う部分を増やしていく努力こそ、新たな構造(世界)をつくり出せるものだと思うのですが、皆さんはこのことをどうお考えですか?



 今回はこれでおしまいです。

 次回は、いままで説明してきた関係を整理した後、クラス図から動的な側面を読み取る方法についてUMLの動的モデルを交えながら説明していくことにします。

Copyright © ITmedia, Inc. All Rights Reserved.

注目のテーマ