⚒️

Mermaid.jsでデザインパターン全23個のクラス図を書いてみた

2022/05/12に公開

結構前ですが、zennがMermaid.jsに対応してましたね。
ちょうど個人的にMermaid.jsでクラス図を書けるようになりたかったので、デザインパターンのクラス図を表現してみることにしました。
『RefactoringGuru』のクラス図を元にしています。
今回記述したコードは全てgithubに上げております。
https://github.com/engineer-taro/mermaid_design_pattern

「デザインパターンの復習とMermaid.jsの学習が一気に出来るのはラッキー!」という気持ちで書いています。
Mermaid.jsを使ってみようかな、と思っている方の参考になると嬉しいです。

ざっくりと書いてしまったので、良い書き方を知っている方がいましたらコメント頂けると嬉しいです!

作ってみた感想

最初にやってみた感想です。

良かった点

  • コード化できているので、後から変更がしやすい
  • 図をgit管理できるのが良い
  • mermaid.jsで何も見なくてもクラス図が書けるようになった

難しい点

  • 手や図作成のソフトで書く場合に比べると、細かなニュアンスが出しにくい
  • 階層が表しづらい
    • 例: 子クラスと孫クラスが横並びになってしまったりする
  • 記載する順番によって並び順や矢印の位置が変わってしまう。この辺りをチームで統一するのは難しそう。
  • 微妙にかゆい所に手が届かないことがある
    • 例: クラスの変数を書く際に、変数の型を記述する形式が無い。
      メソッドの返り値の型は指定できる: +createMessage() String -> 図:+createMessage() : String
      変数の型は書いた通りに図に反映される: +String message -> 図: +String message

アルファベット順で掲載していきます。

AbstractFactory

複雑なクラス図を書く際は、工夫が必要そうです。
また業務で使うときに考えよう...

classDiagram
  class AbstractProductA {
    <<abstract>>
  }
  AbstractProductA <|-- ConcreteProductA1
  ConcreteProductA2 --|> AbstractProductA

  class AbstractProductB {
    <<abstract>>
  }
  AbstractProductB <|-- ConcreteProductB1
  ConcreteProductB2 --|> AbstractProductB

  class ConcreteFactory1 {
    +createProductA() ProductA
    +createProductB() ProductB
  }
  class AbstractFactory {
    <<interface>>
    +createProductA() ProductA
    +createProductB() ProductB    
  }
  class ConcreteFactory2 {
    +createProductA() ProductA
    +createProductB() ProductB
  }

  AbstractFactory <|.. ConcreteFactory1
  ConcreteFactory2 ..|> AbstractFactory
  
  class Client {
    -factory: AbstractFactory
    +Client(f: AbstractFactory)
    +someOperation()
  }

  ConcreteFactory1 ..> ConcreteProductB1
  ConcreteFactory1 ..> ConcreteProductA1

  ConcreteFactory2 ..> ConcreteProductB2
  ConcreteFactory2 ..> ConcreteProductA2

  Client --> AbstractFactory

Adaptor

classDiagram
  direction BT
  class Client
  class ClientInterface {
    <<interface>>
    +method(data)
  }
  class Adapter {
    -adaptee: Service
    +method(data)
  }
  class Service {
    +serviceMethod(specialData)
  }
  
  Client --> ClientInterface: use
  Adapter ..|> ClientInterface
  Adapter o-->Service

Bridge

classDiagram
  direction BT
  class Client
  class Abstraction {
    -i: Implementation
    +feature1()
    +feature2()
  }
  class Implementation {
    <<interface>>
    +method1()
    +method2()
    +method3()
  }
  class RefinedAbstraction {
    +featureN()
  }
  class ConcreteImplementations
  
  Client --> Abstraction: use
  RefinedAbstraction --|> Abstraction
  Abstraction o--> Implementation
  ConcreteImplementations ..|> Implementation

Builder

classDiagram
    direction BT
    class Builder {
        <<interface>>
        +reset()
        +buildStepA()
        +buildStepB()
        +buildStepC()
    }

    class ConcreteBuilder1 {
        -Product1 result
        +reset()
        +buildStepA()
        +buildStepB()
        +buildStepC()
        +getResult() Product1
    }

    class ConcreteBuilder2 {
        -Product2 result
        +reset()
        +buildStepA()
        +buildStepB()
        +buildStepC()
        +getResult() Product2
    }

    class Director {
        -Builder builder
        +Director(builder)
        +changeBuilder(builder)
        +make(type)
    }

    class Product1
    class Product2

    Product1 <-- ConcreteBuilder1: create
    Product2 <-- ConcreteBuilder2: create
    ConcreteBuilder1 ..|> Builder
    ConcreteBuilder2 ..|> Builder
    Director o--> Builder

ChainOfResponsibility

classDiagram
  direction BT
  class Handler {
      <<interface>>
      +setNext(h: Handler)
      +handle(request)
  }

  class BaseHandler {
      <<abstract>>
      -next: Handler
      +setNext(h: Handler)
      +handle(request)
  }

  class ConcreteHandlers {
      +handle(request)
  }

  BaseHandler o--> Handler
  BaseHandler ..|> Handler
  ConcreteHandlers --|> BaseHandler
  Client --> Handler

Command

classDiagram
  class Client
  class Invoker {
      -command
      +setCommand(command)
      +executeCommand()
  }
  
  class Command {
      <<interface>>
      +execute()
  }

  class Command1 {
      -receiver
      -params
      +Command1(receiver, params)
      +execute()
  }

  class Command2 {
      +execute()
  }

  class Receiver {
      +operation(a, b, c)
  }
  
  Invoker --> Command
  Command1 ..|> Command
  Command1 --> Receiver
  Command2 ..|> Command

  Client --> Invoker
  Client ..> Command1
  Client --> Receiver

Composite

classDiagram
  direction BT
  class Client
  class Component {
    <<interface>>
    +execute()
  }
  class Leaf {
    +execute()
  }
  class Composite {
    -children: Component[]
    +add(c: Component)
    +remove(c: Component)
    +getChildren() Component[]
    +execute()
  }
  Client --> Component: use
  Leaf ..|> Component
  Composite ..|> Component
  Composite o--> Component 

Decorator

classDiagram
  direction BT
  class Client
  class Component {
    <<interface>>
    +execute()
  }
  class ConcreteComponent {
    +execute()
  }
  class BaseDecorator {
    -wrappee: Component
    +BaseDecorator(c: Component)
    +execute()
  }
  class ConcreteDecorators {
    +execute()
    +extra()
  }
  
  Client --> Component: use
  ConcreteComponent ..|> Component
  BaseDecorator ..|> Component
  ConcreteDecorators --|> BaseDecorator
  BaseDecorator o--> Component

Facade

classDiagram
  direction LR
  class Client
  class Facade {
    -linksToSubsystemObjects
    -optionalAdditionalFacade
    +subsystemOperation()
  }
  class AdditionalFacade {
    +anotherOperation()
  }
  Client --> Facade
  Facade --> AdditionalFacade

FactoryMethod

classDiagram
  direction BT
  class Creator{
    <<abstract>>
    +someOperation()
    +createProduct()* Product
  }
  class Product{
    <<interface>>
    +doStuff()
  }
  class ConcreteCreatorA{
    +createProduct() Product
  }
  class ConcreteCreatorB{
    +createProduct() Product
  }
  class ConcreteProductA{
  }
  class ConcreteProductB{
  }
  
  ConcreteCreatorA --|> Creator
  ConcreteCreatorB --|> Creator
  Creator --> Product : Create 
  ConcreteProductA ..|> Product
  ConcreteProductB ..|> Product

Flyweight

classDiagram
  direction BT
  class FlyweightFactory {
      -cache: Flyweight[]
      +getFlyweight(repeatingState)
  }
  class Context {
      -uniqueState
      -flyweight
      +Context(repeatingState, uniqueState)
      +operation()
  }
  class Flyweight {
      -repeatingState
      +operation(uniqueState)
  }
  FlyweightFactory o--> Flyweight
  Context --> FlyweightFactory
  Context --> Flyweight
  Client *--> Context

Interpreter

RefactoringGuruにこちらのパターンは記載が無かったため、「Java言語で学ぶデザインパターン入門」を参照しました。

classDiagram
  direction LR
  class Client
  class AbstractExpression {
      <<abstract>>
      interpret()*
  }
  class TerminalExpression {
      interpret()
  }
  class NonterminalExpression {
      childExpression
      interpret()
  }
  class Context {
      getInfoToInterpret()
  }

  Client o--> Context
  Client --> AbstractExpression
  NonterminalExpression --|> AbstractExpression
  TerminalExpression --|> AbstractExpression
  NonterminalExpression o--> AbstractExpression

Iterator

classDiagram
    direction BT
    class Client
    class Iterator {
        <<interface>>
        +getNext()
        +hasMore() bool
    }
    class IterableCollection {
        <<interface>>
        +createIterator() Iterator
    }
    class ConcreteIterator {
        -collection: ConcreteCollection
        -iterationState
        +ConcreteIterator(c: ConcreteCollection)
        +getNext()
        +hasMore() bool
    }
    class ConcreteCollection {
        +createIterator() Iterator
    }
    Iterator <-- Client: use
    IterableCollection <-- Client: use
    ConcreteIterator ..|> Iterator
    ConcreteCollection ..|> IterableCollection
    ConcreteIterator <--> ConcreteCollection

Mediator

ConcreteMediator ..|> Mediator を間に記載することで、いい感じに配置できました。

classDiagram
  direction BT
  class Mediator {
      <<interface>>
      +notify(sender)
  }
  class ConcreteMediator {
      -componentA
      -componentB
      -componentC
      -componentD
      +notify(sender)
      +reactOnA()
      +reactOnB()
      +reactOnC()
      +reactOnD()
  }
  class ComponentA {
      -m: Mediator
      +operationA()
  }
  class ComponentB {
      -m: Mediator
      +operationB()
  }
  class ComponentC {
      -m: Mediator
      +operationC()
  }
  class ComponentD {
      -m: Mediator
      +operationD()
  }
  ComponentA --> Mediator
  ComponentB --> Mediator
  ConcreteMediator ..|> Mediator
  ComponentC --> Mediator
  ComponentD --> Mediator
  ConcreteMediator *--> ComponentA
  ConcreteMediator *--> ComponentB
  ConcreteMediator *--> ComponentC
  ConcreteMediator *--> ComponentD

Memento

classDiagram
  direction LR
  class Originator {
      -state
      +save() Memento
      +restore(m: Memento)
  }
  class Memento {
      -state
      -Memento(state)
      -getState()
  }
  class Caretaker {
      -originator
      -history: Memento[]
      +doSomething()
      +undo()
  }

  Originator ..> Memento
  Memento <--o Caretaker

Observer

classDiagram
  direction LR
  class Publisher {
      -subscribers: Subscriber[]
      -mainState
      +subscribe(s: Subscriber)
      +unsubscribe(s: Subscriber)
      +notifySubscribers()
      +mainBusinessLogic()
  }
  class Subscriber {
      <<interface>>
      +upgrade(context)
  }
  class ConcreteSubscribers {
      +update(context)
  }
  class Client

  Publisher o--> Subscriber
  ConcreteSubscribers ..|> Subscriber
  Client --> Publisher
  Client ..> ConcreteSubscribers

Prototype

Prototype(基本)

この後、PrototypeパターンでRegisterをもつバージョンのクラス図も載せるので、こちらは基本系としています。
cloneメソッドをサブクラスで実装するシンプルな構成です。

  classDiagram
    direction BT
    class Prototype{
      <<interface>>
      +clone() Prototype
    }

    class ConcretePrototype{
      -field1
      +ConcretePrototype(prototype)
      +clone() Prototype
    }

    class SubclassPrototype{
      -field2
      +SubclassPrototype(prototype)
      +clone() SubclassPrototype
    }
    
    class Client
    SubclassPrototype --|> ConcretePrototype
    ConcretePrototype ..|> Prototype
    Client --> Prototype : use

Prototype(レジスター有版)

頻繁に使うインスタンスをレジスターに登録しておき、使うときはレジスターに登録されているインスタンスをcloneして利用する、というPrototypeパターンの拡張版です。

classDiagram
    direction BT
    class PrototypeRegistry{
        - Prototype[] items
        +addItem(id: string, p: Prototype)
        +getById(id: string) Prototype
        +getByColor(color: string) Prototype
    }

    class Prototype{
        <<interface>>
        +getColor() string
        +clone() Prototype
    }

    class Button{
        -x, y, color
        +Button(x, y, color)
        +Button(prototype)
        +getColor() string
        +clone() Prototype
    }

    class Client

    PrototypeRegistry o--> Prototype
    Button ..|> Prototype
    
    Client --> PrototypeRegistry : use 

Proxy

classDiagram
  direction BT
  class ServiceInterface {
    <<interface>>
    +operation()
  }
  class Proxy {
    -realService: Service
    +Proxy(s: Service)
    +checkAccess()
    +operation()
  }
  class Service {
    +operation()
  }
  Client --> ServiceInterface
  Proxy ..|> ServiceInterface
  Service ..|> ServiceInterface
  Proxy o--> Service

Singleton

classDiagram
    direction LR
    class Singleton {
        -Singleton instance$
        -Singleton()
        +getInstance()$ Singleton
    }

    class Client

    Client --> Singleton : use
    Singleton --> Singleton : create, has

State

classDiagram
  direction BT
  class Context {
      -state
      +Context(initialState)
      +changeState(state)
      +doThis()
      +doThat()
  }
  class State {
      <<interface>>
      +doThis()
      +doThat()
  }
  class ConcreteStates {
      -context
      +setContext(context)
      +doThis()
      +doThat()
  }
  class Client

  Context o--> State
  ConcreteStates ..|> State
  ConcreteStates --> Context
  Client ..> ConcreteStates
  Client --> Context

Strategy

classDiagram
  direction BT
  class Client
  class Context{
    -strategy
    +setStrategy(strategy)
    +doSomething()
  }
  class Strategy{
    <<interface>>
    +execute(data)
  }
  class ConcreteStrategies{
    +execute(data)
  }
  Client --> Context: use
  Client ..> ConcreteStrategies: create
  Context o--> Strategy
  ConcreteStrategies ..|> Strategy

TemplateMethod

classDiagram
  direction BT
  class AbstractClass{
    <<abstract>>
    +templateMethod()
    +step1()
    +step2()
    +step3()*
    +step4()*
  }
  class ConcreteClass1{
    +step3()
    +step4()
  }
  class ConcreteClass2{
    +step1()
    +step2()
    +step3()
    +step4()
  }
  ConcreteClass1 --|> AbstractClass
  ConcreteClass2 --|> AbstractClass

Visitor

classDiagram
    direction BT
    class Client
    class ConcreteVisitors {
        +visit(e: ElementA)
        +visit(e: ElementB)
    }
    class Visitor {
        <<interface>>
        +visit(e: ElementA)
        +visit(e: ElementB)
    }
    class ElementA {
        +featureA()
        +accept(v: Visitor)
    }
    class ElementB {
        +featureB()
        +accept(v: Visitor)
    }
    class Element {
        <<interface>>
        +accept(v: Visitor)
    }


    Client ..> ConcreteVisitors
    ConcreteVisitors ..|> Visitor
    Element ..> Visitor
    ElementA ..|> Element
    ElementB ..|> Element
    Visitor ..> ElementA
    Visitor ..> ElementB
    Client ..> Element

参照

refactoring.guru様のクラス図を参考にしております。
以下のUsage policyに従っております。

https://refactoring.guru/content-usage-policy

While most of this website is protected by copyright, we permit free usage of content and illustrations as long as you place a hyperlink to the specific page (or print its URL if it’s printed media) where the content is taken from (or just the home page if multiple pieces of content is used) in the following bounds:
You can cite any text on this website, as long as it’s not a substantial part of the cited article.
You can use up to 10 illustrations from this website in total for all of your publications and presentations.

本サイトの大部分は著作権で保護されていますが、以下の範囲内で、コンテンツを引用した特定のページ(印刷媒体の場合はそのURLを印刷したもの)へのハイパーリンク(複数のコンテンツを使用した場合はトップページのみ)を設置していただければ、コンテンツやイラストの自由利用を許可します。
引用された論文の実質的な部分でない限り、当サイトのどの文章を引用してもかまいません。
本サイトに掲載されている図版は、出版物やプレゼンテーションに使用する場合、合計10点までとします。

Discussion