# Design Pattern
## Creational Patterns
產生物件的機制,針對實際情況使用合適的方法建立物件,以提升code的靈活性與重複利用。
為什麼要有機制?因為基本的物件建立方式可能導致設計上的問題或是增加設計的複雜度。
主要關注在怎麼建立物件,核心思想就是把物件的建立與物件的使用分開。
### Singleton 單例
* 一個類只有一個實例,提供全域的安全存取點。
* 為什麼要控制實例數量?為了控制共享資源(共享文件、資料庫)的訪問。
* 全域的安全存取點?放在全域的變量方便使用也不安全,所以需要一個安全的通道確保變量不會因為被隨意的改動或覆蓋而導致程式崩潰。
<details style="color: red; width: auto; margin-top: 15px;">
<summary><I><b>程式碼(Golang)</b></I></summary>
```go=
package signalton
import (
"sync"
)
var once sync.Once
type signal struct {
Value int
}
var signalInstance *signal
func getInstance() *signal {
once.Do(func() {
signalInstance = &signal{Value: 5}
})
return signalInstance
}
```
</details>
<details style="color: green;padding: 3px; width: auto; margin-top: 15px;">
<summary>程式碼(kotlin)</summary>
```kotlin=
fun main(){
Singleton.getInstance(3)
Singleton.getInstance(10).printValue()
// 3
}
class Singleton{
companion object{
private var instance: Singleton? = null
fun getInstance(value: Int): Singleton {
if (instance == null) {
// 同步鎖
synchronized(Singleton::class.java) {
if (instance == null) {
instance = Singleton(value)
}
}
}
return instance!!
}
}
fun printValue(): Unit {
println(this.value)
}
}
```
</details>
### Pototype 原型
* 是一種可以複製已有的物件的創建方法。
* 在複製物件時,無法複製物件的private fields。
### Factory Method 工廠方法
Ex: 海運與陸運
* 在父類別中提供一個interface來產生物件,允許子類別修改物件的型態(type)來產生物件。
* 透過實作相同的介面去做不同的事情
* client端只需要知道不同運輸方式都有deliver的方法即可。
<details style="color: red; width: auto; margin-top: 15px;">
<summary><I><b>程式碼(Golang)</b></I></summary>
```go=
package factorymethod
import (
"errors"
"fmt"
)
type Logistics interface {
CreateTransport() Transport
}
type roadLogistics struct{}
func (rl roadLogistics) CreateTransport() Transport {
return truck{}
}
type seaLogistics struct{}
func (sl seaLogistics) CreateTransport() Transport {
return ship{}
}
type Transport interface {
Deliver()
}
type truck struct{}
func (t truck) Deliver() {
fmt.Println("truck delivery")
}
type ship struct{}
func (s ship) Deliver() {
fmt.Println("ship delivery")
}
func NewLogistics(t string) (Logistics, error) {
switch t {
case "road":
return roadLogistics{}, nil
case "sea":
return seaLogistics{}, nil
}
return nil, errors.New(t + " 沒有符合的選項")
}
```
</details>
<details style="color: green;padding: 3px; width: auto; margin-top: 15px;">
<summary>程式碼(kotlin)</summary>
```kotlin=
fun main() {
val roadLogistics: Logistics = RoadLogistics() // 產生物件
roadLogistics.planDelivery() // 利用物件呼叫行為
//truck delivery
}
// 使用物件
abstract class Logistics{
fun planDelivery(){
val t: Transport = createTransport()
t.deliver()
}
abstract fun createTransport(): Transport
}
class RoadLogistics: Logistics() {
override fun createTransport(): Transport {
return Truck()
}
}
class SeaLogistics: Logistics() {
override fun createTransport(): Transport {
return Ship()
}
}
// 產生物件
interface Transport{
fun deliver()
}
class Truck(): Transport{
override fun deliver() {
println("truck delivery")
}
}
class Ship(): Transport{
override fun deliver() {
println("ship delivery")
}
}
```
</details>
### Abstract Factory 抽象工廠
* 可以生產相關的物件,沒有指定他們的具體類別。
* 可以透過更改工廠類型回傳不同風格(維多利亞風格、現代風格)的產品(沙發、椅子、...)。
* client端只需要知道這個物件(椅子)有的方法(sitOn),而不在乎他的風格。
:::info
家具工廠(interface)有沙發(abstract class)、椅子、桌子三條生產線(createSofa,...)
A工廠(implement 家具工廠)製造維多利亞風格的家具,B工廠製造現代風格的家具。
:::
<details style="color: red; width: auto; margin-top: 15px;">
<summary><I><b>程式碼(Golang)</b></I></summary>
```go=
package abstractfactory
import "log"
type FurnitureFactory interface {
CreateChair() Chair
CreateSofa() Sofa
CreateCoffeeTable() CoffeeTable
}
type VictorianFurnitureFactory struct{}
func (vff VictorianFurnitureFactory) CreateChair() Chair {
return VictorianChair{}
}
func (vff VictorianFurnitureFactory) CreateSofa() Sofa {
return VictorianSofa{}
}
func (vff VictorianFurnitureFactory) CreateCoffeeTable() CoffeeTable {
return VictorianCoffeeTable{}
}
type Chair interface {
HasLegs()
SitOn()
}
type VictorianChair struct{}
func (vc VictorianChair) HasLegs() {
log.Println("4")
}
func (vc VictorianChair) SitOn() {
log.Println("sit on VictorianChair")
}
type Sofa interface {
HasLegs()
SitOn()
}
type VictorianSofa struct{}
func (vs VictorianSofa) HasLegs() {
log.Println("2")
}
func (vc VictorianSofa) SitOn() {
log.Println("sit on VictorianSofa")
}
type CoffeeTable interface {
HasLegs()
SitOn()
}
type VictorianCoffeeTable struct{}
func (vct VictorianCoffeeTable) HasLegs() {
log.Println("2")
}
func (vct VictorianCoffeeTable) SitOn() {
log.Println("sit on VictorianCoffeeTable")
}
```
</details>
<details style="color: green;padding: 3px; width: auto; margin-top: 15px;">
<summary>程式碼(kotlin)</summary>
```kotlin=
fun main() {
val factory = VictorianFurnitureFactory()
factory.createChair().sitOn()
// sit on VictorianChair
}
interface FurnitureFactory {
fun createChair(): Chair
fun createSofa(): Sofa
fun createCoffeeTable(): CoffeeTable
}
class VictorianFurnitureFactory : FurnitureFactory {
override fun createChair(): Chair {
return VictorianChair()
}
override fun createSofa(): Sofa {
return VictorianSofa()
}
override fun createCoffeeTable(): CoffeeTable {
return VictorianCoffeeTable()
}
}
abstract class Chair {
abstract fun hasLegs()
abstract fun sitOn()
}
class VictorianChair : Chair() {
override fun hasLegs() {
println("4")
}
override fun sitOn() {
println("sit on VictorianChair")
}
}
abstract class Sofa {
abstract fun hasLegs()
abstract fun sitOn()
}
class VictorianSofa : Sofa() {
override fun hasLegs() {
println("2")
}
override fun sitOn() {
TODO("Not yet implemented")
}
}
abstract class CoffeeTable {
abstract fun hasLegs()
abstract fun sitOn()
}
class VictorianCoffeeTable : CoffeeTable() {
override fun hasLegs() {
println("2")
}
override fun sitOn() {
TODO("Not yet implemented")
}
}
```
</details>
### Builder 建造者
* 分步驟產生複雜的物件。(零件組成產品)
* 可以用相同的code產生不同形式(零件的差異)的物件。
* 遇到擁有很多參數的物件,卻不是每個參數都會被使用時,就可以使用建造者模式將參數抽取成獨立的物件,用獨立的物件(小零件)組成複雜的物件(產品)。
* 想生產不同形式的產品(木頭房子、磚頭房子)時,也可以使用builder創建。
* ==Director==是一個負責創建產品流程順序的類別,builder則負責實作零件。
* 不一定需要Director,但有利於創建例行性的物件重複利用。
<details style="color: red; width: auto; margin-top: 15px;">
<summary><I><b>程式碼(Golang)</b></I></summary>
```go=
package builder
import "log"
type CarType int
const (
SUV CarType = iota
GTR
)
type Builder interface {
setCarType(cartype CarType)
setDoors(doors int)
setEngine(engin string)
}
type CarBuilder struct {
cartype CarType
doors int
engin string
}
func (cb *CarBuilder) setCarType(cartype CarType) {
cb.cartype = cartype
log.Println("build type..")
}
func (cb *CarBuilder) setDoors(doors int) {
cb.doors = doors
log.Println("build doors..")
}
func (cb *CarBuilder) setEngine(engin string) {
cb.engin = engin
log.Println("build engin..")
}
func (cb *CarBuilder) GetResult() *Car {
return &Car{
cartype: cb.cartype,
doors: cb.doors,
engin: cb.engin,
}
}
type ManualBuilder struct {
cartype CarType
doors int
engin string
}
func (mb *ManualBuilder) setCarType(cartype CarType) {
mb.cartype = cartype
log.Println("build type..")
}
func (mb *ManualBuilder) setDoors(doors int) {
mb.doors = doors
log.Println("build doors..")
}
func (mb *ManualBuilder) setEngine(engin string) {
mb.engin = engin
log.Println("build engin..")
}
func (mb *ManualBuilder) GetResult() *Manual {
return &Manual{
cartype: mb.cartype,
doors: mb.doors,
engin: mb.engin,
}
}
type Car struct {
cartype CarType
doors int
engin string
}
func (c *Car) GetCarType() CarType {
return c.cartype
}
func (c *Car) GetDoors() int {
return c.doors
}
func (c *Car) GetEngin() string {
return c.engin
}
type Manual struct {
cartype CarType
doors int
engin string
}
func (m *Manual) PrintManual() {
log.Printf("====Manual====\n")
log.Printf("CarType: %v\n", m.cartype)
log.Printf("Doors: %v\n", m.doors)
log.Printf("Engin: %v\n", m.engin)
}
type Director struct{}
func (d *Director) ConstructSUV(builder Builder) {
builder.setCarType(SUV)
builder.setDoors(4)
builder.setEngine("EnginForSUV")
}
func (d *Director) ConstructGTR(builder Builder) {
builder.setCarType(GTR)
builder.setDoors(2)
builder.setEngine("EnginFOrGTR")
}
```
</details>
<details style="color: green;padding: 3px; width: auto; margin-top: 15px;">
<summary>程式碼(kotlin)</summary>
```kotlin=
fun main() {
val carBuilder = CarBuilder()
val director = Director()
val manualBuilder = ManualBuilder()
// 建造GTR
director.constructGTR(carBuilder)
// 建造GTR的操作手冊
director.constructGTR(manualBuilder)
val manual: Manual = manualBuilder.getResult()
manual.printManual()
// build type..
// build doors..
// build engin..
// ====Manual====
// CarType: GTR
// Doors: 2
// Engin: EnginForGTR
}
// 以車子與操作手冊為例
enum class CarType{
SUV, GTR
}
interface Builder{
fun setCarType(carType: CarType)
fun setDoors(doors: Int)
fun setEngine(engin: String)
}
class CarBuilder : Builder{
private lateinit var carType: CarType
private var doors = 0
private var engin = ""
override fun setCarType(carType: CarType) {
this.carType = carType
println("build type..")
}
override fun setDoors(doors: Int) {
this.doors = doors
println("build doors..")
}
override fun setEngine(engin: String) {
this.engin = engin
println("build engin..")
}
fun getResult(): Car{
return Car(carType, doors, engin)
}
}
class ManualBuilder : Builder{
private lateinit var carType: CarType
private var doors = 0
private var engin = ""
override fun setCarType(carType: CarType) {
this.carType = carType
}
override fun setDoors(doors: Int) {
this.doors = doors
}
override fun setEngine(engin: String) {
this.engin = engin
}
fun getResult(): Manual{
return Manual(carType, doors, engin)
}
}
class Car{
private val carType: CarType
private val doors: Int
private val engin: String
constructor(
carType: CarType,
doors: Int,
engin: String
){
this.carType = carType
this.doors = doors
this.engin = engin
}
fun getCarType(): CarType{
return carType
}
fun getDoors(): Int{
return doors
}
fun getEngin(): String{
return engin
}
}
class Manual{
private val carType: CarType
private val doors: Int
private val engin: String
constructor(
carType: CarType,
doors: Int,
engin: String
){
this.carType = carType
this.doors = doors
this.engin = engin
}
fun printManual(){
println("====Manual====")
println("CarType: $carType")
println("Doors: $doors")
println("Engin: $engin")
}
}
// 管理車輛生產過程
class Director{
fun constructSUV(builder: Builder){
builder.setCarType(CarType.SUV)
builder.setDoors(4)
builder.setEngine("EnginForSUV")
}
fun constructGTR(builder: Builder){
builder.setCarType(CarType.GTR)
builder.setDoors(2)
builder.setEngine("EnginForGTR")
}
}
```
</details>
## Structural Patterns
如何將物件與類別組合成複雜的結構,又同時保持結構的靈活性與效能。
### Adapter Pattern 適配器
- 讓不相容的物件與介面/類別互相合作。
- 當遇到與格式不兼容的資料時,無法直接修改程式碼的情況下,可以創建一個Adapter用來轉換資料的格式。
### Bridge Pattern 橋接
- 將一個大的類別,或是複雜的類別,拆成抽象與實作兩個獨立的部分,可以在開發的時候分別使用。
- 可以用於想要拆分或重組一個複雜的類別。
### Composite Pattern 組合
- 可以把物件組合成樹狀結構,可以像使用獨立物件一樣使用他們。
### Decorator Pattern 裝飾器
### Facade Pattern 門面
### Flyweight Pattern 輕量級
### Proxy Pattern 代理人
## Behavioral Pattern