Overview of Content
如有引用參考請詳註出處,感謝
在這份文章中,我們將深入探討 Chain
(責任鏈模式),一種在軟體開發中常被使用的設計模式。本文將從使用場景著手,探討責任鏈在不同情境下的應用。我們將分類責任鏈的不同形式,並透過相應的 UML 圖表進行清晰的定義。此外,我們將深入探討責任鏈模式的優缺點,以幫助讀者更好地理解其適用性和局限性。
在「Chain 實作」一節中,我們將探討責任鏈的實際實作。我們將首先介紹不分離請求的標準模式,其中各個元件共同處理請求。接著,我們將討論分離請求的標準模式,其中每個元件專注處理特定類型的請求,形成一個有序的處理鏈。
最後,我們將深入研究 Android Framework 中的相關實例。透過探討 ViewGroup 事件分發和事件傳遞的方法,我們將更具體地理解 Android 框架中如何應用責任鏈模式來有效處理事件流。
這份文章將為讀者提供深入的 Chain(責任鏈模式)相關知識,不僅理論豐富,還包含實際的實作例子,以幫助讀者更好地應用這一強大的設計模式。
Chain 使用場景
這是資料結構 Array 單向鏈結 的一種應用,串連多個物件,使其都有機會處理請求
● 多個物件 1. 有順序的處理、2. 同一請求
● 具體要由哪個物件處理可由 運行時動態決定
Chain 責任鏈分類
● 責任鏈有兩種類型,如下表
責任鏈類型 | 說明 |
---|---|
純 - 責任鏈 | 需求可被責任鏈消費 |
不純 - 責任鏈 | 責任鏈不能處理需求 |
Chain 定義 & Chain UML
● Chain 定義
多個物件都有機會處理請求,避免請求者(request
)、接收處理者(handle
)耦合;將對象鏈成一條鏈,並沿著鏈傳遞直到有處理者
● 處理的邏輯重點在 鏈上
● Chain UML 有分為兩種:分別都有兩個行為 請求、處理
A. 合併 請求、處理:這裡我們主要是關注 Linked 的處理
類別 | 功能 |
---|---|
LinkedItem (鏈、抽象) | 其內部有 0 ~ 1 個 LinkedItem,判斷是否讓下一個 LinkedItem 處理;這裡 有 2 個選擇,不是 處理,就是 往下傳遞 |
ConcreateA、B (實作) | 處理使用者的請求 (請求、處理一起) |
● LinkedItem 有三個責任
A. 對外開放一個請求處理入口(
handleRequest
)B. 設定鏈的方式 (
setNext
)C. 具體的請求者的抽象方法
● 抽象定義能處理的級別
● 抽象定義每個子類處理的方式
B. 分離 請求、處理:我們在基礎的責任鏈上將 請求
、處理
分開
● 這裡的重點是使用 依賴倒置 概念,將
請求
、處理
抽象化
類別 | 功能 |
---|---|
LinkedItem (鏈、抽象) | 其內部有 0~1 個 LinkedItem,判斷是否讓下一個 LinkedItem 處理;這裡 有 2 個選擇,不是 1.處理,就是 2.往下傳遞 |
ConcreateLinkedA、B(實作) | 處理使用者的請求 |
Request (抽象) | 抽象化共同處理的請求 |
ConcreateRequestA、B(實作) | 定義請求的詳細規則 |
Chain 優缺點
● Chain 設計優點
● 自由的決定要由何處開始處理這個任務
● 符合單一職責:一個類只負責它自身要處理的有範圍的邏輯
● 符合迪米特原則:不用知道別的類如何處理,只須關注自己的部份,其他交給父類處理
● 請求者(Request
)與 處理者(Handler
)解偶
● 處理者不用知道請求的全貌,只須關注部份(關注自己能不能處理、處理方式)
● 請求者也無須知道是哪個類處理的,只須關注結果即可
● Chain 設計缺點
● 搜尋速度較慢(如 Linked List 的特性)
可以 在
setNext
時設置一個閥值,避免無意識的破壞調系統性能(不好查)
● 採用遞歸方式運作,也就導致了複雜度增加、可讀性降低
Chain 實作
Chain 標準實現:不分離請求
● 現在假設在申報出國出差的經費,經費需要經過不同主管批省
A. 批省要有順序
B. 每個主管的額度也不相同
A. LinkedItem
(抽象):1. 定義相同行為,並且內部有 2. 0 ~ 1
個相同的抽象成員 (指向 next 處理對象)
● 這裡要特別注意,結束條件:如果沒有設定結束條件,會導致 Function 堆疊超出 (StackOverflow 錯誤)
// 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);
}
● 從這裡可以看出 模板設計的影子 (固定調用抽象
handle
方法)
B. ConstractA、B
:定義實體處理的子類,並在這裡 由各個子類自行判斷是否處理,在這裡返回的 結果將會決定 Linked 是否繼續往下執行
// 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 使用:使用者可以自行串接處理的順序,並決定啟動時機
// 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));
}
}
● final 關鍵字:
使用 Java 類加載的特性,定義 final 變量
最慢
必須在建構函數中定義
--實作--
Chain 標準實現拓展:分離請求
● 現在除了上面的需求外還有新增一個需求:需要填入申請者的資料 (申請者名稱、Project 名稱、花費)
A. Request
:定義新增的 Request 抽象類
public interface Request {
String getRequestName();
String projectName();
int cost();
}
B. ConcreateRequestA、B
:定義具體的 Request 處理細節
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;
}
}
C. LinkedItem
抽象:也就是處理類的抽象,主要修改接收參數,讓 LinkedItem 抽象
參數依賴抽象 Request
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);
}
D. ConcreateLinkedA
、B:具體實現抽象 Linked 細節,同上個範例,沒有做修改
請參考上一小節的程式範例
● User 使用:這裡較不一樣的是使用者必須自訂 Request 需求
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()));
}
}
● 這裡可以注意到一點,使用者(高層模塊)通常會使用類一個封裝過得類來進行請求,而不是自己串接任務鏈
可以使用創建類型:像是
Factory
、Builder
設計都可以
--實作--
Android Source View 事件
Android View 的傳遞事件就是使用 Linked 模式,可以參考另一篇 View 事件分發
透過 dispatchTouchEvent
方法來迭代
ViewGroup 事件分發 dispatchTouchEvent
● 這裡我們直接從 ViewGroup 接收的 dispatchTouchEvent
方法開始分析
簡單來說,都是傳遞的事件會從 PhoneWindow 的 DecorView (ViewGroup) 往下傳遞事件,傳遞方式是使用 遞迴呼叫 (
DFS
)
A. 清理當前 View 的事件:如果是 ACTION_DOWN
事件,代表它是一個新的事件,需要清理當前 ViewGroup 的一些設定
關注變數 | 當前數值 |
---|---|
actionMasked | MotionEvent.ACTION_DOWN |
// 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
// 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 中斷
// 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;
}
}
B. 檢查該 ViewGroup 中斷事件
關注變數 | 當前數值 |
---|---|
actionMasked | MotionEvent.ACTION_DOWN |
mFirstTouchTarget | null |
// 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;
}
...
}
}
C. 檢查事件是否被取消
// 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;
D. ViewGroup 處理、分發事件(事件沒有被取消或中斷):在這一步會透過遞迴呼叫,嘗試找到處理事件的 View
這裡的遞迴操作,就是遍歷二元樹的操作
關注變數 | 當前數值 |
---|---|
actionMasked | MotionEvent.ACTION_DOWN |
mFirstTouchTarget | null |
canceled | false |
intercepted | false |
// 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;
}
E. 判斷事件是否已經分發到目標 ChildView,1. 如果沒有 ChildView 處理,則拋回到 ViewGroup 的 View、2. 處理 cancel 事件
關注變數 | 當前數值 |
---|---|
actionMasked | MotionEvent.ACTION_DOWN |
mFirstTouchTarget | 有可能為 null (如果是 ChildView 處理就不是 null) |
canceled | false |
intercepted | false |
// 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 事件傳遞 dispatchTransformedTouchEvent
● 上面介紹了 ViewGroup 的事件分法,現在介紹 ViewGroup 的事件傳遞 (+Transformed)
A. 處理 View 的取消事件:如果有就透過 dispatchTouchEvent
方法分發給目標 View
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;
}
B. 計算將被傳遞的 pointer 數量
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;
}
...
}
C. 觸摸數量相同:計算偏移量,再透過 dispatchTouchEvent
方法分發事件給 ChildView
● 偏移量
如果有調用
scrollTo
orscrollBy
對 ChildView 進行滾動,就會產生 xy 的偏移量
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);
}
...
}
觸摸數量不相同*:同上,最終都是透過 dispatchTouchEvent
方法分發事件
D. 觸摸數量不相同:同上,最終都是透過 dispatchTouchEvent
方法分發事件
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;
}
更多的物件導向設計
物件導向的設計基礎如下,如果是初學者或是不熟悉的各位,建議可以從這些基礎開始認識,打好基底才能走個更穩(在學習的時候也需要不斷回頭看)!
創建模式 Creation Patterns
● 創建模式 PK
● 創建模式 - Creation Patterns
:
創建模式用於「物件的創建」,它關注於如何更靈活、更有效地創建物件。這些模式可以隱藏創建物件的細節,並提供創建物件的機制,例如單例模式、工廠模式… 等等,詳細解說請點擊以下連結
● Singleton 單例模式 | 解說實現 | Android Framework Context Service
● Abstract Factory 設計模式 | 實現解說 | Android MediaPlayer
● Factory 工廠方法模式 | 解說實現 | Java 集合設計
● Builder 建構者模式 | 實現與解說 | Android Framwrok Dialog 視窗
● Clone 原型模式 | 解說實現 | Android Framework Intent
行為模式 Behavioral Patterns
● 行為模式 PK
● 行為模式 - Behavioral Patterns
:
行為模式關注物件之間的「通信」和「職責分配」。它們描述了一系列物件如何協作,以完成特定任務。這些模式專注於改進物件之間的通信,從而提高系統的靈活性。例如,策略模式、觀察者模式… 等等,詳細解說請點擊以下連結
● Stragety 策略模式 | 解說實現 | Android Framework 動畫
● Interpreter 解譯器模式 | 解說實現 | Android Framework PackageManagerService
● Chain 責任鏈模式 | 解說實現 | Android Framework View 事件傳遞
● Specification 規格模式 | 解說實現 | Query 語句實做
● Command 命令、Servant 雇工模式 | 實現與解說 | 物件導向設計
● Memo 備忘錄模式 | 實現與解說 | Android Framwrok Activity 保存
● Visitor 設計模式 | 實現與解說 | 物件導向設計
● Template 設計模式 | 實現與解說 | 物件導向設計
● Mediator 模式設計 | 實現與解說 | 物件導向設計
● Composite 組合模式 | 實現與解說 | 物件導向設計
● Observer 觀察者模式 | JDK Observer | Android Framework Listview
結構模式 Structural Patterns
● 結構模式 PK
● 結構模式 - Structural Patterns
:
結構模式專注於「物件之間的組成」,以形成更大的結構。這些模式可以幫助你確保當系統進行擴展或修改時,不會破壞其整體結構。例如,外觀模式、代理模式… 等等,詳細解說請點擊以下連結
● Decorate 裝飾模式 | 解說實現 | 物件導向設計
● Iterator 迭代設計 | 解說實現 | 物件導向設計