🎃

mermaidのクラス図メモ

2022/02/05に公開

mermaidでUMLを書く際のメモをサンプルコードと共に残しておきます。

クラス図

クラス間の関係

記号 意味 サンプル
<|-- Inheritance(継承) ClassA <|-- ClassB
<|.. Realization(実現) ClassC <|.. ClassD
<-- Association(関連) ClassE <-- ClassF
<.. Dependency(依存) ClassG <.. ClassH
*-- Composition(委譲) ClassI *-- ClassJ
o-- Aggregation(集約) ClassK o-- ClassL
-- Link(線) ClassM -- ClassN
.. Link(破線) ClassO .. ClassP

その他の記述方法として下記があります。

  • ラベルを付ける場合はClassA <-- ClassB : Label
  • 関連の向きを変える場合はClassA --> ClassB

変数と関数

変数は下記のように記述します。

  • 変数はfoo
  • 型を指定する場合はint foo

関数は下記のように記述します。

  • 関数はfoo()
  • 戻り値の型を指定する場合はfoo(): int
  • 引数を指定する場合はfoo(bar, baz)
  • 引数の型を指定する場合はfoo(int bar, String baz)

また、各記号に関しては下記の意味があります。

記号 意味 サンプル
* Abstract foo*, foo()*
$ Static foo$, foo()$
+ Public +foo, +foo()
- Private -foo, -foo()
# Protected #foo, #foo()
~ Package/Internal ~foo

クラスの定義

インターフェース等は下記のように記述できます。

<<interface>> ClassA

<<abstract>> ClassA

<<enumeration>> ClassA

ブロックを定義して記述することも可能です。

class ClassA {
  <<interface>>
  -int foo
  +bar()* String
}

サンプルとDartコード

クラスの関係性をDartコードとともに示します。
コードはあくまで一例に過ぎない点に注意してください。

Inheritance(継承)

class ClassA {}

class ClassB extends ClassA {}

継承は親クラス(ClassA)の要素を子クラス(ClassB)の関係を表します。

Realization(実現)

abstract class ClassC {
  void foo();
}

class ClassD extends ClassC {
  void foo() => print('foo');
}

実現は抽象クラスまたはインターフェース(ClassC)と具象クラス(ClassD)の関係を表します。

Association(関連)

class ClassE {}

class ClassF {
  ClassE e;
  ClassF(this.e);
}

関連は"has-a"の関係を表し、矢印の向きは「誘導可能性」を示します。そのためClassFからClassEを辿ることができます。

Dependency(依存)

class ClassG {
  void foo() => print('foo');
}

class ClassH {
  void bar(ClassG g) {
    g.foo();
  }
}

依存はClassGの変更がClassHに影響を与えます。

Composition(委譲)

class ClassI {
  ClassJ j = ClassJ();
}

class ClassJ {}

合成は関連のサブセットで"part-of"の関係を表し、部分(ClassJ)が唯一の全体(ClassI)に共有されます。つまり、オブジェクトの生存期間が一致します。

Aggregation(集約)

class ClassK {
  ClassL l;
  ClassK(this.l);
}

class ClassL {}

集約は関連のサブセットで"part-of"の関係を表し、部分(ClassF)が複数の全体(ClassE)に共有されます。つまり、オブジェクトの生存期間が一致しません。
関連と実装上の違いはないですが意味的に「全体と部分」の関係を示したものが集約と考えれます。

Link(線)

class ClassM {
  ClassN n;
  ClassM(this.n);
}

class ClassN {
  ClassM m;
  ClassN(this.m);
}

線は双方向の関連がある場合(または関連が不明の場合)を表します。

Link(破線)

class ClassO {
  void foo() => print('foo');
  
  void bar(ClassP p) {
    p.bar();
  }
}

class ClassP {
  void bar() => print('bar');

  void foo(ClassO o) {
    o.foo();
  }
}

破線は双方向の依存がある場合(また依存関係が不明の場合)を表します。

参考文献

Discussion