前回の記事の補足資料です。
目次
使い方
前回、PlantUmlClassDiagramGenerator の使い方を書くのを忘れていましたので載せておきます。
PlantUmlClassDiagramGenerator はコンソールアプリケーションです。以下のパラメータを受け付けます。
PlantUmlClassDiagramGenerator.exe SOURCE_PATH [DEST_DIR]
- 第1引数: SOURCE_PATH
- 変換元のソースファイル名または、ソースファイルを格納したフォルダ名を指定します。
- フォルダを指定した場合は、直下にある .cs ファイルをすべて読み込みます。(フォルダの階層は辿りません)
- 第2引数: DEST_DIR
- 結果のPlantUMLファイルの出力先を指定します。省略可能です。
- 省略した場合は、入力ファイルと同じ階層に"uml" というフォルダを作成してそこに出力します。
C:\ > PlantUmlClassDiagramGenerator.exe SOURCE_PATH DEST_DIR
設計メモ
C#からPlantUMLのクラス図に変換する処理の設計メモです。
- UMLに関する知識不足等で、不適切な部分があるかもしれません。 お気づきの点などありましたらご指摘頂けると嬉しいです。
型定義
インターフェース、クラス、構造体などの型定義に関する仕様です。
C#のキーワードとPlantUMLでの記述との対応付けを表にしてみました。
型キーワード
PlantUMLでは、interface , class , abstract class , enum が使用可能です。
| C# | PlantUML | Memo |
|---|---|---|
class |
class |
|
struct |
<<struct>> class |
PlantUMLにはstruct で定義できる型が無いので、class にステレオタイプ<<struct>> を付加することで構造体を表現 |
interface |
interface |
|
abstract class |
abstract class |
抽象クラスはabstract class で宣言可能 |
enum |
enum |
修飾子
修飾子は、(基本的には)ステレオタイプで表現します。
| C# | PlantUML | Memo |
|---|---|---|
abstract |
abstract |
‘abstract‘ は、‘class‘ キーワードと組み合わせて抽象クラスの宣言時に使用する |
static |
<<static>> |
|
partial |
<<partial>> |
|
sealed |
<<sealed>> |
- 型のアクセス修飾子について
UMLに型自体のアクセス修飾子に関する規定がない(?)ため、型に対するアクセス修飾子は無視するようにしました。
必要な場合は、<<internal>> class ClassAの様にステレオタイプを付加する?
型引数
ジェネリックの型引数は、PlantUMLでもC#と同じように書けます
class GenericsType<string,int> { }
例
クラス定義の変換例です。
//csharp sealed class ClassA{ } abstract class AbstractClass{ } static class StaticClass{ } struct Structure{ } enum EnumType{ }
'plantuml @startuml class ClassA <<sealed>> abstract class AbstractClass class StaticClass <<static>> class Structure <<struct>> enum EnumType class GenericsType<string,int> @enduml
メンバー定義
フィールド、プロパティ、メソッド、Enum定数 など型のメンバー定義に関する仕様です。
アクセス修飾子(共通)
| C# | PlantUML | Comment |
|---|---|---|
public |
+ |
|
internal |
<<internal>> |
~ (package) が意味合い的に近いと思うが、 protected internal と合わせてステレオタイプにした |
protected internal |
# <<internal>> |
#~ はPlantUMLではエラーになるので internal をステレオタイプで表現することに |
protected |
# |
|
private |
- |
修飾子 (共通)
| C# | PlantUML | Comment |
|---|---|---|
abstract |
{abstract} |
ステレオタイプではなく{}で括る。イタリック体の表記になる |
static |
{static} |
ステレオタイプではなく{}で括る。下線付きの文字で表現される |
virtual |
<<virtual>> |
|
override |
<<override>> |
|
readonly |
<<readonly>> |
プロパティ
プロパティの表現は悩みどころですが、変換のしやすさを優先して以下の様に出力するようにしました。
- C#のコードに記述されているアクセサー(get,set)をそのままステレオタイプとして出力
//csharp public int PropA {get; set;} public int PropB {get;} public int PropC {get; protected set;}
'prantuml + PropA : int <<get>> <<set>> + PropB : int <<get>> + PropC : int <<get>> <<protected set>>
- 別候補
他にも以下のように変換することも考えたのですが
(1) は protected set 等、アクセサーごとにアクセス修飾子が付けられた際の表現が難しく、
(2) は 変換処理が少し複雑になるのと、もはやプロパティではなくなるので却下しました。
'prantuml
'(1) <<property>> ステレオタイプを付け、getterのみの場合はreadonlyの制約を付ける
+ PropA : int <<property>>
+ PropB : int <<property>> { readonly }
'(2) Java風にメソッドで表現
+ getPropA():int
+ setPropA(value:int)
+ getPropB():int
+ getPropC():int
# setPropC(value:int)
初期化子(フィールド、プロパティ)
フィールド、プロパティの初期値を初期化子で設定している場合、初期値がリテラルの場合のみ = (初期値) を付加するようにしています。
リテラルのみにした理由は、初期化子にコンストラクタやメソッドを使った場合に、 PlantUMLが 初期化子の() を見て フィールド(プロパティ)ではなくメソッドと判断してしまう為です。
'plantuml
''次のようにフィールドを記述しても、PlantUMLはメソッドと判断してしまう
class ClassA{
# int : IList<int> = new List<int>()
}
初期化子の変換例
//csharp class ClassA { private readonly int intField = 100; protected double X = 0, Y = 1, Z = 2; internal double PropC { get; } = 3.141592; protected IList<int> list = new List<int>(); }
'plantuml
class ClassA {
- <<readonly>> intField : int = 100
# X : double = 0
# Y : double = 1
# Z : double = 2
<<internal>> PropC : double <<get>> = 3.141592;
' リテラル以外の初期化子は出力しない
# list : IList<int>
}
未対応
現在対応できていないコードについてまとめます。
ネストクラス
現状、入れ子になった型の定義は、以下の様にそのまま入れ子の状態で出力されるのですが、PlantUMLではエラーとなり変換できません。
'plantuml
'' このコードはエラーになります
class NestedClass {
+ A : int <<get>>
+ B : InnerClass <<get>>
class InnerClass {
+ X : string <<get>>
+ MethodX() : void
}
}
内部クラスを外に出して、+-- で結ぶと入れ子を表現可能なので、以下の様に変換すべきなのですが。。。
class NestedClass {
+ A : int <<get>>
+ B : InnerClass <<get>>
}
class InnerClass {
+ X : string <<get>>
+ MethodX() : void
}
NestedClass +-- InnerClass
演算子のオーバーロード
演算子のオーバーロードを定義してもPlantUMLに出力されません。(未検討)
変換例
最後に、いろいろな変換例を載せておきます。
変換前
変換元C#のコードはこちらを参照ください InputClasses.cs
変換後のPlantUML
'plantuml
class ClassA {
- <<readonly>> intField : int = 100
- {static} strField : string
# X : double = 0
# Y : double = 1
# Z : double = 2
- list : IList<int>
# PropA : int <<get>>
# <<internal>> PropB : string <<get>> <<protected set>>
<<internal>> PropC : double <<get>> = 3.141592
+ ClassA()
{static} ClassA()
# <<virtual>> VirtualMethod() : void
+ <<override>> ToString() : string
+ {static} StaticMethod() : string
}
abstract class ClassB {
- field_1 : int
{abstract} + PropA : int <<get>> <<protected set>>
# <<virtual>> VirtualMethod() : string
+ {abstract} AbstractMethod(arg1:int, arg2:double) : string
}
class ClassC <<sealed>> {
- {static} <<readonly>> readonlyField : string = "ReadOnly"
+ <<override>> PropA : int <<get>> <<protected set>> = 100
+ <<override>> AbstractMethod(arg1:int, arg2:double) : string
# <<override>> VirtualMethod() : string
}
class Vector <<struct>> {
+ X : double <<get>>
+ Y : double <<get>>
+ Z : double <<get>>
+ Vector(x:double, y:double, z:double)
+ Vector(source:Vector)
}
enum EnumA {
AA= 0x0001,
BB= 0x0002,
CC= 0x0004,
DD= 0x0008,
EE= 0x0010,
}
ClassB <|-- ClassC