--- title: 'Chain - 責任鏈模式' disqus: kyleAlien --- Chain - 責任鏈 模式 === ## Overview of Content 如有引用參考請詳註出處,感謝 :smile: > 這是資料結構 [**Array 單向鏈結**](https://hackmd.io/sAAk-tzzQiipnx1kaSNG0A#單向鏈結串列) 的一種應用,串連多個物件,使其都有機會處理請求 :::success * 如果喜歡讀更好看一點的網頁版本,可以到我新做的網站 [**DevTech Ascendancy Hub**](https://devtechascendancy.com/) 本篇文章對應的是 [**Chain 責任鏈模式 | 解說實現 | Android Framework View 事件傳遞**](https://devtechascendancy.com/object-oriented_design_chain_framework/) ::: [TOC] ## Chain 使用場景 * 多個物件 **^1.^ 有順序的處理、^2.^ 同一請求** * 具體要由哪個物件處理可由 **運行時動態決定** ### Chain 責任鏈 - 分類 * 責任鏈有兩種類型,如下表 | 責任鏈類型 | 說明 | | -------- | -------- | | 純 - 責任鏈 | 需求可被責任鏈消費 | | 不純 - 責任鏈 | 責任鏈不能處理需求 | ### Chain 定義 & Chain UML * **Chain 定義** 多個物件都有機會處理請求,**避免請求者(`request`)、接收處理者(`handle`)耦合**;將對象鏈成一條鏈,並沿著鏈傳遞直到有處理者 :::success * 處理的邏輯重點在 **鏈上** ::: * **Chain UML 有分為兩種**:分別都有兩個行為 請求、處理 1. **合併 請求、處理**:這裡我們主要是關注 Linked 的處理 | 類別 | 功能 | | -------- | -------- | | LinkedItem (鏈、抽象) | **其內部有 `0 ~ 1` 個 LinkedItem,判斷是否讓下一個 LinkedItem 處理**;這裡 **有 2 個選擇,不是 處理,就是 往下傳遞** | | ConcreateA、B (實作) | **處理使用者的請求** (請求、處理一起) | :::info * **LinkedItem 有三個責任** 1. 對外開放一個請求處理入口( `handleRequest` ) 2. 設定鏈的方式 (`setNext`) 3. 具體的請求者的抽象方法 * 抽象定義能處理的級別 * 抽象定義每個子類處理的方式 ::: > ![](https://i.imgur.com/zx7XS4V.png) 2. **分離 請求、處理**:我們在基礎的責任鏈上將 `請求`、`處理` 分開 :::info * 這裡的重點是使用 **依賴倒置** 概念,將 `請求`、`處理` 抽象化 ::: | 類別 | 功能 | | -------- | -------- | | LinkedItem (鏈、抽象) | **其內部有 `0~1` 個 LinkedItem,判斷是否讓下一個 LinkedItem 處理**;這裡 **有 2 個選擇,不是 ^1.^處理,就是 ^2.^往下傳遞** | | ConcreateLinkedA、B(實作) | **處理使用者的請求** | | Request (抽象) | 抽象化共同處理的請求 | | ConcreateRequestA、B(實作) | 定義請求的詳細規則 | > ![](https://i.imgur.com/RfsZQNv.png) ### Chain - 優缺點 * **Chain 設計優點** * 自由的決定要由何處開始處理這個任務 * 符合單一職責:一個類只負責它自身要處理的有範圍的邏輯 * 符合迪米特原則:不用知道別的類如何處理,只須關注自己的部份,其他交給父類處理 * **請求者(Request)與 處理者(Handler)解偶** * 處理者不用知道請求的全貌,只須關注部份(關注自己能不能處理、處理方式) * 請求者也無須知道是哪個類處理的,只須關注結果即可 * **Chain 設計缺點** * 搜尋速度較慢(如 Linked List 的特性) > 可以 **在 `setNext` 時設置一個閥值,避免無意識的破壞調系統性能**(不好查) * 採用遞歸方式運作,也就導致了複雜度增加、可讀性降低 ## Chain 實作 ### Chain 標準 - 不分離請求 :::info * 現在假設在申報出國出差的經費,經費需要經過不同主管批省 1. 批省要有順序 2. 每個主管的額度也不相同 ::: 1. **LinkedItem (抽象)**:**^1.^ 定義相同行為,並且內部有 ^2.^ `0 ~ 1` 個相同的抽象成員 (指向 next 處理對象)** :::danger * 這裡要特別注意,**結束條件**:如果沒有設定結束條件,會導致 Function 堆疊超出 (StackOverflow 錯誤) ::: ```java= // LinkedItem (抽象) public abstract class LinkedItem { private final String handlePersonName; public LinkedItem() { // 簡單定義處理者 this.handlePersonName = getClass().getSimpleName(); } public LinkedItem next; public final boolean start(int money) { boolean canHandle = handle(money); if(!canHandle) { System.out.println(handlePersonName + " cannot pay."); // 將處理傳遞給下一個對象 // 結束條件 next == null return next != null && next.start(money); } else { System.out.println(handlePersonName + " handle pay: " + money); } return true; } // 1. 為相同行為定義抽象 protected abstract boolean handle(int money); } ``` :::info * 從這裡可以看出 **模板設計的影子** (固定調用抽象 `handle` 方法) ::: 2. **ConstractA、B**:定義實體處理的子類,並在這裡 **由各個子類自行判斷是否處理**,在這裡返回的 **結果將會決定 Linked 是否繼續往下執行** ```java= // ConstractA、B public class Supervisor extends LinkedItem { protected static final int MONEY_LIMIT = 1_000; @Override protected boolean handle(int money) { return money <= MONEY_LIMIT; } } // ----------------------------------------------------- public class Manager extends LinkedItem { protected static final int MONEY_LIMIT = 10_000; @Override protected boolean handle(int money) { return money <= MONEY_LIMIT; } } // ----------------------------------------------------- public class CEO extends LinkedItem { protected static final int MONEY_LIMIT = 100_000; @Override protected boolean handle(int money) { return money <= MONEY_LIMIT; } } ``` * **User 使用**:使用者可以自行串接處理的順序,並決定啟動時機 ```java= // User public class Main { public static void main(String[] args) { // 創建實際處理的子類 LinkedItem ceo = new CEO(); LinkedItem manager = new Manager(); LinkedItem supervisor = new Supervisor(); // User 自行串接處理順序 supervisor.next = manager; manager.next = ceo; System.out.println("Linked result: " + supervisor.start(1_000)); System.out.println("--------------------------------\n"); System.out.println("Linked result: " + supervisor.start(10_000)); System.out.println("--------------------------------\n"); System.out.println("Linked result: " + supervisor.start(100_000)); System.out.println("--------------------------------\n"); System.out.println("Linked result: " + supervisor.start(1_000_000)); } } ``` :::info * final 關鍵字: 使用 Java 類加載的特性,定義 **final 變量 `最慢` 必須在建構函數中定義** ::: **--實作--** > ![](https://i.imgur.com/cVfVz9M.png) ### Chain 標準 - 分離請求 :::info * 現在除了上面的需求外還有新增一個需求:需要填入申請者的資料 (申請者名稱、Project 名稱、花費) ::: 1. **Request**:定義新增的 Request 抽象類 ```java= public interface Request { String getRequestName(); String projectName(); int cost(); } ``` 2. ConcreateRequestA、B:定義具體的 Request 處理細節 ```java= public class Alien implements Request { @Override public String getRequestName() { return "Alien"; } @Override public String projectName() { return "Sky"; } @Override public int cost() { return 1_000; } } // ----------------------------------------------- public class Pan implements Request { @Override public String getRequestName() { return "Pan"; } @Override public String projectName() { return "Pandora"; } @Override public int cost() { return 10_000; } } // ----------------------------------------------- public class Shanks implements Request { @Override public String getRequestName() { return "Shanks"; } @Override public String projectName() { return "Comics"; } @Override public int cost() { return 100_000; } } ``` 3. LinkedItem 抽象:也就是處理類的抽象,主要修改接收參數,**讓 `LinkedItem 抽象` 參數依賴抽象 Request** ```java= public abstract class LinkedItem { private final String handlePersonName; public LinkedItem() { this.handlePersonName = getClass().getSimpleName(); } public LinkedItem next; // 主要修改接收參數,讓參數依賴抽象 Request public final boolean start(Request request) { // 獲取具體花費 int cost = request.cost(); boolean canHandle = handle(cost); if(!canHandle) { System.out.println(handlePersonName + " cannot pay."); return next != null && next.start(request); } else { // 獲取 Request name、projectName System.out.println("Person: " + request.getRequestName() + ", project" + request.projectName()); System.out.println(handlePersonName + " handle pay: " + cost); } return true; } protected abstract boolean handle(int money); } ``` 4. ConcreateLinkedA、B:具體實現抽象 Linked 細節,同上個範例,沒有做修改 * User 使用:這裡較不一樣的是使用者必須自訂 Request 需求 ```java= public class Main { public static void main(String[] args) { LinkedItem ceo = new CEO(); LinkedItem manager = new Manager(); LinkedItem supervisor = new Supervisor(); supervisor.next = manager; manager.next = ceo; System.out.println("Linked result: " + supervisor.start(new Alien())); System.out.println("--------------------------------\n"); System.out.println("Linked result: " + supervisor.start(new Pan())); System.out.println("--------------------------------\n"); System.out.println("Linked result: " + supervisor.start(new Shanks())); } } ``` :::success * 這裡可以注意到一點,**使用者(高層模塊)通常會使用類一個封裝過得類來進行請求,而不是自己串接任務鏈** > 可以使用創建類型:像是 `Factory`、`Builder` 設計都可以 ::: **--實作--** > ![](https://i.imgur.com/MJpqqlP.png) ## Android Source Android View 的傳遞事件就是使用 Linked 模式,可以參考另一篇 [**View 事件分發**](https://hackmd.io/SDbpHof_Q6eYvuqhdSDevQ) 透過 **dispatchTouchEvent 方法來迭代** > ![](https://i.imgur.com/YXMByuF.png) ### [ViewGroup](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/view/ViewGroup.java) 事件分發 - dispatchTouchEvent * 這裡我們直接從 ViewGroup 接收的 `dispatchTouchEvent` 方法開始分析 :::success 簡單來說,都是傳遞的事件會從 PhoneWindow 的 DecorView (ViewGroup) 往下傳遞事件,傳遞方式是使用 **遞迴呼叫 (DFS)** ::: 1. 清理當前 View 的事件:如果是 `ACTION_DOWN` 事件,代表它是一個新的事件,需要清理當前 ViewGroup 的一些設定 | 關注變數 | 當前數值 | | -------- | -------- | | actionMasked | MotionEvent.ACTION_DOWN | ```java= // ViewGroup.java @Override public boolean dispatchTouchEvent(MotionEvent ev) { ... boolean handled = false; if (onFilterTouchEventForSecurity(ev)) { final int action = ev.getAction(); final int actionMasked = action & MotionEvent.ACTION_MASK; // Handle an initial down. if (actionMasked == MotionEvent.ACTION_DOWN) { // 在新事件開始時,先處理上一個事件 (透過 Cancel、Clear) cancelAndClearTouchTargets(ev); resetTouchState(); } ... } ... } ``` * **cancelAndClearTouchTargets 方法**:透過 TouchTarget Linked 將 **cancel 事件傳遞給所有 view** ```java= // ViewGroup.java private void cancelAndClearTouchTargets(MotionEvent event) { if (mFirstTouchTarget != null) { boolean syntheticEvent = false; if (event == null) { final long now = SystemClock.uptimeMillis(); event = MotionEvent.obtain(now, now, MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0); event.setSource(InputDevice.SOURCE_TOUCHSCREEN); syntheticEvent = true; } for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) { resetCancelNextUpFlag(target.child); // 第二個參數 true, 代表 cacel 事件 // 傳遞所有 view cancel 事件 dispatchTransformedTouchEvent(event, true, target.child, target.pointerIdBits); } clearTouchTargets(); if (syntheticEvent) { event.recycle(); } } } ``` * **resetTouchState 方法**:清除 `FLAG_DISALLOW_INTERCEPT` FLAG:不同意 ViewGroup 中斷 ```java= // ViewGroup.java private TouchTarget mFirstTouchTarget; private void resetTouchState() { clearTouchTargets(); resetCancelNextUpFlag(this); mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT; mNestedScrollAxes = SCROLL_AXIS_NONE; } // 透過該方法將 mFirstTouchTarget 設置為 null private void clearTouchTargets() { TouchTarget target = mFirstTouchTarget; if (target != null) { do { TouchTarget next = target.next; target.recycle(); target = next; } while (target != null); mFirstTouchTarget = null; } } ``` 2. 檢查該 ViewGroup 中斷事件 | 關注變數 | 當前數值 | | -------- | -------- | | actionMasked | MotionEvent.ACTION_DOWN | | mFirstTouchTarget | null | ```java= // ViewGroup.java @Override public boolean dispatchTouchEvent(MotionEvent ev) { ... boolean handled = false; if (onFilterTouchEventForSecurity(ev)) { ... 清除上一個點擊事件 // 檢查中斷 final boolean intercepted; if (actionMasked == MotionEvent.ACTION_DOWN // 成立 || mFirstTouchTarget != null) { // 不成立 // 判斷 viewgroup flag 是否不允許中斷 final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; // 沒有禁止中斷 if (!disallowIntercept) { // 呼叫 onInterceptTouchEvent 方法 intercepted = onInterceptTouchEvent(ev); ev.setAction(action); // restore action in case it was changed } else { intercepted = false; } } else { // 並非下壓事件 & 有 childView 已經在處理事件 intercepted = true; } ... } } ``` 3. 檢查事件是否被取消 ```java= // ViewGroup.java @Override public boolean dispatchTouchEvent(MotionEvent ev) { ... boolean handled = false; if (onFilterTouchEventForSecurity(ev)) { ... // 檢查事件是否被取消 final boolean canceled = resetCancelNextUpFlag(this) || actionMasked == MotionEvent.ACTION_CANCEL; // Update list of touch targets for pointer down, if needed. // 如果有需要的話需要對所有 down 事件檢查 final boolean isMouseEvent = ev.getSource() == InputDevice.SOURCE_MOUSE; final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0 && !isMouseEvent; ``` 4. ViewGroup 處理、分發事件(事件沒有被取消或中斷):在這一步會透過遞迴呼叫,嘗試找到處理事件的 View :::info 這裡的遞迴操作,就是遍歷二元樹的操作 ::: | 關注變數 | 當前數值 | | -------- | -------- | | actionMasked | MotionEvent.ACTION_DOWN | | mFirstTouchTarget | null | | canceled | false | | intercepted | false | ```java= // ViewGroup.java // ViewGroup 的 ChildView 數量 private int mChildrenCount; // ChildView 聚集 private View[] mChildren; @Override public boolean dispatchTouchEvent(MotionEvent ev) { ... boolean handled = false; if (onFilterTouchEventForSecurity(ev)) { ... if (!canceled && !intercepted) { // 成立 // 檢查事件是否有需要分配到其他 View 上 View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus() ? findChildWithAccessibilityFocus() : null; if (actionMasked == MotionEvent.ACTION_DOWN // 成立 || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN) || actionMasked == MotionEvent.ACTION_HOVER_MOVE) { // 多指觸控 // 如果是 ACTION_DOWN 的話就是 0 final int actionIndex = ev.getActionIndex(); // always 0 for down final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex) : TouchTarget.ALL_POINTER_IDS; // Clean up earlier touch targets for this pointer id in case they // have become out of sync. removePointersFromTouchTargets(idBitsToAssign); // 取得目前 ChildeView 的數量 final int childrenCount = mChildrenCount; if (newTouchTarget == null && childrenCount != 0) {// 成立 final float x = isMouseEvent ? ev.getXCursorPosition() : ev.getX(actionIndex); final float y = isMouseEvent ? ev.getYCursorPosition() : ev.getY(actionIndex); // 由上到下找尋可以接收該事件的 ChildView final ArrayList<View> preorderedList = buildTouchDispatchChildList(); final boolean customOrder = preorderedList == null && isChildrenDrawingOrderEnabled(); final View[] children = mChildren; // 跌代所有 ChildView for (int i = childrenCount - 1; i >= 0; i--) { final int childIndex = getAndVerifyPreorderedIndex( childrenCount, i, customOrder); final View child = getAndVerifyPreorderedView( preorderedList, children, childIndex); ... // canReceivePointerEvents,ChildView 無法收到該事件 // isTransformedTouchPointInView,ChildView 無法接都到該事件 if (!child.canReceivePointerEvents() || !isTransformedTouchPointInView(x, y, child, null)) { ev.setTargetAccessibilityFocus(false); continue; } // 取得 處理事件的 View newTouchTarget = getTouchTarget(child); if (newTouchTarget != null) { // Child is already receiving touch within its bounds. // Give it the new pointer in addition to the ones it is handling. newTouchTarget.pointerIdBits |= idBitsToAssign; break; } resetCancelNextUpFlag(child); // 往下分發事件,這邊如果 ChildView // 1. ViewGroup: 遞迴呼叫 dispatchTransformedTouchEvent 方法 // 2. View: 調用 View#dispatchTouchEvent,最終由 onTouchEvent 處理 if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) { // Child wants to receive touch within its bounds. mLastTouchDownTime = ev.getDownTime(); if (preorderedList != null) { // childIndex points into presorted list, find original index for (int j = 0; j < childrenCount; j++) { if (children[childIndex] == mChildren[j]) { mLastTouchDownIndex = j; break; } } } else { mLastTouchDownIndex = childIndex; } mLastTouchDownX = ev.getX(); mLastTouchDownY = ev.getY(); // 處理完事件後,賦予 newTouchTarget // @ 查看 addTouchTarget 方法 newTouchTarget = addTouchTarget(child, idBitsToAssign); // 標註以分發事件完成 alreadyDispatchedToNewTouchTarget = true; break; } // The accessibility focus didn't handle the event, so clear // the flag and do a normal dispatch to all children. ev.setTargetAccessibilityFocus(false); } // end for if (preorderedList != null) preorderedList.clear(); } // end if // 跌代完所有的 ChildView,但沒有 View 處理該事件 if (newTouchTarget == null && mFirstTouchTarget != null) { // 指定 pointer 給最後添加的 View newTouchTarget = mFirstTouchTarget; while (newTouchTarget.next != null) { newTouchTarget = newTouchTarget.next; } newTouchTarget.pointerIdBits |= idBitsToAssign; } } } } } // 賦予 mFirstTouchTarget private TouchTarget addTouchTarget(@NonNull View child, int pointerIdBits) { final TouchTarget target = TouchTarget.obtain(child, pointerIdBits); target.next = mFirstTouchTarget; mFirstTouchTarget = target; return target; } ``` 5. 判斷事件是否已經分發到目標 ChildView,^1.^ 如果沒有 ChildView 處理,則拋回到 ViewGroup 的 View、^2.^ 處理 cancel 事件 | 關注變數 | 當前數值 | | -------- | -------- | | actionMasked | MotionEvent.ACTION_DOWN | | mFirstTouchTarget | **有可能為 null (如果是 ChildView 處理就不是 null)** | | canceled | false | | intercepted | false | ```java= // ViewGroup.java // ViewGroup 的 ChildView 數量 private int mChildrenCount; // ChildView 聚集 private View[] mChildren; @Override public boolean dispatchTouchEvent(MotionEvent ev) { ... boolean handled = false; if (onFilterTouchEventForSecurity(ev)) { ... if (!canceled && !intercepted) { // 成立 ... 結束事件分發 } // 目前沒有 View 處理事件 if (mFirstTouchTarget == null) { // 視為普通視圖 (最終會傳遞到當前 ViewGroup 的 View) handled = dispatchTransformedTouchEvent(ev, canceled, null, TouchTarget.ALL_POINTER_IDS); } else { // 分發到點擊的 View,如果已經分發,有需要的排除其他點擊目標 TouchTarget predecessor = null; TouchTarget target = mFirstTouchTarget; while (target != null) { final TouchTarget next = target.next; // 判斷是否已經分發事件 if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) { // 已分發 handled = true; } else { // 是否取消事件 final boolean cancelChild = resetCancelNextUpFlag(target.child) || intercepted; // 分發事件 if (dispatchTransformedTouchEvent(ev, cancelChild, target.child, target.pointerIdBits)) { handled = true; } // 如果是 Cacncel 事件則轉移 mFirstTouchTarget if (cancelChild) { if (predecessor == null) { mFirstTouchTarget = next; } else { predecessor.next = next; } target.recycle(); target = next; continue; } } predecessor = target; target = next; } } ... } ... return handled; } ``` ### [ViewGroup](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/view/ViewGroup.java) 事件傳遞 - dispatchTransformedTouchEvent * 上面介紹了 ViewGroup 的事件分法,現在介紹 ViewGroup 的事件傳遞 (+Transformed) 1. 處理 View 的取消事件:如果有就透過 `dispatchTouchEvent` 方法分發給目標 View ```java= private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel, View child, int desiredPointerIdBits) { final boolean handled; final int oldAction = event.getAction(); // 判斷事件是否取消 if (cancel || oldAction == MotionEvent.ACTION_CANCEL) { event.setAction(MotionEvent.ACTION_CANCEL); // 沒有目標 View if (child == null) { // 傳給自身的 ViewGroup 的 View handled = super.dispatchTouchEvent(event); } else { handled = child.dispatchTouchEvent(event); } event.setAction(oldAction); return handled; } ... return handle; } ``` 2. **計算將被傳遞的 pointer 數量** ```java= private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel, View child, int desiredPointerIdBits) { final boolean handled; ... // 計算將被傳遞的 pointer 數量 final int oldPointerIdBits = event.getPointerIdBits(); final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits; // 如果沒有點,則直接返回 if (newPointerIdBits == 0) { return false; } ... } ``` 3. **觸摸數量相同**:計算偏移量,再透過 `dispatchTouchEvent` 方法分發事件給 ChildView :::success * 偏移量 如果有調用 `scrollTo` or `scrollBy` 對 ChildView 進行滾動,就會產生 xy 的偏移量 ::: ```java= private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel, View child, int desiredPointerIdBits) { final boolean handled; ... // 保存座標轉換後的 MotionEvent final MotionEvent transformedEvent; // 點擊數量一致 if (newPointerIdBits == oldPointerIdBits) { if (child == null || child.hasIdentityMatrix()) { if (child == null) { // 呼叫 ViewGroup 的 View handled = super.dispatchTouchEvent(event); } else { // 計算 x,y 偏移量 final float offsetX = mScrollX - child.mLeft; final float offsetY = mScrollY - child.mTop; // 設定轉換後的 offset 目標 event.offsetLocation(offsetX, offsetY); // 分發事件 到 ChildView handled = child.dispatchTouchEvent(event); // 復位 event.offsetLocation(-offsetX, -offsetY); } return handled; } transformedEvent = MotionEvent.obtain(event); } else { transformedEvent = event.split(newPointerIdBits); } ... } ```` 5. **觸摸數量不相同**:同上,最終都是透過 `dispatchTouchEvent` 方法分發事件 ```java= private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel, View child, int desiredPointerIdBits) { final boolean handled; ... // Perform any necessary transformations and dispatch. if (child == null) { handled = super.dispatchTouchEvent(transformedEvent); } else { // 計算偏移量 final float offsetX = mScrollX - child.mLeft; final float offsetY = mScrollY - child.mTop; transformedEvent.offsetLocation(offsetX, offsetY); if (! child.hasIdentityMatrix()) { transformedEvent.transform(child.getInverseMatrix()); } // 分發事件 handled = child.dispatchTouchEvent(transformedEvent); } ... return handled; } ``` ## 更多的物件導向設計 物件導向的設計基礎如下,如果是初學者或是不熟悉的各位,建議可以從這些基礎開始認識,打好基底才能走個更穩(在學習的時候也需要不斷回頭看)! :::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 設計模式` `基礎進階`