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 java6

caskコマンドを使っている理由は、caskコマンド経由でjavaを入れることでパスを通す必要がなくなるため、用いています。

VSCode上でダイアグラムを描画するためにgraphvizをインストール

$ brew install graphviz

VSCodeにPlantUMLをインストール

以下の手順でインストールできます。

  • VSCodeを起動し、⌘(command)+P
  • ext install plantumlと入力

PlantUMLの構文について

前提事項

以下のドキュメントより、可視性の定義など厳密にはUMLのクラス図記法に沿っていない部分があります。今回は各プロパティをそのままSwiftのコードに書き起こしやすいようにクラス図を可視化している旨をご了承ください🙏

本来はprivateなど書かずにで表現します。

構文に関してはドキュメントの通りになるのですが、実際に初期設計を行う様子をイメージしやすいように、コードが公開されているABEMA iOS チュートリアルのタスク4 お気に入り機能を実装する際に新たに登場するプロパティを示すクラス図をPlantUMLで書いていこうと思います。

以下のディレクトリに関するロジックに関するクラス図を示していきます。

Flux/Repository

Action, Dispatcher, Storeそれぞれに対して、新たに追加する必要があるプロパティを示している様子です。

Flux/RepositoryをPlantUMLで可視化している様子
Flux/RepositoryをPlantUMLで可視化している様子

上記のクラス図を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それぞれに対して、新たに追加する必要があるプロパティを示している様子です。

Views/FavoriteRepositoryListをPlantUMLで可視化している様子
Views/FavoriteRepositoryListをPlantUMLで可視化している様子

上記のクラス図を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
@enduml

Views/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のバージョン管理で困ったお話しです!

それでは!

AIを仕事の仕組みに変えたい20代へ

AIを使うだけでなく、1業務をチームの仕組みへ

社会人1〜3年目が身近な1業務を選び、人とAIの役割を分け、7日で試し、成果を共有する5ステップをまとめました。

若手社員のAI業務改善5ステップを見る
カフェ・オンライン30分の日時を見る