---
title: 'PK - 行為模式'
disqus: kyleAlien
---
PK - 行為模式
===
## Overview of Content
行為模式包括責任鏈、命令、解釋器、迭代、仲介、備忘錄、觀察者、狀態、策略、模板、訪問者模式(相對的多),它們有很多相似點
每種不同的設計都有其「**特殊的行為**」
:::success
* 如果喜歡讀更好看一點的網頁版本,可以到我新做的網站 [**DevTech Ascendancy Hub**](https://devtechascendancy.com/)
本篇文章對應的是 [**行為模式 – 5 個 PK | Command vs Strategy vs State | Observer vs Chain | 最佳實踐**](https://devtechascendancy.com/pk-design-patterns-cmd-strat-state-obs-chain/)
:::
[TOC]
## 命令、策略
命令、策略設計模式 - 兩者區別
* **策略模式**:
**封裝算法**,並且每個算法都可以相互替換、並獨立維護
> **每個算法皆為原子業務**,不可再拆分的任務
* **命令模式**:
**對 ++動作++ 的解耦**,把一個動作(行為)封裝程一個對象(接收者),並透過執行對象(接收命令者)執行動作
> 解耦「執行」、「行為」兩個動作,**行為有時候不可相互替換**(不同設計概念就會產生不同的特性,之後會說明)
### 策略之替換算法
* 策略模式的核心概念在於 **算法的相互替換**
Example: 接下來實做一個儲存數據的方案,每個方案代表不同算法,並且這些方案都可以相互替換
> 
1. **抽象算法**:
對外說明使用該合約界面可以取得、設定數據… 以抽象的角度來說明這界的功能;而具體的方案實做要了解實做類(就是讀 `Document` 啦~)
```kotlin=
interface IStorage {
fun getData() : Any
fun setData(data: Any)
}
```
2. **實現算法**:各個算法的實做
```kotlin=
// 儲存 Field
class FieldStorage : IStorage {
private var data : Any? = null
override fun getData(): Any {
data.apply {
if (this == null) {
throw RuntimeException()
}
return this
}
}
override fun setData(data: Any) {
this.data = data
}
}
// ------------------------------------------------------
// 儲存 List 列表
class ListStorage : IStorage {
private var list = mutableListOf<Any>()
override fun getData(): Any {
if (list.size == 0) {
throw RuntimeException()
}
return list[0].also {
list.remove(it)
}
}
override fun setData(data: Any) {
list.add(data)
}
}
```
3. **環境類**:
環境類依賴於界面(`interface`, `IStorage`),而非實做,**符合依賴倒置原則**
> 之所以將其稱為環境類,是因為它程載了 `IStorage` 的實體,但是又不是真正的實做類,相對來講是運行界面的平台
```kotlin=
class Context constructor(var storage: IStorage) {
fun getData() : String {
return storage.getData() as String
}
fun setData(data: String) {
storage.setData(data)
}
}
```
* **使用者**:使用者會直接接觸、選擇具體的算法(這裡不符合迪米特原則),並透過 `Context` 環境類來運行(但環境類符合依賴倒置的方案,依賴抽象而不是實做)
```kotlin=
fun main() {
val initStorage = ListStorage()
val contextEnv = Context(initStorage).apply {
setData("123")
println(getData())
}
contextEnv.apply {
storage = FieldStorage()
setData("888")
println(getData())
}
}
```
> 
### 命令之封裝
* 命令模式的核心概念在於 **++封裝++命令**
**讓請求者、實現者(接收者)解耦,不管接收者如何變化,都不會影響請求者**
:::info
* 實做範例如上,實現與上面策略模式相同的需求(不同的儲存方案)
:::
1. **抽象命令、具體命令**:
使用跟策略模式相同的界面是抽象(對外保證)、實做的類(對內實做、實現)
```kotlin=
interface IStorage {
fun getData() : Any
fun setData(data: Any)
}
// ------------------------------------------------------
// 具體命令
class FieldStorage : IStorage {
private var data : Any? = null
override fun getData(): Any {
data.apply {
if (this == null) {
throw RuntimeException()
}
return this
}
}
override fun setData(data: Any) {
this.data = data
}
}
// 具體命令
class ListStorage : IStorage {
private var list = mutableListOf<Any>()
override fun getData(): Any {
if (list.size == 0) {
throw RuntimeException()
}
return list[0]
}
override fun setData(data: Any) {
list.add(data)
}
}
```
2. **抽象命令**:
命令的基礎類,它的核心要點在於「**抽象化命令**」,並且依賴於抽象接收者(`IStorage`),符合依賴倒置原則
```kotlin=
abstract class CommonCmd {
companion object {
@JvmStatic
protected val fieldStorage: IStorage = FieldStorage()
@JvmStatic
protected val listStorage: IStorage = ListStorage()
}
abstract fun controlData(data: String) : Any
}
```
3. **具體命令**:
既然上面有抽象化的命令,那這裡就會有命令的實做類;
它會將每個命令細分為不同的類,每個類代表了一個功能…
```kotlin=
class StorageOneData : CommonCmd() {
override fun controlData(data: String): Any {
fieldStorage.setData(data)
return Unit
}
}
class GetOneStorageData : CommonCmd() {
override fun controlData(data: String): Any {
return fieldStorage.getData()
}
}
class StorageListData : CommonCmd() {
override fun controlData(data: String): Any {
listStorage.setData(data)
return Unit
}
}
class GetListStorageData : CommonCmd() {
override fun controlData(data: String): Any {
return listStorage.getData()
}
}
```
4. **調用者**(環境類):
其功能就跟策略模式的 **環境類相同**,它的存在是為了調用抽象命令;
但它可以持有 `1 ~ 多個` **抽象命令**,並且可以在這邊做出不同花樣(紀錄、替換、取消... 等等行為)
```kotlin=
class Invoker(var cmd: CommonCmd) {
fun controlData(data: String) : Any {
return cmd.controlData(data)
}
}
```
* **使用者調用**:
**抽象命令的替換(實做替換)是個關鍵**,替換命令代表了不同的行為實做
:::warning
* 但相對的,這種設計方式不符合迪米特(最少知識)原則
使用者仍要了解到每個實做的含意
:::
```kotlin=
fun main() {
val initCmd : CommonCmd = StorageOneData()
val invoker = Invoker(initCmd).apply {
controlData("Hello")
// 換命令
cmd = GetOneStorageData()
println(controlData(""))
}
invoker.apply {
// 換命令
cmd = StorageListData()
controlData("World")
// 換命令
cmd = GetListStorageData()
println(controlData(""))
}
}
```
> 
* 在這裡其實有另外一個關鍵概念:
以上我們使用策略模式的實做,它的 **界面是依照功能分類**,在這裡我們可以換個抽象概念,**依照 ==職責== 去重新規劃界面**
1. 抽象接收者界面:**改成依照責任去規劃**
```kotlin=
interface IStorageData {
fun fieldDataControl(data: Any) : Any
fun listDataControl(data: Any) : Any
}
```
2. **接收者實做**:依照具體接收者(執行者)實做
```kotlin=
class SaveData constructor(var data: Any?, val list: MutableList<Any>) : IStorageData {
override fun fieldDataControl(data: Any): Any {
this.data = data
return Unit
}
override fun listDataControl(data: Any): Any {
list.add(data)
return Unit
}
}
class GetData constructor(var data: Any?, val list: MutableList<Any>): IStorageData {
override fun fieldDataControl(data: Any): Any {
this.data.apply {
if (this == null) {
throw RuntimeException()
}
return this
}
}
override fun listDataControl(data: Any): Any {
if (list.size == 0) {
throw RuntimeException()
}
return list[0]
}
}
```
3. **抽象命令**:命令使用的類也會跟著修改,修改為依照責任的接收者(執行者)
```kotlin=
abstract class CommonCmd2 {
companion object {
var data: Any? = null
val list = mutableListOf<Any>()
@JvmStatic
protected val saveData: IStorageData = SaveData(data, list)
@JvmStatic
protected val getStorage: IStorageData = GetData(data, list)
}
abstract fun controlData(data: String) : Any
}
```
4. **具體命令**:既然接收者(執行者)更改,那命令的調用方式也會跟著修改
> 雖然依照接收者修改邏輯,仍符單一職責,**一個類包裝一個命令**
```kotlin=
class StorageOneData2 : CommonCmd2() {
override fun controlData(data: String): Any {
saveData.fieldDataControl(data)
return Unit
}
}
class GetOneStorageData2 : CommonCmd2() {
override fun controlData(data: String): Any {
return getStorage.fieldDataControl("")
}
}
class StorageListData2 : CommonCmd2() {
override fun controlData(data: String): Any {
saveData.listDataControl(data)
return Unit
}
}
class GetListStorageData2 : CommonCmd2() {
override fun controlData(data: String): Any {
return getStorage.listDataControl("")
}
}
```
* 調用者:調用者只須修改依賴界面即可
```kotlin=
class Invoker2(var cmd: CommonCmd2) {
fun controlData(data: String) : Any {
return cmd.controlData(data)
}
}
```
:::success
- 從這裡可以看出來命令模式並不一定要跟策略相同,上面 **依照責任設計界面,就不適用於策略模式!**(因為 **責任不可相互替換**)
:::
### 命令 vs 策略 - 最佳實踐
* **關注點不同**
* **策略模式**:關注於算法的完整性、封裝性,只有同時具備這兩個特點才能簡單、自由切換不同算法
* **命令模式**:關注於解耦呼叫者、執行者(接收者),解耦兩者是這個模式最注重的要點,並且會細分每個命令(一個命令一個類)
:::info
由於封裝了命令,所以可以對命令做不同的處理… 像是撤銷、紀錄
:::
* **角色功能不同**
* **策略模式**:**具體算法是一個原子業務**,一旦變更就是對算法整體的變更
* **命令模式**:命令模式只要沒有個接收者,其實就跟策略模式差不多;換個角度來說,**命令模式的抽象算法 + 實做算法,就類似於策略模式的執行者(接收者)**
:::info
可以清楚拆分請求者、執行者的關係,當執行只被修改,也不會影響到請求者
:::
## 策略、狀態
兩者的目標(++業務的關注點++)不同
* **策略模式**:
1. 封裝算法,並交替算法,**算法之間不會相互交集(也不會調用到)**
2. **策略的切換由使用者操作**
* **狀態模式**:
1. 封裝不同狀態,**透過狀態的切換達到相同方法,不同行為反應**
2. **狀態的切換可由外部、也可由內部(子類)切換**
### 策略之獨立算法
* 策略模式封裝算法,並且每個算法都可以獨立維護,不同算法之間沒有相互交集
:::info
* 以播放音樂為例,每種音樂的風格都不同,不同風格用不同策略,要切換風格時也由使用者切換
> 
:::
1. **抽象策略**:每個類的共同抽象方法
> 每個歌曲都必須區分哪個歌手是屬於哪種曲風
```kotlin=
interface IMusic {
fun musicStyle()
}
```
2. **策略實做**:**每個策略只須完成自身邏輯即可**
```kotlin=
class Classical : IMusic {
override fun musicStyle() {
println("莫札特")
println("巴哈")
println("蕭邦")
}
}
class Soul: IMusic {
override fun musicStyle() {
println("Johnny")
println("LoFi")
}
}
class Pop : IMusic {
override fun musicStyle() {
println("周杰倫")
println("張學友")
}
}
```
3. **環境類**:操作策略的環境,隔離使用者直接操作實例;並且這邊對外開放的方法可以略做修改,不必與策略同名(可以再進行包裝)
```kotlin=
class MusicRadioStation constructor(var music: IMusic) {
fun playMusic() {
music.musicStyle()
}
}
```
* **使用者使用**:在這邊使用者要關注具體作法(不符合迪米特原則),並 **由使用者按照商業邏輯切換算法**
```kotlin=
fun main() {
MusicRadioStation(Classical()).apply {
println("--- Morning ---")
playMusic()
println("--- Afternoon ---")
music = Soul()
playMusic()
println("--- Night ---")
music = Pop()
playMusic()
}
}
```
### 狀態之自動切換
* 狀態模式會包裝每個狀態,並在適當時機由內部自己切換不同狀態
:::info
* 同樣實現音樂風格的播放,不過這邊多了個商業邏輯,每個時段播放不同風格的音樂(也就是 **按照時間切換狀態**)
> 
:::
1. **抽象狀態**:與策略模式相同,有一個必須實現的方法,但是 **它多了一個環境參數,這個環境參數相當重要,它用來給子類切換狀態用**
```kotlin=
abstract class MusicCentral {
var station: DailyMusicRadioStation? = null
abstract fun musicStyle()
}
```
2. **狀態實做**:這裡有時做需要達成 ++兩件事++,^1.^ **完成自身狀態該做的行為**、^2.^ **在合適時機取父類的狀態參數切換狀態**
```kotlin=
class ClassicalCentral : MusicCentral() {
override fun musicStyle() {
println("莫札特")
println("巴哈")
println("蕭邦")
println("Morning music finish play.")
station?.changeStyle(DailyMusicRadioStation.Style.SOUL)
}
}
class SoulCentral : MusicCentral() {
override fun musicStyle() {
println("Johnny")
println("LoFi")
println("Afternoon music finish play.")
station?.changeStyle(DailyMusicRadioStation.Style.POP)
}
}
class PopCentral : MusicCentral() {
override fun musicStyle() {
println("周杰倫")
println("張學友")
println("Night music finish play.")
station?.changeStyle(DailyMusicRadioStation.Style.CLASSICAL)
}
}
```
3. **環境類**:狀態模式的環境類比起策略模式的環境類更加的重要,**它對外 ^1.^ 提供切換狀態的方法**(給其他子類使用),並 ^2.^ 提供操作的方法
```kotlin=
class DailyMusicRadioStation {
private val classical = ClassicalCentral().apply {
station = this@DailyMusicRadioStation
}
private val soul = SoulCentral().apply {
station = this@DailyMusicRadioStation
}
private val pop = PopCentral().apply {
station = this@DailyMusicRadioStation
}
private var curStyle: MusicCentral = classical
enum class Style {
CLASSICAL,
SOUL,
POP
}
// 提供切換狀態的方法
fun changeStyle(style: Style) {
curStyle = when(style) {
Style.CLASSICAL -> classical
Style.SOUL -> soul
Style.POP -> pop
}
}
// 提供操作的方法
fun playDailyMusic() {
curStyle.musicStyle()
}
}
```
* **使用者使用**:
:::info
* 切換狀態的責任交給狀態子類,而不是由外部主動切換(管理)狀態!
:::
```kotlin=
fun main() {
DailyMusicRadioStation().apply {
playDailyMusic()
playDailyMusic()
playDailyMusic()
playDailyMusic()
}
}
```
> 
### 策略 vs. 狀態 - 最佳實踐
以 UML 設計圖來看,兩者的差異相差不大,但從上面範例來看,實際使用起來的功能相差其實是非常大的;差異比較如下
* **環境角色的差異**
* 策略模式:環境角色作為一個委託,代理執行實際的算法
* 狀態模式:具有 **紀錄狀態、操作狀態** 的兩大功能,並擁有所有狀態的實現
* **目標解決方案的差異**(設計想解決的問題)
* 策略模式:把內部實做的算法包裝,讓其對外部的影響降到最低
* 狀態模式:每個子類的狀態看似沒有關係,但其實 **內部狀態的切換會影響最終的商業邏輯結果**
> 封裝狀態並暴露行為,內部狀態的改變在外部看起來就像是同個方法但切換了作法
* **解決問題的方案不同**
* 策略模式:只保證算法的功能,但切換算法要由外部決定
* 狀態模式:狀態的變化由環境角色、具體狀態共同完成(**封裝了狀態的變化,但暴露了不同的行為、結果!**)
* **應用場景的不同**:
* 策略模式:策略模式由於相互可替換,**所有算法必須平起平坐**(平行的!)
* 狀態模式:**一系列的狀態觸發了相同方法但不同結果的行為**,常用於一個物件有 2 維描述的時候(有狀態、行為)
> 如果只有狀態,或是只有行為,那狀態模式就失去了其核心意義
* **複雜度不同**:
* 策略模式:對於設計來說相當清晰,並且也方便拓展,複雜度低
* 狀態模式:狀態模式下由於內部的切換邏輯,對外部產生了不同的行為結果,雖說方便拓展,但是要寫測試,測試商業邏輯,相對來說 **複雜度較高**
## 觀察者、責任鏈
觀察者、責任鏈的相關性在於
1. **觸發性**:都是透過一個函數調用,來達到觸發各個相關類的被調用
2. **觀察、被觀察**:責任鏈當中的每個節點,都可以用觀察者(接收上一個任務結果)、被觀察者(傳遞結果給下一個任務)來解釋
### 責任鏈之責任交接
:::info
* 這裡來看 DNS 機制,DNS 可以幫我們把查看網域對應的 IP 地址
**DNS 機制**:當然不會由一個伺服器記住所有對應數據,DNS 協議會讓每個區域記住自己的網域名,查找不到才往上傳遞查詢!
> 這就是 **責任鏈機制**,交接責任到下一個任務
>
> 
* UML 如下圖
> 
:::
* 使用標準責任鏈實做 DNS 傳遞查詢的機制
1. **請求者類**:這裡做標準的儲存類;礎觀察者,不抽像話請求者
:::success
這個簡單的儲存類也可以稱為 BO(`Business object`) 對象
:::
```kotlin=
data class Recorder constructor(val domain: String, val ip: String, val owner: String) {
override fun toString(): String {
return "Domain: $domain, ip: $ip, owner: $owner"
}
}
```
2. **責任抽象類**:宣告每個子類都必須實做的方法(包括 **判斷、處理**)
```kotlin=
abstract class DnsServer {
var nextServer: DnsServer? = null
fun resolve(dns: String): Recorder {
if (isRecord(dns)) {
return echo(dns)
}
nextServer.let {
if (it == null) {
throw RuntimeException("Cannot resolve.")
}
return it.resolve(dns)
}
}
// 判斷
abstract fun isRecord(dns: String) : Boolean
// 處理
abstract fun echo(dns: String) : Recorder
protected fun randomIp(): String {
Random().apply {
var ip = ""
repeat(4) {
ip += "${nextInt(255)}."
}
return ip.removeRange(ip.length - 1, ip.length)
}
}
}
```
3. **責任實做類**:完成每個完成自生邏輯判斷,**符合最少知識原則(它們不需要知道其他類的傳遞,只須關心自身邏輯)**
```kotlin=
class CityDnsService: DnsServer() {
override fun isRecord(dns: String): Boolean {
return dns.endsWith(".taipei").apply {
println("${this@CityDnsService::class.simpleName} record? $this")
}
}
override fun echo(dns: String): Recorder {
return Recorder(dns, randomIp(), "Taipei 101 DNS")
}
}
class CountryDnsService: DnsServer() {
override fun isRecord(dns: String): Boolean {
return dns.endsWith(".tw").apply {
println("${this@CountryDnsService::class.simpleName} record? $this")
}
}
override fun echo(dns: String): Recorder {
return Recorder(dns, randomIp(), "Taiwan DNS")
}
}
class WorldDnsService: DnsServer() {
override fun isRecord(dns: String): Boolean {
return true.apply {
println("${this@WorldDnsService::class.simpleName} record? $this")
}
}
override fun echo(dns: String): Recorder {
return Recorder(dns, randomIp(), "World DNS")
}
}
```
* **使用者使用**:
```kotlin=
fun main() {
val cityDnsService = CityDnsService().apply {
nextServer = CountryDnsService().apply {
nextServer = WorldDnsService()
}
}
cityDnsService.resolve("www.androidJavaKt.tw").apply {
println(this)
}
}
```
> 
### 觀察者之優化責任鏈
:::warning
* **修正 DNS 修正**?
在上面我們使用責任鏈來完成了 DNS 的機制,但其實 **並不完全實現了 DNS 機制**,以下幾點尚未達成
1. DNS 會緩存之前已經請求過得數據(方便下次快速回覆)
2. DNS 只與自身的左右(節點)交互
> 
:::success
* 透過觀察者修正之後,有以下差異
1. **雙重身份**:**DNS 伺服器具有觀察者、被觀察者的雙重身份!**
2. **業務邏輯**:設定下一個時,其實是設定自己的觀察者,而被 **通知更新時,也是 ++觸發事件++ 通知自己的觀察者**
:::
* UML 如下圖
> 
:::
* 首先要結合 **觀察者模式**,就必須先把觀察者的基礎結構建立
```kotlin=
// 觀察者
interface IObserver<T> {
fun update(data: T)
}
// 被觀察者
open class Observable<T> {
var list = mutableSetOf<IObserver<T>>()
fun notify(data: T) {
list.forEach {
it.update(data)
}
}
}
```
* 接著就可以按照 DNS 協議的邏輯來優化責任鏈
1. **責任鏈抽象類**:建立責任鏈抽象類,該類修改很大,它 **既是觀察者,又是被觀察者**,所以必須實做關者者界面(`IObserver`),並繼承被觀察者(`Observable`)
* 修改如下幾點
* **符合商業邏輯**:添加 `getOwner` 抽象函數來取得每個 DNS 的名稱,之後可用
```kotlin=
abstract fun getOwner() : String
```
* **添加觀察者**:移除 nextServer 成員,改由註冊(添加)觀察者(IObserver)
```kotlin=
fun setNextServer(nextServer: DnsServer2) {
super.list.clear()
// 註冊觀察者(IObserver)
super.list.add(nextServer)
}
```
* **觸發被觀察者**:透過觸發被觀察者,實際上就是通知觀察者(`update` 函數),在這裡就可以做責任鏈的原先判斷
如果無法處理,再往下一層處發(`notify` 函數)
> 一級對一級負責
:::info
並且在這裡我們可以完成 DNS 協議的概念,將請求後的網域修改為自己
:::
```kotlin=
override fun update(data: Recorder2) {
if (isRecord(data.domain)) {
echo(data.domain).apply {
data.domain = domain
data.ip = ip
}
} else {
// 作為被觀察者,通知下一個觀察者
notify(data)
}
// 將請求後的網域修改為自己
data.owner = getOwner()
}
```
:::spoiler 責任鏈抽象類 - 全程式
```kotlin=
abstract class DnsServer2: Observable<Recorder2>(), IObserver<Recorder2> {
fun setNextServer(nextServer: DnsServer2) {
super.list.clear()
// 註冊觀察者(IObserver)
super.list.add(nextServer)
}
abstract fun isRecord(dns: String) : Boolean
fun echo(dns: String) : Recorder2 {
return Recorder2(dns, randomIp(), getOwner())
}
abstract fun getOwner() : String
protected fun randomIp(): String {
Random().apply {
var ip = ""
repeat(4) {
ip += "${nextInt(255)}."
}
return ip.removeRange(ip.length - 1, ip.length)
}
}
override fun update(data: Recorder2) {
if (isRecord(data.domain)) {
echo(data.domain).apply {
data.domain = domain
data.ip = ip
}
} else {
notify(data)
}
data.owner = getOwner()
}
}
```
:::
2. **責任鏈實做類**:很簡單,修改不大
```kotlin=
class CityDnsService2: DnsServer2() {
override fun isRecord(dns: String): Boolean {
return dns.endsWith(".taipei").apply {
println("${this@CityDnsService2::class.simpleName} record? $this")
}
}
override fun getOwner(): String {
return "Taipei 101 DNS"
}
}
class CountryDnsService2: DnsServer2() {
override fun isRecord(dns: String): Boolean {
return dns.endsWith(".tw").apply {
println("${this@CountryDnsService2::class.simpleName} record? $this")
}
}
override fun getOwner(): String {
return "Taiwan DNS"
}
}
class WorldDnsService2: DnsServer2() {
override fun isRecord(dns: String): Boolean {
return true.apply {
println("${this@WorldDnsService2::class.simpleName} record? $this")
}
}
override fun getOwner(): String {
return "World DNS"
}
}
```
3. **請求者類**:修改不大,只將其成員修改為 var (可 `setter`)
```kotlin=
data class Recorder2 constructor(var domain: String, var ip: String, var owner: String) {
override fun toString(): String {
return "Domain: $domain, ip: $ip, owner: $owner"
}
}
```
* **使用者使用**
```kotlin=
fun main() {
val cityDnsService = CityDnsService2()
val countryDnsService = CountryDnsService2()
val worldDnsService = WorldDnsService2()
cityDnsService.setNextServer(countryDnsService)
countryDnsService.setNextServer(worldDnsService)
val record = Recorder2("www.androidJavaKt.org", "", "")
cityDnsService.update(record)
println(record)
}
```
> 
### 觀察者 vs. 責任鏈 - 最佳實踐
* **鏈中的消息對象**
* 傳統責任鏈:**傳統責任鏈對象不會去改變傳遞的物件類型**(可能從頭到尾都是 Person 對象,在傳遞過程中不會修改)
* 觸發行責任鏈:**可以在過程中修改物件類型**,**每個節點只須對鄰居節點負責即可!**
* **上下節點關係**
* 傳統責任鏈:節點之間較無關係,不修改物件類,只須為自身邏輯負責
* 觸發行責任鏈:**節點之間由於修改了物件類型,所以產生較為緊密的(左右)關係,上級對下級的絕對信任**
* **消息的分銷方式不同**
* 傳統責任鏈:從頭到尾一一觸發,基本上每個鏈都會被觸發到(依照需求)
* 觸發行責任鏈:由於使用觀察者模式通知,**通知方式可以多變**,可以使用廣播形式觸發(for 迴圈),**也可以跳躍式觸發**(通知時判斷邏輯)
> 但相對的邏輯複雜度也高
## 更多的物件導向設計
物件導向的設計基礎如下,如果是初學者或是不熟悉的各位,建議可以從這些基礎開始認識,打好基底才能走個更穩(在學習的時候也需要不斷回頭看)!
:::info
* [**設計建模 2 大概念- UML 分類、使用**](https://devtechascendancy.com/introduction-to-uml-and-diagrams/)
* [**物件導向設計原則 – 6 大原則(一)**](https://devtechascendancy.com/object-oriented-design-principles_1/)
* [**物件導向設計原則 – 6 大原則(二)**](https://devtechascendancy.com/object-oriented-design-principles_2/)
:::
### 創建模式 - Creation Patterns
* [**創建模式 PK**](https://devtechascendancy.com/pk-design-patterns-factory-builder-best/)
* **創建模式 - `Creation Patterns`**:
創建模式用於「**物件的創建**」,它關注於如何更靈活、更有效地創建對象。這些模式可以隱藏創建對象的細節,並提供創建對象的機制,例如單例模式、工廠模式… 等等,詳細解說請點擊以下連結
:::success
* [**Singleton 單例模式 | 解說實現 | Android Framework Context Service**](https://devtechascendancy.com/object-oriented_design_singleton/)
* [**Abstract Factory 設計模式 | 實現解說 | Android MediaPlayer**](https://devtechascendancy.com/object-oriented_design_abstract-factory/)
* [**Factory 工廠方法模式 | 解說實現 | Java 集合設計**](https://devtechascendancy.com/object-oriented_design_factory_framework/)
* [**Builder 建構者模式 | 實現與解說 | Android Framwrok Dialog 視窗**](https://devtechascendancy.com/object-oriented_design_builder_dialog/)
* [**Clone 原型模式 | 解說實現 | Android Framework Intent**](https://devtechascendancy.com/object-oriented_design_clone_framework/)
* [**Object Pool 設計模式 | 實現與解說 | 利用 JVM**](https://devtechascendancy.com/object-oriented_design_object-pool/)
* [**Flyweight 享元模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_flyweight/)
:::
### 行為模式 - Behavioral Patterns
* [**行為模式 PK**](https://devtechascendancy.com/pk-design-patterns-cmd-strat-state-obs-chain/)
* **行為模式 - `Behavioral Patterns`**:
行為模式關注物件之間的「**通信**」和「**職責分配**」。它們描述了一系列對象如何協作,以完成特定任務。這些模式專注於改進物件之間的通信,從而提高系統的靈活性。例如,策略模式、觀察者模式… 等等,詳細解說請點擊以下連結
:::warning
* [**Stragety 策略模式 | 解說實現 | Android Framework 動畫**](https://devtechascendancy.com/object-oriented_design_stragety_framework/)
* [**Interpreter 解譯器模式 | 解說實現 | Android Framework PackageManagerService**](https://devtechascendancy.com/object-oriented_design_interpreter_framework/)
* [**Chain 責任鏈模式 | 解說實現 | Android Framework View 事件傳遞**](https://devtechascendancy.com/object-oriented_design_chain_framework/)
* [**State 狀態模式 | 實現解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_state/)
* [**Specification 規格模式 | 解說實現 | Query 語句實做**](https://devtechascendancy.com/object-oriented_design_specification-query/)
* [**Command 命令、Servant 雇工模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_command_servant/)
* [**Memo 備忘錄模式 | 實現與解說 | Android Framwrok Activity 保存**](https://devtechascendancy.com/object-oriented_design_memo_framework/)
* [**Visitor 設計模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_visitor_dispatch/)
* [**Template 設計模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_template/)
* [**Mediator 模式設計 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_programming_mediator/)
* [**Composite 組合模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_programming_composite/)
:::
### 結構模式 - Structural Patterns
* [**結構模式 PK**](https://devtechascendancy.com/pk-design-patterns-proxy-decorate-adapter/)
* **結構模式 - `Structural Patterns`**:
結構模式專注於「物件之間的組成」,以形成更大的結構。這些模式可以幫助你確保當系統進行擴展或修改時,不會破壞其整體結構。例如,外觀模式、代理模式… 等等,詳細解說請點擊以下連結
:::danger
* [**Bridge 橋接模式 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_bridge/)
* [**Decorate 裝飾模式 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_decorate/)
* [**Proxy 代理模式 | 解說實現 | 分析動態代理**](https://devtechascendancy.com/object-oriented_design_proxy_dynamic-proxy/)
* [**Iterator 迭代設計 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_iterator/)
* [**Facade 外觀、門面模式 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_facade/)
* [**Adapter 設計模式 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_adapter/)
:::
## Appendix & FAQ
:::info
:::
###### tags: `Java 設計模式`