デザインパターンをPlantUMLで確認(構造パターン編)

デザインパターンをPlantUMLで確認します。ここでは、構造パターンとして利用頻度の高いパターンについて取り上げます。( Decorator Adapter Facade Composite )

構造パターンでは、互いに関知すべきことをなるべく少なくして、疎結合な構造で実装するためのアイデアを与えてくれます。

デザインパターンをPlantUMLで確認(作成パターン編)
デザインパターンをPlantUMLで確認(構造パターン編) ← 今回
デザインパターンをPlantUMLで確認(振舞いパターン編)

Decorator
( 新しい責務を動的に装飾 )

@startuml
title: Decorator

interface Component {
  --
  + methodA()
  + methodB()
}

class ConcreteComponent {
  --
  + methodA()
  + methodB()
}

class Decorator {
  - Component wrappedObject
  --
  + methodA()
  + methodB()
}

class DecoratorA {
  - Component wrappedObject
  --
  + methodA()
  + methodB()
  + newBehavior()
}

class DecoratorB {
  - Component wrappedObject
  --
  + methodA()
  + methodB()
  + newBehavior()
}

Component <|.. ConcreteComponent
Component <|.. Decorator
Decorator <|-- DecoratorA
Decorator <|-- DecoratorB
Decorator --> Component : component

note bottom of DecoratorB
  // 実装例[新しい振る舞いを装飾]
  func methodA() {
    wrappedObject.methodA()
    this.newBehavior()
  }
end note
@enduml
603-desgin-pattern-structural-with-uml-decorator.png

Decoratorパターンは、必要な機能をサブクラスで装飾します。ベースクラスの処理を肥大化させずに、機能を拡張させることができます。ただし、多数のクラスが作られるので、利用者はどのクラスで機能を拡張させるのか理解しておく必要があります。

以下のような形で利用します。

$component = new ConcreteComponent();
$decoratorA = new DecoratorA($component);
$decoratorB = new DecoratorB($component);

動的に必要な機能を持ったオブジェクトを選択して利用することができます。

Adapter
( 目的のインタフェースに適合するよう変換 )

@startuml
title Adapter

class Client
interface Target {
  --
  + request()
}
class Adapter {
  - adaptee : Adaptee
  --
  + request()
}
class Adaptee {
  --
  + specificRequest()
}

Client -> Target
Target <|.. Adapter
Adapter -> Adaptee

note bottom of Adapter
  // 実装例
  // specificRequest() を request() に適合させる
  func request() {
    return adaptee.specificRequest()
  }
end note
@enduml
603-desgin-pattern-structural-with-uml-adapter.png

AdapterAdaptee をラッピングして Tagetインタフェース に適合させるように変換します。
クライアントは Targetインターフェース のみを関知していればよくて、Ataptee については関知しません。

Facade
( サブシステムをカプセル化して、高水準なインタフェースを提供 )

@startuml
title Facade

class Client
class Facade
Client -> Facade

package Subsystem {
  class ClassA
  class ClassB
  class ClassC
  class ClassD
  class ClassE
  class ClassF

  ClassA --> ClassC
  ClassA --> ClassF
  ClassB --> ClassD
  ClassB --> ClassE
  ClassC --> ClassD
  ClassF --> ClassD
}

Facade --> ClassA
Facade --> ClassB
@enduml
603-desgin-pattern-structural-with-uml-facade.png

クライアントは、Facadeで提供されている高水準なインタフェースのみ関知しており、Subsystem内のクラスは関知しません。

Composite
( 複合要素と単独要素を同一視して操作 )

@startuml
title Composite

class Client
interface Component {
  --
  + operation()
  + add(Component)
  + remove(Component)
  + getChild(int)
}
class Leaf {
  --
  + operation()
}
class Composite {
  - components
  --
  + operation()
  + add(Component)
  + remove(Component)
  + getChild(int)
}

Client -> Component
Component <|.. Leaf
Component "0..*" <--o "1" Composite
Component <|.. Composite
@enduml
603-desgin-pattern-structural-with-uml-composite.png

Compositeパターンは下記のように、複合要素と単独要素をもつ構造で活用できます。

├── menuA
├── menuB
│   ├── menuItemB1
│   ├── menuItemB2
│   └── menuC
│       ├── menuItemC1
│       ├── menuItemC2
│       └── menuItemC3
└── menuD

Class図に登場する概念の役割は以下のようになります。

  • Component
    • 葉と枝の両方のインタフェースを提供
  • Composite
    • 枝(複合要素)
      • 複数の葉を格納できる
  • Leaf
    • 葉(単独要素)

クライアントは枝と葉の違いを気にせず、同じメソッド(上記Class図では operationメソッド )を実行することができます。