---
# System prepended metadata

title: Visitor 訪問者模式
tags: [基礎進階, Java 設計模式]

---

---
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 設計的模型不符合「依賴倒置」原則，同時也是在提醒使用這個設計的人，**需求要足夠穩定再使用**
    :::

    > ![](https://i.imgur.com/XKbZ0MB.png)

### 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())
        }
    }
    ```
    > ![](https://i.imgur.com/18b5ld8.png)


### 拓展 Visitor：抽象 Mutli Visitor 實作

* 多個訪問者的情況也是非常常見的，我們可以透過「繼承」 `Visitor` 界面，來達到拓展 `Visitor` 界面；如下圖 UML，每個訪問者可以再拓展不同界面

    > ![](https://i.imgur.com/wi8oEcy.png)

* 以下已會修改到的類來展示；現在我們在 **根據功能** (總金額、購物明細) 來拓展兩個 `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()})")
                }
            }
        }

        ```


        > ![](https://i.imgur.com/x4IrYZo.png)

## 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` 是單分派語言
        > 
        > ![](https://i.imgur.com/LLqoeW3.png)

### 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)
        }
        ```
        > ![](https://i.imgur.com/fZSbxxo.png)
        
        :::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 設計模式` `基礎進階`