Javaによる具象クラスの実現例はリスト6のようになります
public class Account { private String id; private String name; private long balance; public Account(String id, String name) { this.id = id; this.name = name; this.balance = 0; } public void setBalance(long balance) { this.balance = balance; } public long getBalance() { return (balance); } public void deposit(long balance) { this.balance += balance; } public void withdraw(long balance) throws IllegalArgumentException { if (this.balance < balance) { throw (new IllegalArgumentException("insufficient balance")); } this.balance -= balance; } }
このJavaクラスAccountは典型的な具象クラスです。インスタンス変数はすべて可視性をprivateにして、外部からの不用意なアクセスを不可能にしています。インスタンス変数に対するアクセスはオペレーションを通して行うようになっています。
具象クラスAccountをUMLで表現したものが図13です。
Accountはコンストラクタを1つ定義しています。これをUML上で表現しているのが、ステレオタイプcreateを設定したオペレーションAccountです。
withdrawメソッドはthrows句で送出する可能性のある例外としてIllegalArgumentExceptionを宣言しています。このようなオペレーションと例外の関係はsendディペンデンシィで表現されます。
例外はステレオタイプexceptionを設定したクラスとして表現されます。IllegalArgumentExceptionはjava.lang.RuntimeExceptionの子孫クラスであり、検査なし例外と呼ばれているものです。この点をプロパティruntimeで表現しています。
抽象クラスの実現例はリスト7のようになります。
public abstract class AbstractItem { private String id; private String name; protected AbstractItem(String id) { this.id = id; } public final String getId() { return (id); } public void setName(String name) { this.name = name; } public String getName() { return (name); } public abstract long getPrice() throws java.io.IOException; }
このJavaクラスAbstractItemは典型的な抽象クラスです。抽象クラスはクライアントオブジェクトから直接生成されることはないので、コンストラクタの可視性をprotectedにしています。
抽象クラスにつきものの抽象メソッドであるgetPriceを定義しています。また、getIdメソッドがfinalとなっており、サブクラスでのオーバーライドを許さないことが明示されています。抽象クラスAbstractItemをUMLで表現したものが図14です。
クラス名AbstractItemがイタリック体で表示されており、AbstractItemが抽象クラスであることを示しています。抽象メソッドgetPriceもイタリック体で表示されています。
コンストラクタはステレオタイプcreateのオペレーションで表現されています。可視性protectedもそのままUMLで表現されています。
getIdメソッドのfinalは、プロパティfinalとして表現しています。ここを標準プロパティleafで表現することも可能です。
インターフェイスの実現例はリスト8のようになります。
public interface IItem { String getId(); String getName(); String getPrice() throws java.io.IOException; }
インターフェイスIItemをUMLで表現したものが図15です。
図15は、ステレオタイプinterfaceを用いてインターフェイスであることを表現しています。インターフェイスは、それ自身がインスタンス化されることはなく、また定義してあるオペレーションはすべて抽象オペレーションとなります。通常のクラスであればインターフェイス名やオペレーションは、プロパティabstractを明記するか、オペレーションをイタリック体で表示するところですが、インターフェイスの場合は、通常の表示のままでよいことになっています。
定数を使ったインターフェイスの実現例はリスト9となります。
public interface IScheduleable { int PRI_HIGH = 1; int PRI_MIDDLE = 2; int PRI_LOW = 3; void setPriority(int priority); int getPriority(); }
setPriorityメソッドやgetPriorityメソッドで扱うint型のデータに用いる定数としてPRI_HIGH、PRI_MIDDLE、PRI_LOWの3つを定義しています。このインターフェイスIScheduleableをUMLで表現したものが図16です。
定数はUMLの仕様外です。属性で表現することもできなくはありませんが、ここでは定数用のオプションの並び区画を設け、そこにJavaの文法の文字列を記述するという方法を用いています。
Javaによるユーティリティの実現例はリスト10となります。
public class Calc { public static Integer plus(Integer lhs, Integer rhs) { return (new Integer(lhs.getIntValue() + rhs.getIntValue())); } public static Integer minus(Integer lhs, Integer rhs) { return (new Integer(lhs.getIntValue() - rhs.getIntValue())); } }
このユーティリティCalcをUMLで表現したものが図17です。
定義されているオペレーションはすべてクラススコープなので通常のクラスであれば下線を引いて表現するところですが、ユーティリティの場合は、普通のオペレーションと同様の記述でよいことになっています。
インスタンススコープのオペレーションとクラススコープのオペレーションが混在する例として、ファクトリオブジェクトについて考えます。Javaによるファクトリの実現例はリスト11となります。
public class ItemFactory { private static ItemFactory factory = null; private int numberOfItems = 0; private ItemFactory() { } public Item getItem(String name) { numberOfItems++; return (new Item(name)); } public int getNumberOfItems() { return (numberOfItems); } public static ItemFactory newInstance() { if (factory == null) { factory = new ItemFactory(); } return (factory); } }
ファクトリでは、オブジェクトの生成を自分自身のクラスメソッド(この場合はnewInstance)でしか行えないようにすると危険防止になるので、コンストラクタをprivateにしています。
変数factoryとメソッドnewInstanceはいずれも修飾子staticが指定されており、それぞれクラス変数とクラスメソッドであることが宣言されています。
ItemFactoryをUMLで表現したものが図18です。
属性factoryやオペレーションnewInstanceに下線が引かれておりクラススコープの属性やオペレーションであることを示しています。
パッケージ内で利用するデータを格納するためのオブジェクトについて考えます。 Javaによる実現例はリスト12となります。
package sample; class ActionValue { int x; int y; int width; int height; String command; ActionValue(int x, int y, int width, int height, String command) { this.x = x; this.y = y; this.width = width; this.height = height; this.command = command; } }
パッケージ内で利用するデータなのでクラス本体、オペレーションの可視性をパッケージにしています。クラス本体の可視性は、パッケージ外からのアクセスに対する可視性ということになります。また、内部データ用のオブジェクトなので属性をオペレーション経由ではなく、 直接外部から操作できるようにしています。 クラス本体の可視性の表現は、クラス名の前に可視性を示す記号を表示することで行います。「パッケージ内」の可視性を表現するためにUML 1.4から導入された新機能である「~」を使用しています(図19)。
“クラス”は、UMLとJavaでほとんど同じ構造になっているので、簡単なクラス図を書くことが目的であればマッピングは比較的簡単に行えます。しかし、CASEツールでの自動マッピングなどをターゲットにして、厳密にマッピングを考えていくと、UMLの複雑な部分も見えてきます。UMLの仕様が膨大であるうえに、UMLとJavaのモデルが微妙にズレている点もあるからです。
この問題に対応するためには、Javaとのマッピングに使用するUMLの仕様を絞り込んだうえで、UMLの仕様にないJavaの機能をステレオタイプやプロパティなどの拡張メカニズムを使って、拡張していく必要があります。もちろん、このような拡張を行う場合には、用途に応じてプロファイルとしてまとめていくことが必要です。そして、このプロファイルを開発チームの中でのルールとして利用することで、UMLを用いたコミュニケーションを円滑に行うことができるようになります。
今回は、オブジェクトの核となるモデル要素であるクラスについて、JavaとUMLのマッピングを検討するために必要な論点を明らかにしたうえで、サンプルのプロファイルを作成してみました。このプロファイルは筆者の好みが大きく反映されていますが、一般的な開発ではそのまま利用できるのではないかと思います。もちろん、実際の開発に当たっては、利用するCASEツールとの整合性といった要因を考慮に入れてチューニングしていくとよいでしょう。
Copyright © ITmedia, Inc. All Rights Reserved.