---
title: 'Visitor 訪問者模式'
disqus: kyleAlien
---
Visitor 訪問者模式
===
## Overview of Content
如有引用參考請詳註出處,感謝 :smile:
**將資料結構 & 資料處理分離**,是設計模式中最複雜的一個
:::success
* 如果喜歡讀更好看一點的網頁版本,可以到我新做的網站 [**DevTech Ascendancy Hub**](https://devtechascendancy.com/)
本篇文章對應的是 [**Visitor 設計模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_visitor_dispatch//)
:::
[TOC]
## Visitor 使用場景
* **物件結構穩定**(若結構不穩定,這個設計模式的結構就會常常被更動,甚至影響到已經實作的子類),但**需要常在該物件上定義新的操作**
* 假設一物件由多個物件聚合而成,這些物件都有一個類用來接收訪問者的存取,而訪問者是一個界面,對於實做這個界面,**存取到物件結構中不同類型的元素做出不同的處理**
> 解開接收者、訪問者的耦合性
* 對一物件結構中物件進行 `不同` 並 `不相關` 的操作,**避免該物件被汙染**,也不希望新增操作時修改到該類別
:::info
如果需求是:需要不改變封裝,並對於數據有不同操作模式後,就可以使用 `Visitor` 模式
:::
:::success
這個模式也非常適合用於 **大規模的重構項目**(因為需求已經很清晰、變動性不大),但 **如果項目的需求不夠穩定、清晰,那建議先不要使用這個模式**
:::
### Visitor 定義 & Visitor UML
* **Visitor 定義**:封裝一些作用於某個數據結構中的元素,它可以再 **不改變數據結構的前提之下,重新定義訪問這個結構的行為**
也就說說 Visitor 設計的目的在於:**解耦數據結構、數據訪問(或是操作)的兩個部份**
```mermaid
graph LR
數據 --> visitor
visitor -.-> 結構
visitor -.-> 訪問
```
* **Visitor 角色介紹**
| 角色 | 功能 |
| - | - |
| `Visitor` 界面 | **定義須訪問的元素(定義哪些實體元素可被訪問);實體元素 ConcreteElement** |
| `ConcreteVisitor` | 實做操作元素 |
| `Element` 抽象 | 抽象元素,定義抽象訪法讓子類實現 Visitor 界面的訪問 |
| `ConcreteElement` | 具體元素,將自身傳入 Visitor 訪問的方法 |
| `ObjectStructure` | 容納多個不同的類,可以返回不同的結構讓使用者使用(Option) |
> 很少抽象化 `ObjectStructure` 角色,它算是具體的邏輯類,組裝多個不同類
:::info
* **`Visitor` 為啥要依賴具體 `ConcreteElement`** ?
其實也是可以不用,但是如果不依賴具體 `ConcreteElement` 在 `ConcreteVisitor` 實做時就需要用 `instanceof` 來判斷
**但如果使用了 instanceof 來判斷據理實做類,那就無法善用語言「分派」的特性**!(分派寫在下面的小節)
> 從這裡也就可以看出 Visitor 設計的模型不符合「依賴倒置」原則,同時也是在提醒使用這個設計的人,**需求要足夠穩定再使用**
:::
> 
### Visitor 優缺點
* **Visitor 設計的優點** :
1. **單一責任**:個角色職責分離,符合單一責任原則
2. 對於 Visitor 的方法 **擴充相當自由、靈活性高**
3. 將操作與資料解偶
* **Visitor 設計的缺點** :
1. 當具體元素改變實 (Element),需要修改的成本太高
3. 違反了依賴倒制原則,為了分別處理依賴了具體類別
## Visitor 實現
以下實現一個:男孩、女孩看到同一個商品時,關注的點不同(不同的反應也就是對於相同商品不同的 `Visitor`)
### Visitor 標準實現
1. **`Element` 抽象**:重點是有一個方法其變數依賴於 `Visitor`
```kotlin=
abstract class MyTools constructor(val name: String, val price: Int) {
// 依賴 Visitor
abstract fun accept(visitor: Visitor)
}
```
2. **`ConcreteElement` 類**:實做類,也就是一個不修改的數據結構
```kotlin=
class Robot constructor(name: String, price: Int) : MyTools(name, price) {
fun doWork(): String {
return "Working, learning, Server"
}
override fun accept(visitor: Visitor) {
// 傳入自身
visitor.visitor(this)
}
}
class Cosmetic constructor(name: String, price: Int) : MyTools(name, price) {
fun doWork(): String {
return "Make face beauty"
}
override fun accept(visitor: Visitor) {
// 傳入自身
visitor.visitor(this)
}
}
```
3. **`Visitor` 界面**:**定義須訪問的元素**
:::warning
* 從 `Visitor` 的界面設計,我們可以發現,它不符合依賴倒置,這樣做好嗎?
**Visitor 界面依賴實做(`ConcreteElement`)的類,也就是以下的 `Robot`, `Cosmetic` 類**
但是!**這也是 Visitor 設計在利用「單分派」的特性**!
:::
```kotlin=
interface Visitor {
// 定義須訪問的元素
fun visitor(robot: Robot)
// 定義須訪問的元素
fun visitor(cosmetic: Cosmetic)
}
```
4. **`ConcreteVisitor` 類**:**透過它來達到 ++同種結構不同的訪問方式++**(男女對於不同產品的看法)
```kotlin=
// 關注點不同
class BoyVisitor : Visitor {
override fun visitor(robot: Robot) {
// Boy 關注 doWork
println("I am Boy, focus Robot ${robot.name}, ${robot.doWork()}")
}
override fun visitor(cosmetic: Cosmetic) {
// Boy 關注 price
println("I am Boy, focus Cosmetic ${cosmetic.name}, ${cosmetic.price}")
}
}
// 關注點不同
class GirlVisitor : Visitor {
override fun visitor(robot: Robot)
// Girl 關注 price
println("I am Girl, focus Robot ${robot.name}, ${robot.price}")
}
override fun visitor(cosmetic: Cosmetic) {
// Girl 關注 doWork
println("I am Girl, focus Cosmetic ${cosmetic.name}, ${cosmetic.doWork()}")
}
}
```
5. **`ObjectStructure` 類**:該類的目的是用來收集、遍歷所有的可被訪問者(資源),並且它也不需要知道訪問者是如何操作資料,就可以體現出「相同的資料,不同的實做」的特點
```kotlin=
class ManagerIterator {
private val ls = ArrayList<MyTools>()
init {
ls.add(Robot("多拉A夢", 300))
ls.add(Robot("多拉美", 299))
ls.add(Robot("佛朗基", 699))
ls.add(Cosmetic("SKII", 333))
ls.add(Cosmetic("Dr.Wu", 123))
}
// 遍歷所有的可訪問類
fun accept(v: Visitor) {
for (t in ls) {
// 抽象方法,實作類不同會有不同結果 (這就是我們要的結果)
t.accept(v)
}
}
}
```
* **測試 訪問者 實作**:由使用者來決定要使用哪個「訪問者」,從結果我們可以看到,不同訪問者訪問相同資源會有不同的反應
> **從這裡我們可以看到,訪問者模式允許使用者透過不同的物件對元素(資料)做不同角度、方向的訪問**
```kotlin=
fun main() {
ManagerIterator().run {
accept(BoyVisitor())
println("----------------------")
accept(GirlVisitor())
}
}
```
> 
### 拓展 Visitor:抽象 Mutli Visitor 實作
* 多個訪問者的情況也是非常常見的,我們可以透過「繼承」 `Visitor` 界面,來達到拓展 `Visitor` 界面;如下圖 UML,每個訪問者可以再拓展不同界面
> 
* 以下已會修改到的類來展示;現在我們在 **根據功能** (總金額、購物明細) 來拓展兩個 `Visitor`
1. **`Visitor` 拓展**:根據功能繼承 Visitor 並拓展其功能
* **繼承 `Visitor` 界面,拓展總金額**
```kotlin=
interface ICostVisitor : Visitor {
fun getTotalCost() : Int
}
```
* **繼承 `Visitor` 界面,拓展購物明細**
```kotlin=
interface IShoppingDetailVisitor : Visitor {
fun showShoppingDetail()
}
```
2. **`ConcreteVisitor` 類**:由於上面我們拓展 Visitor 抽象,所以在這裡也需要相對應的實做,實做範例如下…
* 實做總金額 Visitor
```kotlin=
class CostVisitorImpl : ICostVisitor {
private var totalCost : Int = 0
override fun getTotalCost(): Int {
return totalCost
}
override fun visitor(robot: Robot) {
totalCost += robot.price
}
override fun visitor(cosmetic: Cosmetic) {
totalCost += cosmetic.price
}
}
```
* 實做購物明細 Visitor
```kotlin=
class ShoppingCarVisitorImpl : IShoppingDetailVisitor {
private val str : StringBuffer = StringBuffer()
override fun showShoppingDetail() {
println("$str")
}
override fun visitor(robot: Robot) {
str.append("${robot.name} : ${robot.price}").append("\n")
}
override fun visitor(cosmetic: Cosmetic) {
str.append("${cosmetic.name} : ${cosmetic.price}").append("\n")
}
}
```
:::success
* 修改到這邊就結束了,可以發現要拓展 Visitor 類是相當簡單的,**並不需要修改之前已經實做好的類**
:::
:::warning
* **但這似乎不滿足「開閉原則」**?
不!這是符合開閉原則的,因為每多一個 Visitor 拓展類只需「新增」而不需要修改舊有的類型
:::
* 使用拓展 Visitor 類的功能
```kotlin=
fun main() {
ManagerIterator().run {
// 使用我們新拓展的 ShoppingCarVisitorImpl 訪問者,來訪問資源
ShoppingCarVisitorImpl().run {
accept(this) // 這裡傳入的 this 是 ShoppingCarVisitorImpl
showShoppingDetail()
}
println("--------------------------------------")
// 使用我們新拓展的 CostVisitorImpl 訪問者,來訪問資源
CostVisitorImpl().run {
accept(this) // 這裡傳入的 this 是 CostVisitorImpl
println("total=(${getTotalCost()})")
}
}
}
```
> 
## Single, Double dispatch 單、雙分派
* 程式語言中,有分為「**單分派**, `Single dispatch`」、「**雙分派`Double dispatch`**」,這會與多型的概念有交互
:::info
* **單、雙分派定義**:
* **雙分派**:真正的執行操作決定於 **請求者的種類、接收者的類型**(由兩者共同決定);這種情況下,多態性是基於多個參數的類型
> 多出現在動態語言中
* **單分派**:反知,單分派的執行則不是由這兩者決定;**它指根據一個操作的一個參數的類型來選擇方法的過程**;這種情況下,多態性是基於單一的參數類型
:::
### 單、雙分派的判斷:Kotin 語言
* 我們可以從程式語言的實作中,測試出這個程式語言是支持「單分派」還是「雙分派」;像是 `Java`、`Kotlin` 就是支持單分派的程式語… 接下來我們 **透過測試 Kotlin 語言來判斷 Kotlin 到底是屬於單分派還是雙分派**
1. 宣告抽象界面(`interface`)、實現類(`class`),等等測試會用「**宣告界面**」、「**使用實體類**」比較
```kotlin=
interface People
class RichPeople : People
class PoorPeople : People
```
2. 抽象類:這裡使用 `Overload` 重載 `buy` 函數,等等用來判斷單、雙分派的結果
```kotlin=
abstract class Shop {
fun buy(people: People) {
println("People buy something")
}
// 重載(overload)函數,並開放子類重載(Override)
open fun buy(people: RichPeople) {
println("RichPeople buy something")
}
}
```
3. 繼承抽象 `Shop` 類,並 `Override` 覆寫 `buy` 函數,看看等等是否會被呼叫到
```kotlin=
class BookShop: Shop() {
// 覆寫函數
override fun buy(people: RichPeople) {
// 書出不同的資訊
println("RichPeople buy book")
}
}
```
* 測試 `Kotlin` 語言:透過宣告抽象(`interface`)、類型(`Class`)看看是否會影響到最終會抵達的 `buy` 函數… 請注意以下註釋
```kotlin=
fun main() {
// 宣告類型為 Shop,接收者類行為 Shop
val shop : Shop = BookShop()
// 動態綁定
// 請求者類行為 RichPeople
// 這個是 buy 是呼叫到哪個方法呢?
// 它呼叫到的是 BookShop#buy(RichPeople) 方法
shop.buy(RichPeople())
// 請求者類行為 People
// 這個是 buy 是呼叫到哪個方法呢?
// 它會呼叫到抽象 Shop#buy(People) 方法
val people : People = PoorPeople()
shop.buy(people)
}
```
> 從以下結果我們可以看到 `Kotlin` 這門語言是呼叫方法時的決定權是「不是由宣告類型,而是 **由實作類型決定**!」
>
> 我們可以看到 `Kotlin` 單以由請求者類型決定函數的去向,所以 `Kotlin` 是單分派語言
>
> 
### Visitor 拓展單分派
* 從上面的實現我們可以知道 `Java`、`Koltin` 語言是 **單分派**(`Single dispatch`)的功能,在這邊我們再次加強單分派的功能特性,將 `Visitor` 改造,讓它「體現單分派功能」
1. **`Visitor` 界面、`ConcreteVisitor` 實做**:這裡是修改原先 `Visitor` 設計的關鍵,透過 `overload`、`override` 來補足設計的不足之處
```kotlin=
// Visitor 讓其支持單分派特性,由外部請求者類型決定實作者
abstract class Shop2 {
fun buy(people: People2) {
println("People buy something")
}
// overload
open fun buy(people: RichPeople2) {
println("RichPeople buy something")
}
}
// ConcreteVisitor
class BookShop2: Shop2() {
// override
override fun buy(people: RichPeople2) {
println("RichPeople buy book")
}
}
```
2. **`Element`、`ConcreteElement`:這部分與原先設計相同,讓 `Element` 依賴於 `Visitor` 抽象類**(也就是讓以下範例的 `People2` 依賴於 `Shop2`)
```kotlin=
// Element 界面
interface People2 {
// 改為依賴抽象
fun accept(shop: Shop2)
}
// ConcreteElement 實做
class RichPeople2 : People2 {
override fun accept(shop: Shop2) {
shop.buy(this)
}
}
// ConcreteElement 實做
class PoorPeople2 : People2 {
override fun accept(shop: Shop2) {
shop.buy(this)
}
}
```
* **測試修改後的 Visitor 設計**:
從結果我們可以看到,**透過修改後 `Visitor` 設計,可以讓單分派語言體現更多個可能性** 😌(顯示出語言的細節,讓單分派由外部決定執行的真正類別)
```kotlin=
fun main() {
// 接收者類型定義為 Shop2(接收者 Visitor)
val shop : Shop2 = BookShop2()
// 動態判定:傳入類型為 RichPeople2(請求類 Element)
shop.buy(RichPeople2())
// 靜態判定:傳入類行為 People2(請求類 Element)
val people : People2 = RichPeople2()
shop.buy(people)
}
```
> 
:::warning
* 透過 `Visitor` 設計 可以讓單分派語言擁有雙分派語言的特性,但是它只是 **類似雙分派**,並非真正的雙分派語言的判斷
:::
## Java APT
Visitor 模式的具體實現也應用在 Java APT(`Annotaion Processing Tools`) 框架之上,而 APT 框架的實現:有名的像是 `ButterKnife`、`Dagger`、`Retrofit`... 等等開源庫
:::success
* 這裡為 Java APT 技術做個簡單的介紹:
APT 技術其實是基於 Meta programming 的角度來開發,而這個 Meta programming 是 **運作在編譯期間**,透過編譯期間分析源碼中的註釋(`Annotaion`)來達到新類別的產生或是其他的功能!
:::
### [Element](https://github.com/fiji/javac/blob/master/src/main/java/javax/lang/model/element/Element.java) 數據、[ElementVisitor](https://github.com/fiji/javac/blob/master/src/main/java/javax/lang/model/element/ElementVisitor.java) 訪問
* 我們先從 [**Element**](https://github.com/fiji/javac/blob/master/src/main/java/javax/lang/model/element/Element.java) 界面入手,該界面功能是的含意是「**表示語言級的程式元素**」,包括套件 `package`、類別 `class`、方法 `method`... 等等
以下我們來看看這個 Element 界面中的幾個常見方法
```java=
// Element.java
public interface Element {
... 省略部份
// 取得註解類
<A extends Annotation> A getAnnotation(Class<A> annotationType);
// 取得修飾符,像是 public, static, final... 等等
Set<Modifier> getModifiers();
// 把透過訪問者,來訪問數據結構
<R, P> R accept(ElementVisitor<R, P> v, P p);
}
```
而在這裡,我們主要關注的是 `accept` 方法,這個方法是 Visitor 設計的入口:我們把它做個拆分理解,如下表…
| Visitor 設計角色 | APT 中代表的類 |
| - | - |
| 數據的結構 | Element 界面 |
| 數據的訪問 | ElementVisitor 界面 |
從表中,我們可以分清晰的看到,APT 的設計就是明確的使用 Visitor 設計,它將源碼的結構透過 `Element` 界面來表達,而訪問源碼的方式使用 `ElementVisitor` 界面,清晰的分離了數據、以及對數據的操作!
* 接著,我們來看 [**ElementVisitor**](https://github.com/fiji/javac/blob/master/src/main/java/javax/lang/model/element/ElementVisitor.java) 界面,這個界面的主要功能是「**編譯期操作源碼**」,它的方法如下,從這些方法中我們可以發現幾件事
1. **已有的訪問者**:APT 在設計時,有預計的幾個訪問方案,像是對於 Package 可使用是 `PackageElement`、對於類型可以使用 `TypeElement`... 等等類
2. **兼容變化**:`ElementVisitor`#`visitUnknown` 的方法設計,是為了界面的穩定性,為未來拓展的 MetaData 做兼容變化
:::warning
* 透過這個方法來達到遵守「開閉原則」的需求,每當有心的 MetaData 時,也不用去修改這個界面上的功能
:::
```java=
// ElementVisitor.java
/**
* R: 此訪客方法的傳回 (return) 類型
* P: 此訪客的附加參數 (Params) 的類型
*/
public interface ElementVisitor<R, P> {
/**
* Visits an element.
*/
R visit(Element e, P p);
/**
* A convenience method equivalent to {@code v.visit(e, null)}.
*/
R visit(Element e);
/**
* Visits a package element.
*/
R visitPackage(PackageElement e, P p);
/**
* Visits a type element.
*/
R visitType(TypeElement e, P p);
/**
* Visits a variable element.
*/
R visitVariable(VariableElement e, P p);
/**
* Visits an executable element.
*/
R visitExecutable(ExecutableElement e, P p);
/**
* Visits a type parameter element.
*/
R visitTypeParameter(TypeParameterElement e, P p);
/**
* Visits an unknown kind of element.
* This can occur if the language evolves and new kinds
* of elements are added to the {@code Element} hierarchy.
*/
R visitUnknown(Element e, P p);
}
```
### APT ElementVisitor 的拓展範例
* APT source code 中有提供幾個拓展範例,這邊我們就來看看它如何去拓展 ElementVisitor 元素(新增訪問者)
源碼中有一個 [**SimpleElementVisitor6**](https://github.com/fiji/javac/blob/master/src/main/java/javax/lang/model/util/SimpleElementVisitor6.java) 類,它繼承於 AbstractElementVisitor6,而 AbstractElementVisitor6 又實做了 ElementVisitor 界面
也就是說 SimpleElementVisitor6 是一個新的訪問程式 MetaData 的類別,它的部份程式如下:我們可以看出這個 Visitor 其實沒有做甚麽事情,單純返回一個預設值 `DEFAULT_VALUE`
```java=
// SimpleElementVisitor6.java
@SupportedSourceVersion(RELEASE_6)
public class SimpleElementVisitor6<R, P> extends AbstractElementVisitor6<R, P> {
protected final R DEFAULT_VALUE;
... 省略部份
protected R defaultAction(Element e, P p) {
return DEFAULT_VALUE;
}
/**
* {@inheritDoc} This implementation calls {@code defaultAction}.
*
* @param e {@inheritDoc}
* @param p {@inheritDoc}
* @return the result of {@code defaultAction}
*/
public R visitPackage(PackageElement e, P p) {
return defaultAction(e, p);
}
/**
* {@inheritDoc} This implementation calls {@code defaultAction}.
*
* @param e {@inheritDoc}
* @param p {@inheritDoc}
* @return the result of {@code defaultAction}
*/
public R visitType(TypeElement e, P p) {
return defaultAction(e, p);
}
/**
* {@inheritDoc} This implementation calls {@code defaultAction}.
*
* @param e {@inheritDoc}
* @param p {@inheritDoc}
* @return the result of {@code defaultAction}
*/
public R visitVariable(VariableElement e, P p) {
return defaultAction(e, p);
}
/**
* {@inheritDoc} This implementation calls {@code defaultAction}.
*
* @param e {@inheritDoc}
* @param p {@inheritDoc}
* @return the result of {@code defaultAction}
*/
public R visitExecutable(ExecutableElement e, P p) {
return defaultAction(e, p);
}
/**
* {@inheritDoc} This implementation calls {@code defaultAction}.
*
* @param e {@inheritDoc}
* @param p {@inheritDoc}
* @return the result of {@code defaultAction}
*/
public R visitTypeParameter(TypeParameterElement e, P p) {
return defaultAction(e, p);
}
}
```
* 接著,我們來看 [**ElementKindVisitor6**](https://github.com/fiji/javac/blob/master/src/main/java/javax/lang/model/util/ElementKindVisitor6.java) 類別,它繼承於 SimpleElementVisitor6 類;在這個類中,它透過 `TypeElement`#`getKind()` 方法取得 MetaData,並拓展了類型(`Type`)的訪問
```java=
// ElementKindVisitor6.java
@SupportedSourceVersion(RELEASE_6)
public class ElementKindVisitor6<R, P>
extends SimpleElementVisitor6<R, P> {
... 省略部份
@Override
public R visitType(TypeElement e, P p) {
ElementKind k = e.getKind();
switch(k) {
case ANNOTATION_TYPE:
return visitTypeAsAnnotationType(e, p);
case CLASS:
return visitTypeAsClass(e, p);
case ENUM:
return visitTypeAsEnum(e, p);
case INTERFACE:
return visitTypeAsInterface(e, p);
default:
throw new AssertionError("Bad kind " + k + " for TypeElement" + e);
}
}
public R visitTypeAsAnnotationType(TypeElement e, P p) {
return defaultAction(e, p);
}
... 省略部份
}
```
## 更多的物件導向設計
物件導向的設計基礎如下,如果是初學者或是不熟悉的各位,建議可以從這些基礎開始認識,打好基底才能走個更穩(在學習的時候也需要不斷回頭看)!
:::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 設計模式` `基礎進階`