連載
Javaオブジェクトモデリング
第4回
静的モデル:クラスにおけるUMLとJavaのマッピング(2)
2002/8/24
4. “クラス”のマッピング |
前回(第3回 静的モデル:クラスにおけるUMLとJavaのマッピング(1))は、UMLの“クラス”とJavaの“クラス”についてそれぞれを構成する部品をメタモデルという形で明確化しました。今回は、このメタモデルを比較することによって、UMLとJavaのマッピングを考えていきます。
設計モデルは、プログラミング言語やミドルウェアといった実行環境上における実装を意識したモデルです。本連載は、Javaによる実装をターゲットにしている設計モデルとJavaのマッピングをテーマにしています。このため、UMLを使ってどのようにしてJavaの部品を表現するのか、という点がマッピングを考えるうえでの最重要課題となります。
加えて、オブジェクト指向設計上重要でありながらもJava言語では直接サポートしていないモデル要素についてのJava側での対応も考えていきます。
今回は、以下の戦略によりマッピングを考えていきます。
- Javaの部品はすべて、UMLでの表現を考える
- UMLの部品は、オブジェクト指向設計のテクニック上必要なものを選び、Javaでの実装を考える
まず、UMLクラスとJavaクラスのマッピングについて考えてみましょう。なお、UMLシグナルとUML例外は基本構造がほとんどUMLクラスと同じであり、Java側ではクラスとして実装されるので、クラスのマッピングに含めて考えることにします。
UMLクラスの部品とJavaクラスの部品のマッピングは図1となります。当然ですが、UMLのクラスとJavaのクラスではかなりの部分が共通となっています。
図1 UMLクラスとJavaクラスのマッピング(クリックすると拡大します) |
●4.1.1 共通の部品
UMLのクラス名はJavaでもクラス名となります。UMLの可視性はJavaでも可視性として表現されます。
UMLの属性はJavaではインスタンス変数またはクラス変数となります。インスタンススコープを持つUML属性がJavaではインスタンス変数として、クラススコープを持つUML属性がJavaではクラス変数として実装されます。
UMLにおけるオペレーションは、Javaのメソッド、クラスメソッド、コンストラクタに対応します。
抽象クラスの宣言はUMLとJavaの両方に用意されています。UMLではプロパティにabstractを指定、Javaではクラス宣言で修飾子abstractを指定することで抽象クラスを宣言したことになります。
サブクラスを持たないクラスの宣言もUMLとJavaの両方に用意されています。UMLではプロパティにleafを指定、Javaではクラス宣言で修飾子finalを指定することでサブクラスを持たないクラスを宣言したことになります。
●4.1.2 要検討の部品
activeは、アクティブオブジェクトを示すプロパティです。Javaでは、java.lang.Threadを用いて実装することになるでしょう。
rootは、ルートのクラスであることを示すプロパティです。Javaでは、クラスとしてrootプロパティが対応するのはjava.lang.Objectになります。
●4.1.3 Javaのみの部品
定数はJavaのみに存在する部品です。定数をUMLで表現するためには以下の2つの手法が考えられます。
- 属性を利用する
- 定数専用の並び区画を用意する
●4.1.4 UMLのみの部品
persistenceは、オブジェクトの永続性を示すプロパティです。Javaのオブジェクトは永続性を持っていないので、persistenceに対応するJavaの部品はありません。
■■4.2 インターフェイスのマッピング■■
UMLインターフェイスの部品とJavaインターフェイスの部品のマッピングは図2となります。
図2 UMLインタフェースとJavaインタフェースのマッピング(クリックすると拡大します) |
●4.2.1 共通の部品
まず、共通しているのがインターフェイス名です。可視性も共通しています。UMLのオペレーションはJavaではメソッドに対応します。
●4.2.2 Javaのみの部品
UMLインターフェイスとJavaインターフェイスの最大の相違点は、Javaインターフェイスが定数を宣言できる点です。UMLで定数を表現するには以下の2つの方法が考えられます。
- 属性を拡張する
- 定数宣言専用の並び区画を設ける
インターフェイスという意味で注意しなければならないのは、UMLのインターフェイスでは属性を定義することができないという点です。この問題を満足させる方法という点においては、定数宣言専用の並び区画を用いる方法が有力です。
Javaでは、インターフェイスの修飾子としてstrictfpが指定できます。このstrictfpに相当する機能はUMLにはありません。UMLではプロパティで表現するのが適切でしょう。
●4.2.3 UMLのみの部品
プロパティrootとプロパティleafは、UMLのみに定義された部品ということになります。無理をしてJavaで実装する必要もないので、設計上の補助情報として用いるのがよいでしょう。
■■4.3 “クラス”の種類■■
まずUMLの“クラス”とJavaの“クラス”を列挙し、それぞれの“クラス”の対応関係を見ていきます。UMLの“クラス”は以下のものに分類できます。
- 普通のクラス
- ステレオタイプmetaClassのクラス
- ステレオタイプthreadのクラス
- ステレオタイプutilityのクラス
- インターフェイス
- 例外
- 普通のJavaクラス
- java.lang.Object
- java.lang.Class
- java.lang.Throwable
- java.lang.Error
- java.lang.Exception
- java.lang.RuntimeException
- java.lang.String
- java.lang.Thread
- 普通のインターフェイス
- java.lang.Cloneable
- java.lang.Runnable
- java.io.Serializable
- java.io.Externalizable
図3 UMLとJavaの“クラス”の関係 |
●4.3.1 普通のクラス
UMLにおける「普通のクラス」はJavaでもそのまま「普通のクラス」にマッピングされます。
●4.3.2 metaClass
UMLにおいて「ステレオタイプmetaClassのクラス」は、Javaではjava.lang.Classに相当します。
●4.3.3 thread
UMLの「ステレオタイプthreadのクラス」は、軽量プロセス(いわゆるスレッド)を持ったアクティブオブジェクトの実装を意味します。これは、Javaのjava.lang.Threadに相当すると考えてよいでしょう。
●4.3.4 utility
UMLの「ステレオタイプutilityのクラス」、すなわちユーティリティはJavaに直接対応する機能はありませんが、Javaで実装することは可能です。
Javaでは、クラススコープを持ったメソッドであるクラスメソッドを定義することができます。このクラスメソッドのみを定義したクラスがユーティリティに対応します。このようなユーティリティはJavaにおいても頻出のテクニックです。例えば数値演算の関数をまとめたjava.lang.Mathがこのユーティリティに相当します。
●4.3.5 インターフェイス
UMLの「インターフェイス」は、Javaの「普通のインターフェイス」に相当します。ただし、Javaのインターフェイスでは定数の定義ができる点には注意が必要です。
●4.3.6 シグナル
シグナルはオブジェクト間でやりとりされる非同期の「刺激(stimulus)」を表現する分類子です。
Javaの言語では、java.lang.Throwableがこのシグナルに相当します。java.lang.Throwableは、throw句で送出され、catch句で受信することで、通常の制御フローを飛び越えて、直接制御を移すことができます。
●4.3.7 例外
例外はシグナルの一種で、異常状態を通知するために用いられる分類子です。Javaの言語仕様上一番ぴったりくるのが java.lang.Exceptionです。もちろんjava.lang.Exceptionのサブクラスであるjava.lang.RuntimeExceptionも例外としてモデル化されることになります。単なるjava.lang.Exceptionとjava.lang.RuntimeExceptionでは、重大な機能上の相違があります。しかし、UMLの例外ではこの機能上の相違を十分に表現することができません。この点をどのように実現するかがマッピングにおける検討項目になってきます。
この実現には以下の2つの方法が考えられます。
- 通常のサブクラスの関係としてjava.lang.Exceptionあるいはjava.lang.RuntimeExceptionとのリレーションシップを明記する
- Java用の拡張プロパティ(例えばruntime)を用いて区別する
java.lang.ErrorをUML例外と考えるとステレオタイプexceptionを用いるだけではjava.lang.Exception(java.lang.RuntimeException)のサブクラスである例外なのか、java.lang.Errorのサブクラスである例外なのかの判断がつきにくくなります。この点の区別を明確にするためには、汎化を用いて例外とjava.lang.Errorやjava.lang.Exceptionの関係を明確にする方法が普通です。しかし例外が登場するたびに汎化による指定を毎回行っていては煩雑で仕方がありませんし、クラス図も見づらいものになってしまいます。書きやすさや図の見やすさを考慮するのであれば専用の拡張プロパティを導入する方法が有力です(図4)。
図4 専用の拡張プロパティ |
プロパティなし | java.lang.Exceptionの子孫 |
プロパティruntime | java.lang.RuntimeExceptionの子孫 |
プロパティerror | java.lang.Errorの子孫 |
■■4.4 そのほかのJava“クラス”■■
以上の考察から、残された“クラス”は、UMLに直接対応するモデル要素がないもの、ということになります。このカテゴリの“クラス”としては、java.lang.Object、java.lang.Stringの2つのクラス、java.lang.Cloneable、java.lang.Runnable、java.io.Serializable、java.io.Externalizableの4つのインターフェイスが相当します。
これらの“クラス”に関しては、無理やりマッピングを考えなくても、通常のクラスやインターフェイスと同様の方法を用いることでマッピングを行うことができます。しかしもっと便利な方法があるかもしれないので、ここではもう少し踏み込んで考えてみましょう。
●4.4.1 java.lang.Object
java.lang.Objectは、すべてのJavaオブジェクトのルートとなるオブジェクトです。Javaの文法上は、extendsによる親クラスの指定を行わないクラスの定義が可能ですが、この場合には暗黙的にjava.lang.Objectが親クラスとして使用されます。
ユーザー定義クラスとjava.lang.Objectを表現する方法としては図5に示す方法が考えられます。
図5 Objectの扱い |
(1)はJavaの文法どおり、ユーザー定義クラスとjava.lang.Objectの関係を明示しない方法です。
(2)は、“Object”というクラスを継承する方法です。Objectがjava.lang.Objectと同一のものであるという暗黙の関係を想定しています。
(3)は、java.lang.Objectのパッケージ名を明記した形でユーザー定義クラスとの継承関係を表現する方法です。
(4)は、(3)と同じ意味ですが、パッケージアイコンを利用して、java.lang.Objectのパッケージを表現しています。Javaによる実装モデルをオブジェクトモデルに正確にマッピングすると(3)または(4)となります。しかし、いずれの方法も実務で使用するには煩雑です。
そこで、実用上は(1)の方法を取るのが普通です。つまり、Java文法の暗黙の設定をオブジェクトモデルにそのまま反映させてしまうわけです。
(2)は、(3)や(4)と同じ方法ですが、パッケージを明記していない点が異なります。Javaでは、パッケージjava.langに所属するクラスは、パッケージ名を指定しなくてもそのまま利用できる仕様になっています。この仕様を反映する形で、パッケージ名を省略した“Object”を参照するという方法も選択肢として考えられます。
●4.4.2 java.lang.String
Java言語で文字列を表すjava.lang.Stringは、オブジェクトではありますが、利用のされ方という点ではデータ型的な含みもあります。
一方、UMLではStringというデータ型を用意しており、このUMLのStringとJavaのjava.lang.Stringのマッピングは少し検討が必要となってきます。
取りあえず、今回は「java.lang.StringはUMLのデータ型Stringにマッピングされる」ものと考えておくことにします。この点の考察は次回以降「データ型」のマッピングの検討の中で行う予定です。
●4.4.3 配列
Java言語の文法はC言語の文法を踏襲しているため、Javaの配列もCの配列と同じものと錯覚しがちです。しかし、実際のJavaの配列は、内部の実装は特殊なオブジェクトとなっています。つまり、Javaの変数はオブジェクトへの参照を保持しており、この参照先がたまたま配列オブジェクトだった場合は、配列を操作することになるわけです。もちろんこれは、このような錯覚を起こすようにJava言語がシンタックスシュガーをまぶしているからです。
UMLの属性は多重性や順序性というモデル要素を持っていますが、これらのモデル要素のJavaへのマッピングを考える場合には、配列というオブジェクトへのマッピングを検討しなければなりません。また、java.util.Listなどのコレクションライブラリも検討対象となってきます。
以上の点から、属性の多重性および順序性については、次回以降「アソシエーション」の検討の中で併せて行いたいと思います。
●4.4.4 java.lang.Cloneable
ユーザー定義クラスとjava.lang.Cloneableの関係の表現には図6に示す方法が考えられます。パッケージ名の扱い方については、java.lang.Objectで検討したクラスの場合と同様の論点があります。
図6 Cloneableの扱い |
一般のインターフェイスの場合には、(1)か(2)を取ります。
java.lang.Cloneableのように、実装モデル上特別な意味を持つインターフェイスについては、(3)のプロパティや、(4)のステレオタイプといったUML拡張メカニズムを用いて表現する方法も考えられます。拡張メカニズムを用いる場合には、(3)に示すプロパティを用いる方が適切と考えられます。
●4.4.5 java.lang.Runnable
ユーザー定義クラスとjava.lang.Runnableの関係の表現もjava.lang.Cloneableと同様に以下の選択肢があります。
- 通常のインターフェイスとして表現
- ステレオタイプで表現
- プロパティで表現
java.io.Serializableは、UMLのクラスで利用されるプロパティpersistenceとは意味が異なり、直列化可能という性質を表します。
ユーザー定義クラスとjava.io.Serializableの関係の表現もjava.lang.Cloneableと同様に以下の選択肢があります。
- 通常のインターフェイスとして表現
- ステレオタイプで表現
- プロパティで表現
java.io.Externalizableは、java.io.Serializableのサブインターフェイスであり、java.io.Serializableと同様にUMLのクラスで利 用されるプロパティpersistenceとは意味が異なります。
ユーザー定義クラスとjava.io.Serializableの関係の表現もjava.lang.Cloneableと同様に以下の選択肢があります。
- 通常のインターフェイスとして表現
- ステレオタイプで表現
- プロパティで表現
1/5
|
Javaオブジェクトモデリング 第4回 | |
“クラス”のマッピング | |
属性のマッピング | |
オペレーションのマッピング | |
サンプルプロファイル | |
マッピング例 |
Javaオブジェクトモデリング INDEX |
IT Architect 連載記事一覧 |
アーキテクチャ 新着記事
@IT情報マネジメント 新着記事
この記事に対するご意見をお寄せください managemail@atmarkit.co.jp