PlantUMLでデザインパターンのクラス図を描く

GoFデザインパターンは23種類もあるので、PlantUMLでクラス図を書く練習にちょうどよい。 そういう訳で、PlantUMLでデザインパターンのクラス図を描く。

その前に、用語を整理しておこう。

UML では、用途に応じて図が何種類か定義されている。クラス図(Class Diagram)はそのうちの1つだ。

PlantUML はこれを書くためのツールで、jarとして端末から実行する。IntelliJ IDEA や Visual Studio Code といった IDE で使えるプラグインもあり、esa.io や Confluence のような情報共有ツール上でも利用できるので、好きな環境でやろう。

こういうテキストを書いておくと、

@startuml
class ClassA {
}

class ClassB {
}
@enduml

こういう図が出力される。

f:id:akrfjmt:20170930062913p:plain

図を書く際は、ここから PlantUML Language Reference Guide をダウンロードしてこれを見ながらやることになる。なんと、日本語訳されたバージョンもある。

デザインパターン)は、JavaC++に頻出のクラスの設計に名前が付けられたもので、次の本が詳しい。

古いC++使ってる人は次の本(結城さんの本のネタ元だ)でもいいかもしれない。

私はどちらも持ってるが、わかりやすいのは1冊目だった。

クラス図

01. Iteratorパターン

@startuml
interface Aggregate {
  {abstract} iterator()
}
 
interface Iterator {
  {abstract} hasNext()
  {abstract} next()
}
 
class ConcreteAggregate {
  iterator()
}
 
class ConcreteIterator {
  aggregate
  hasNext()
  next()
}
 
Aggregate -right-> Iterator : Creates >
ConcreteAggregate .up.|> Aggregate
ConcreteIterator o-left-> ConcreteAggregate
ConcreteIterator .up.|> Iterator
@enduml

f:id:akrfjmt:20170930121743p:plain

02. Adapterパターン

継承を用いたAdapterパターン

@startuml

class Client

interface Target {
  {abstract} targetMethod1()
  {abstract} targetMethod2()
}

class Adapter {
  targetMethod1()
  targetMethod2()
}

class Adaptee {
  methodA()
  methodB()
  methodC()
}

Client -down-> Target : Uses >
Target <|.right. Adapter : implements <
Adapter -right-|> Adaptee : extends >

@enduml

f:id:akrfjmt:20170929225824p:plain

委譲を用いたAdapterパターン

@startuml

class Client

abstract Target {
  {abstract} targetMethod1()
  {abstract} targetMethod2()
}

class Adapter {
  adaptee
  targetMethod1()
  targetMethod2()
}

class Adaptee {
  methodA()
  methodB()
  methodC()
}

Client -down-> Target : Uses >

Target <|-right- Adapter : extends <

Adapter o-right-> Adaptee : has >

@enduml

f:id:akrfjmt:20170930121841p:plain

03. Template Method パターン

@startuml

abstract AbstractClass {
    abstract method1()
    abstract method2()
    abstract method3()
    abstract templateMethod()
}

class ConcreteClass {
    method1
    method2
    method3
}

ConcreteClass -up-|> AbstractClass

@enduml

f:id:akrfjmt:20170930052219p:plain

04. Factory Method パターン

@startuml
 
package "フレームワーク" {
  abstract Creator {
    create()
    {abstract} factoryMethod()
  }
  abstract Product {
    {abstract} method1()
    {abstract} method2()
    {abstract} method3()
  }
}
 
package "具体的な肉付け" {
  class ConcreteCreator {
    factoryMethod
  }
  class ConcreteProduct {
    method1()
    method2()
    method3()
  }
}
 
Creator -right-> Product : Creates >
ConcreteCreator -right-> ConcreteProduct : Creates >
ConcreteCreator -up-|> Creator
ConcreteProduct -up-|> Product

f:id:akrfjmt:20170929230123p:plain

05. Singletonパターン

@startuml
class Singleton {
 {static} -singleton
 -Singleton()
 {static} +getInstance()
}
@enduml

f:id:akrfjmt:20170929230235p:plain

06. Prototypeパターン

@startuml
 
class Client {
}
 
abstract Prototype{
  {abstract} createClone()
}
 
class ConcretePrototype {
  createClone()
}
 
 
Client -right-> Prototype : Uses >
ConcretePrototype -up-|> Prototype

@enduml

f:id:akrfjmt:20170930051846p:plain

07. Builderパターン

@startuml

class Director {
  builder
  construct()
}

class Builder {
  buildPart()
}

class ConcreteBuilder {
  buildPart()
  getResult() : Product
}

class Product {
}

Director o-> Builder

Builder <|- ConcreteBuilder

ConcreteBuilder --> Product : Creates >

@enduml

f:id:akrfjmt:20170930051827p:plain

08. Abstract Factoryパターン

@startuml
 
package "factory" {
  abstract AbstractProduct1 {
    {abstract} executeA()
    {abstract} executeB()
  }
 
  abstract AbstractProduct2 {
    {abstract} doAlpha()
    {abstract} doBeta()
  }
 
  abstract AbstractProduct3 {
    {abstract} performOne()
    {abstract} performTwo()
  }
 
  abstract AbstractFactory {
    {abstract} createProduct1()
    {abstract} createProduct2()
    {abstract} createProduct3()
  }
 
  AbstractFactory -up-> AbstractProduct1 : Creates >
  AbstractFactory -up-> AbstractProduct2 : Creates >
  AbstractFactory -up-> AbstractProduct3 : Creates >
}
 
package "concretefactory" {
  class ConcreteProduct1 {
    executeA()
    executeB()
  }
 
  class ConcreteProduct2 {
    doAlpha()
    doBeta()
  }
 
  class ConcreteProduct3 {
    performOne()
    performTwo()
  }
 
  class ConcreteFactory {
    createProduct1()
    createProduct2()
    createProduct3()
  }
 
  ConcreteFactory -up-> ConcreteProduct1 : Creates >

  ConcreteFactory -up-> ConcreteProduct2 : Creates >
  ConcreteFactory -up-> ConcreteProduct3 : Creates >
}

ConcreteProduct1 -left-|> AbstractProduct1
ConcreteProduct2 -left-|> AbstractProduct2
ConcreteProduct3 -left-|> AbstractProduct3
ConcreteFactory -left-|> AbstractFactory
 
@enduml

f:id:akrfjmt:20170930052348p:plain

09. Bridgeパターン

@startuml
class Abstraction {
  impl
  method1()
  method2()
  method3()
}

abstract Implementor {
  {abstract} implMethodX()
  {abstract} implMethodY()
}

class RefinedAbstraction {
  refinedMethodA()
  refinedMethodB()
}

class ConcreteImplementor {
  implMethodX()
  implMethodY()
}

RefinedAbstraction -up-|> Abstraction
ConcreteImplementor -up-|> Implementor
Abstraction o-right-> Implementor

note left of Abstraction::method1
  This method uses impl's method
end note
@enduml

f:id:akrfjmt:20170930052528p:plain

10. Strategyパターン

@startuml

class Context {
  strategy
  contextMethod()
}

abstract Strategy {
  {abstract} strategyMethod()
}

class ConcreteStrategy1 {
  strategyMethod()
}

class ConcreteStrategy2 {
  strategyMethod()
}

Context o-right-> Strategy
ConcreteStrategy1 -up-|> Strategy
ConcreteStrategy2 -up-|> Strategy

@enduml

f:id:akrfjmt:20170930052612p:plain

11. Compositeパターン

@startuml
 
class Client {
}
 
abstract Component {
  {abstract} method1()
  {abstract} method2()
  add()
  remove()
  getChild()
}
 
class Leaf {
  method1()
  method2()
}
 
class Composite {
  children
  method1()
  method2()
  add()
  remove()
  getChild()
}
 
Client -right-> Component : Uses >
Leaf -up-|> Component
Composite -up-|> Component
Composite o-up-> Component
 
@enduml

f:id:akrfjmt:20170930052731p:plain

12. Decoratorパターン

@startuml

abstract Component {
  {abstract} method1()
  {abstract} method2()
  {abstract} method3()
}

class ConcreteComponent {
  method1()
  method2()
  method3()
}

class Decorator {
  component
}

class ConcreteDecorator {
  method1()
  method2()
  method3()
}

ConcreteComponent -up-|> Component
Decorator -up-|> Component
Decorator o-up-> Component
ConcreteDecorator -up|> Decorator
@enduml

f:id:akrfjmt:20170930052815p:plain

13. Visitorパターン

@startuml

abstract Visitor {
  {abstract} visit(ConcreteElementA)
  {abstract} visit(ConcreteElementB)
}

class ConcreteVisitor {
  visit(ConcreteElementA)
  visit(ConcreteElementB)
}
ConcreteVisitor -up-|> Visitor

abstract Element {
  {abstract} accept()
}

class ConcreteElementA {
  accept()
}

class ConcreteElementB {
  accept()
}

class ObjectStructure

ConcreteElementA -up-|> Element
ConcreteElementB -up-|> Element
ObjectStructure o-left-> Element

@enduml

f:id:akrfjmt:20170930052958p:plain

14. Chain Of Responsibilityパターン

@startuml

class Client

abstract Handler
Handler : next
Handler : {abstract} request()

class ConcreteHandler1
ConcreteHandler1 : request()

class ConcreteHandler2
ConcreteHandler2 : request()

Client -right-> Handler : Request >
Handler o-right-> Handler
ConcreteHandler1 -up-|> Handler
ConcreteHandler2 -up-|> Handler

@enduml

f:id:akrfjmt:20170930053052p:plain

15. Facadeパターン

@startuml

class Client
class Facade
class ClassA
class ClassB
class ClassC
class ClassD

Client -down-> Facade : Uses >
Facade -down-> ClassA
Facade -down-> ClassB
Facade -down-> ClassC
Facade -down-> ClassD

ClassA -down-> ClassB
ClassB -right-> ClassC
ClassC -left-> ClassB
ClassD -down-> ClassC

@enduml

f:id:akrfjmt:20170930053241p:plain

16. Mediatorパターン

@startuml

abstract Mediator {
  {abstract} createColleagues()
  {abstract} colleagueChanged()
}

abstract Colleague {
  mediator
  {abstract} setMediator()
  {abstract} controlColleague()
}
class ConcreteMediator {
  concreteColleague1
  concreteColleague2
  concreteColleague3
  createColleagues()
  colleagueChanged()
}

class ConcreteColleague1 {
  controlColleague()
}

class ConcreteColleague2 {
  controlColleague()
}

class ConcreteColleague3 {
  controlColleague()
}

ConcreteMediator -up-|> Mediator
Colleague o-left-> Mediator

ConcreteColleague1 -up-|> Colleague
ConcreteColleague2 -up-|> Colleague
ConcreteColleague3 -up-|> Colleague

ConcreteMediator o-up-> ConcreteColleague1
ConcreteMediator o-up-> ConcreteColleague2
ConcreteMediator o-up-> ConcreteColleague3

@enduml

f:id:akrfjmt:20170930055628p:plain

17. Mementoパターン

@startuml

class Caretaker

class Originator {
  createMemento()
  restoreMemento()
}

class Memento <<wide interface>> <<narrow interface>> {
  ~getProtectedInfo
  +getPublicInfo
}

Caretaker -right-> Originator : Requests >
Caretaker o-down-> Memento
Originator -down-> Memento : Creates >

@enduml

f:id:akrfjmt:20170930055833p:plain

18. Observerパターン

@startuml

abstract Subject {
  observers
  addObserver()
  deleteObserver()
  notifyObserver()
  {abstract} getSubjectStatus()
}

abstract Observer {
  {abstract} update()
}

class ConcreteSubject {
  getSubjecdtStatus()
}

class ConcreteObserver {
  update()
}

Subject o-right-> Observer : Notifies >
ConcreteSubject -up-|> Subject
ConcreteObserver -up-|> Observer

@enduml

f:id:akrfjmt:20170930055934p:plain

19. Stateパターン

@startuml

class Context {
  state
  requestX()
  requestY()
  requestZ()
}

abstract State {
  methodA()
  methodB()
  methodC()
  methodD()
}

class ConcreteState1 {
  methodA()
  methodB()
  methodC()
  methodD()
}

class ConcreteState2 {
  methodA()
  methodB()
  methodC()
  methodD()
}

Context o-right-> State
ConcreteState1 -up-|> State
ConcreteState2 -up-|> State

@enduml

f:id:akrfjmt:20170930060025p:plain

20. Flyweightパターン

@startuml

class Flyweight {
  methodA()
  methodB()
}

class FlyweightFactory {
  pool
  getFlyweight()
}

class Client {
}

FlyweightFactory o-up-> Flyweight : Creates >
Client -up-> FlyweightFactory : Uses >
Client --up--> Flyweight : Uses >

@enduml

f:id:akrfjmt:20170930060200p:plain

21. Proxyパターン

@startuml

class Client

abstract Subject {
  request1()
  request2()
  request3()
}

class Proxy {
  realSubject
  request1()
  request2()
  request3()
}

class RealSubject {
  request1()
  request2()
  request3()
}

Client o-right-> Subject : Uses >
Proxy -up-|> Subject
RealSubject -up-|> Subject
Proxy o-right-> RealSubject : Uses >

@enduml

f:id:akrfjmt:20170930060312p:plain

22. Commandパターン

@startuml

class Command {
  execute()
}

class Invoker {
}

class Receiver {
  action()
}

class ConcreteCommand {
  receiver
  execute()
}

class Client {
}

Invoker o-left-> Command
ConcreteCommand -up-|> Command
ConcreteCommand o-left-> Receiver
Client -up-> ConcreteCommand : Creates <

@enduml

f:id:akrfjmt:20170930061027p:plain

23. Interpreterパターン

@startuml

class Client {
}

class Context {
  getInfoToInterpret()
}

abstract AbstractExpression {
  {abstract} interpret()
}

class TerminalExpression {
  interpret()
}

class NonterminalExpression {
  childExpressions
  interpret()
}

Client -left-> Context : Creates >
Client -right-> AbstractExpression : Uses >

TerminalExpression -up-|> AbstractExpression
NonterminalExpression -up-|> AbstractExpression
NonterminalExpression o--> AbstractExpression

@enduml

f:id:akrfjmt:20170930061114p:plain

感想

23つの中で私が好きなのはTemplate MethodパターンとBridgeパターンだが、もしもこれに名前がついてなかったら、

  • 「親クラスのメソッドから、子クラスで定義されるメソッドを呼び出すやつ」
  • 「コンストラクタで実装を渡すやつ」

という冗長な名前で呼ばれるか、もしくは、職場の誰かの名前か地名がついて

  • 「伊藤パターン」
  • 「銀座パターン」

とか呼ばれ、その結果レビューが意味不明になったろうなあと思っている。