PlantUML デザインパターン


はじめに

PlantUMLは、テキストベースでUMLを記述するための、おそらく世界で最も普及したツール。

テキストベースなので、gitを使ったUMLの変更管理などが容易で、マウスを使わずにUMLをさくっと書けるメリットがある。
ここでは、PlantUMLを記述する上で参考になるデザインパターンを紹介する。

  1. パターン未適用
    デザインパターン未適用の状態のPlantUMLファイルとその課題を述べる。
  2. パターン適用
    デザインパターンを適用したPlantUMLファイルとその利点を述べる。

Packageパターン

パターン未適用

1つのPlantUmlファイルに複数のパッケージが定義される。

Arrowパターン未適用
package shop {
  class Shop
  Shop *-- Item
}

package customer {
  class Customer
  Customer --> Item : buy
}

クラスが増えるほどファイルが肥大化してメンテナンス性が劣化する。

パターン適用

パッケージ毎にファイルを分ける。

Arrowパターン適用
package shop {
  class Shop
  Shop *-- Item
}
Arrowパターン適用
package customer {
  class Customer
  Customer --> Item : buy
}

クラス増加時にもファイルが肥大化しにくく、メンテナンス性を維持できる。

Arrowパターン

パターン未適用

オブジェクト間の依存関係をextendsimplementsといったキーワードで記述する。

Arrowパターン未適用
class Parent
class Child extends Parent

この表現には以下の問題がある。

  • 矢印向きを指定できない。
  • 個々のオブジェクトを表現するclass宣言と、"矢印"というレイアウトを指定するextendsキーワードが混ざっている。

パターン適用

Arrowパターンを適用した例を以下に示す。

Arrowパターン適用
class Parent
class Child

Parent <|-up- Child

Arrowパターンは個々のオブジェクトのコンテキストを保持するclass宣言からレイアウト情報を保持する矢印を分離する。

コンテキストとレイアウトの分離により、より柔軟な図表現が可能となる。上記の例では矢印にupキーワードを使うことで矢印の向きを逆向きにした。

Includeパターン

パターン未適用

start/endsubincludesub キーワードをつかって、PlantUMLファイルをオブジェクト指向の構造にまとめよう。

次のようなUMLを書くことを考えてみよう。

Activityクラスはactivitiesパッケージの一部であり、アブストラクトクラスActivityEdgeを集約する。このUMLを、1つのPlantUMLファイルに記述すると次のようになるだろう。

all.puml
@startuml

package activities {
  class Activity
}

abstract class ActivityEdge

Activity "0..1" *-- "*" ActivityEdge

@enduml

このとき、activitiesパッケージ内のActivityクラスとパッケージ外のActivityEdgeクラスが、同じファイル内に書かれることになる。

こうすると、たとえばactivitiesパッケージだけを図示したい場合や、他のクラス図にActivityクラスを載せたい場合にコードクローンするしかなく、不便である。

パターン適用例

ここで使えるのが、start/endsubincludesub キーワードである。前述のPlantUMLファイルをこのキーワードを使って2つのファイルに分割する。

activity.puml
@startuml

!startsub interface
package activities {
  class Activity
}
!endsub

@enduml
index.puml
@startuml

!includesub activity.puml!interface

!startsub interface
abstract class ActivityEdge
!endsub

Activity "0..1" *-- "*" ActivityEdge

@enduml

感嘆符!は、PlantUMLのプリプロセッサ命令を意味する記号である。

activity.puml!startsub interface ... !endsubは、interfaceという名前のサブパートを宣言するプリプロセッサ命令である。このサブパートは、!includesubキーワードを使って、別のPlantUmlから呼び出すことができる。

index.puml!includesub activity.puml!interfaceは、activity.pumlinterfaceサブパートを呼び出すプリプロセッサ命令である。

このように、start/endsubincludesub キーワードを用いてactivitiesパッケージを再利用しやすい別のファイルに分割することができた。