Design Patterns · Part 3 of 4
Introduction to Structural Design Patterns
Contents
In the last blog post, we’ve explored on the creational design patterns. In this blog post, we would explore a different set of design patterns: the structural ones. The structural design patterns are concerned with how classes and objects are composed to form even larger structure. The primary objective here is to compose the components in a flexible and extensible way, so that change can be made in specific parts of the structure without changing the entire structure.
Adapter Pattern:
Intent: Covert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn’t otherwise due to incompatible interfaces.
Class Diagram:
Class Diagram: adapter
Implementation:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package adapter
import (
"fmt"
)
// -- Adaptee ---
type Adaptee interface {
SpecificRequest()
}
func NewAdaptee() Adaptee {
return &adaptee{}
}
type adaptee struct{}
func (adptr *adaptee) SpecificRequest() {}
// -- Target ---
type Target interface {
Request()
}
func NewTarget() Target {
return &target{}
}
type target struct{}
func (tgt *target) Request() {}
// -- Adapter ---
func NewAdapter() Target {
return &adapter{adaptee: NewAdaptee()}
}
type adapter struct {
adaptee Adaptee
}
func (adpt *adapter) Request() {
fmt.Println("Using the requesst specific method for adaptee!")
adpt.adaptee.SpecificRequest()
}
Bridge Pattern:
Intent: Decouple an abstraction from its implementation so that it can vary independently
Class Diagram:
Class Diagram: bridge
Implementation:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package bridge
import (
"fmt"
)
type Abstraction interface {
Operation()
}
type Implementor interface {
OperationImp()
}
func NewRefinedAbstraction(imp Implementor) Abstraction {
return &refinedAbstraction{imp: imp}
}
type refinedAbstraction struct {
imp Implementor
}
func (abs *refinedAbstraction) Operation() {
abs.imp.OperationImp()
}
func NewConcreteImplementorA() Implementor {
return &ConcreteImplementorA{}
}
type ConcreteImplementorA struct{}
func (impl *ConcreteImplementorA) OperationImp() {
fmt.Println("Inside operationImp of concrete implementor A")
}
Composite Pattern:
Intent: Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.
Class Diagram:
Class Diagram: composite
Decorator Pattern:
Intent: Attach additional responsibilities to an object dynamically. Decorator provides a flexible alternative to subclassing for extending functionality.
Class Diagram:
Class Diagram: decorator
Facade Pattern:
Intent: Provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use.
Class Diagram:
Class Diagram: facade
Flyweight Pattern:
Intent: Use sharing to support large numbers of fine-grained objects efficiently.
Class Diagram:
Class Diagram: flyweight
Object Diagram:
Object Diagram: flyweight Object
Proxy Pattern:
Intent: Provide a surrogate or placeholder for another object to control access to it.
Class Diagram:
Class Diagram: proxy
Object Diagram:
Object Diagram: proxy Obj
References:
- Design Patterns: Elements of Reusable Object-oriented Software
- Structural Design Pattern