デザインパターンを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
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
Adapter
が Adaptee
をラッピングして 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
クライアントは、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
Compositeパターンは下記のように、複合要素と単独要素をもつ構造で活用できます。
├── menuA
├── menuB
│ ├── menuItemB1
│ ├── menuItemB2
│ └── menuC
│ ├── menuItemC1
│ ├── menuItemC2
│ └── menuItemC3
└── menuD
Class図に登場する概念の役割は以下のようになります。
- Component
- 葉と枝の両方のインタフェースを提供
- Composite
- 枝(複合要素)
- 複数の葉を格納できる
- 枝(複合要素)
- Leaf
- 葉(単独要素)
クライアントは枝と葉の違いを気にせず、同じメソッド(上記Class図では operationメソッド
)を実行することができます。