--- 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 設計模式` `基礎進階`