PlantUMLでクラス図を作成!Swiftで学ぶ初期設計
Swiftを例にPlantUMLでクラス図を作成する方法を解説。初期設計の可視化や既存コードのクラス図自動生成の手順も紹介!
はじめに
この記事は、KIT Developer Advent Calendar 2020初日の記事になります!
クラス図を書く際に使うツールに関して
クラス図を作成する際に便利なツールとして、様々なものがあります。
簡単にいくつかご紹介していきます。
PlantUML
VSCodeの拡張で、リアルタイムプレビューしながらプログラムベースでクラス図を書くことが出来ます。
CUI上で簡潔する点がとても良いです。また、.puファイルをGit等でバージョン管理することもできるため、チームで差分を共有しやすい側面もあります。
Webベースの作図ツールでPNG, JPEG, SVGなどの画像ファイル・PDF形式で保存することもできます。
また、GitHubのファイルと紐づけて使用することも可能です。
GitMind
こちらもWebベースの作図ツールで、GUI上で用いることができ、クラス図の作成の他に、ブレスト・プロジェクト計画などでも使用できます。
PlantUMLを使えるようにする
Javaのインストール
// javaのインストールをしてある場合は不要
$ brew cask install java6caskコマンドを使っている理由は、caskコマンド経由でjavaを入れることでパスを通す必要がなくなるため、用いています。
VSCode上でダイアグラムを描画するためにgraphvizをインストール
$ brew install graphvizVSCodeにPlantUMLをインストール
以下の手順でインストールできます。
- VSCodeを起動し、
⌘(command)+P ext install plantumlと入力
PlantUMLの構文について
前提事項
以下のドキュメントより、可視性の定義など厳密にはUMLのクラス図記法に沿っていない部分があります。今回は各プロパティをそのままSwiftのコードに書き起こしやすいようにクラス図を可視化している旨をご了承ください🙏
本来はprivateなど書かずに□で表現します。
構文に関してはドキュメントの通りになるのですが、実際に初期設計を行う様子をイメージしやすいように、コードが公開されているABEMA iOS チュートリアルのタスク4 お気に入り機能を実装する際に新たに登場するプロパティを示すクラス図をPlantUMLで書いていこうと思います。
以下のディレクトリに関するロジックに関するクラス図を示していきます。
Flux/Repository
Action, Dispatcher, Storeそれぞれに対して、新たに追加する必要があるプロパティを示している様子です。
上記のクラス図をPlantUMLで示すと
@startuml
title Flux/Repository
interface RepositoryActionType {
func addFavoriteRepository(repository)
func deleteFavoriteRepository(repository)
func getFavoriteRepository()
}
class RepositoryAction {
private let userDefaults: userDefoultsProtocol
}
RepositoryAction <|.. RepositoryActionType
note bottom : RepositoryAction.swift
class RepositoryDispatcher <<S, #ffeecc)>>{
let updateFavoriteRepositories
}
note bottom : RepositoryDispatcher.swift
interface RepositoryStoreType {
var favoriteRepositories: Property<[Repository]>
}
class RepositoryStore {
let favoriteRepositories: Property<[Repository]>
}
RepositoryStore <|.. RepositoryStoreType
note bottom : RepositoryStore.swift
@endumlといった感じになります。
アレンジしている部分について
どのディレクトリに相当するのか分かりやすくするために
title Flux/Repository
とタイトルをつけています。
また、どのswiftファイルに記述するのか分かりやすくするために
note bottom : RepositoryAction.swift
と注釈をつけています。
structを示すために
class RepositoryDispatcher <<S, #ffeecc)>>
とすることで表記をSに変更しています。
なぜ型キーワードにclassを使っているかというと、PlantUMLの型キーワードはinterface,class,abstract class,enumのみでstructに対応するものが存在しなかったため、classにステレオタイプをつけ、文字をSに変更しています。
参考:ステレオタイプとスポット
Views/FavoriteRepositoryList
ViewStream, ViewController, DataSourceそれぞれに対して、新たに追加する必要があるプロパティを示している様子です。
上記のクラス図をPlantUMLで示すと
@startuml
title Views/FavoriteRepositoryList
interface FavoriteRepositoryListViewStreamType {
var input: InputWrapper<FavoriteRepositoryListViewStream.Input>
var output: OutputWrapper<FavoriteRepositoryListViewStream.Output>
}
class FavoriteRepositoryListViewStream {
struct Input
struct Output
struct State
struct Extra
static func bind(from dependency: Dependency<Input, State, Extra>, disposeBag: DisposeBag)
}
FavoriteRepositoryListViewStream <|.. FavoriteRepositoryListViewStreamType
note bottom : FavoriteRepositoryListViewStream.swift
class FavoriteRepositoryListViewController {
private let viewStream = FavoriteRepositoryListViewStream()
private lazy var dataSource = FavoriteRepositoryListViewDataSource(viewStream)
private let disposeBag = DisposeBat()
private lazy var tableView
init()
required init?(coder aDecoder: NSCoder)
override func viewDidLoad()
override func viewWillAppear()
}
note bottom : FavoriteRepositoryListViewController.swift
class FavoriteRepositoryListViewDataSource {
private let viewStream: FavoriteRepositoryListViewStreamType
private let disposeBag = DisposeBag()
init(viewStream: FavoriteRepositoryListViewStreamType)
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int)
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)
}
note bottom : FavoriteRepositoryListViewDataSource.swift
@endumlViews/FavoriteRepositoryList/Cell
Cell, CellStreamに対して、新たに追加する必要があるプロパティを示している様子です。
上記のクラス図をPlantUMLで示すと
@startuml
title Views/FavoriteRepositoryList/Cell
class FavoriteRepositoryListCell {
private let viewStream = FavoriteRepositoryListCellStream()
private let dispostBag = DisposeBag()
private var favoriteRepository: Repository?
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?)
required init?(coder aDecoder: NSCoder)
override func prepareForReuse()
}
note bottom : FavoriteRepositoryListCell.swift
interface FavoriteRepositoryListCellStreamType {
var input: InputWrapper<FavoriteRepositoryListCellViewStream.Input>
var output: OutputWrapper<FavoriteRepositoryListCellViewStream.Output>
}
class FavoriteRepositoryListCellStream {
struct Input
struct Output
struct State
struct Extra
static func bind(from dependency: Dependency<Input, State, Extra>, disposeBag: DisposeBag)
}
FavoriteRepositoryListCellStream <|.. FavoriteRepositoryListCellStreamType
note bottom : FavoriteRepositoryListCellStream.swift
@endumlこのような感じでクラス図を可視化することができます。
既存のSwiftコードからクラス図を自動生成する
上記では、コードを書き始める前の初期設計についてお話をしてきましたが既に書かれたコードから自動的にクラス図を作成する方法もあります。
既存のコードを自動生成して可視化する際は、swiftumlのスクリプトを用います。
以下の記事を参考にしてみると導入がスムーズかと思います。
まとめ
コードを書き始める前にクラスの初期設計を行い、可視化することは自分がどのような実装を行うのか整理でき責務が明確な良いコードを書くことに繋げやすいです。
採用している・されているアーキテクチャに慣れていない場合などにも、クラスの初期設計を行う恩恵は十分にあると思います。
TDDなどでテストコードを先に書く場合も、必要となるクラスを先に列挙することで行いやすくなると感じました。
最後に
読んでいただきありがとうございました!
PlantUMLは直感的に書くことができ、構文もそれほど複雑ではないということが少しでも伝われば嬉しいです😄
簡単に可視化することができるので、みなさんもぜひ使ってみてください。
次の日は、@daikimareさんのNode.jsのバージョン管理で困ったお話しです!
それでは!