owned this note
owned this note
Published
Linked with GitHub
---
title: 'Decorate 裝飾模式'
disqus: kyleAlien
---
Decorate 裝飾模式
===
## Overview of Content
如有引用參考請詳註出處,感謝 :smile:
> 裝飾模式,在不改變其繼承類別動態拓展功能的方法,**==繼承的替代方案之一==**,**裝飾模式比繼承生成子類更靈活**
它的實現可參考 Java's IO 流、Android 的第三方庫 RxJava
:::success
* 如果喜歡讀更好看一點的網頁版本,可以到我新做的網站 [**DevTech Ascendancy Hub**](https://devtechascendancy.com/)
本篇文章對應的是 [**Decorate 裝飾模式 | 解說實現 | Android Framework Context**](https://devtechascendancy.com/object-oriented_design_decorate/)
:::
[TOC]
## Decorate 使用場景
1. 當不能使用繼承或是使用了繼承後不利於維護時可以使用
:::warning
當繼承超過 2 層的時候,其實程式會難以維護,這時其實就可以重新檢視程式,並考慮轉換設計
:::
2. 以不影響其他類別的方式 **動態的使用(動態新增、移除功能)、撤銷其類別,而不必依靠覆寫** (Override)
:::success
繼承是靜態的給類新增功能、裝飾則是動態的給類新增功能
:::
3. 對於原先的類做加裝、改裝功能(並不影響原先的類)
### Decorate 定義 & Decorate UML
* **Decorate 定義**
**動態的給一個物件添加(減少)額外功能,裝飾模式相比繼承更加靈活**
* **Decorate 角色介紹**
| 角色 | 功能說明 |
| -------- | -------- |
| **Component** | 可以是一個抽象,或是接口,它是最原始的物件(有最基礎、最核心的方法) |
| **ConcreateComponent** | 具體實現類,也是等待被裝飾(被代理)者,它實現了最基礎的方法 |
| **Decorator** | **它^1.^ 繼承 Component 類 並 ^2.^ 依賴抽象(Component 類)取代繼承**,內部持有一個抽象 Component 物件 |
| **ConcreateDecorator** | 具體裝飾者,你要它組裝結合 ConcreateComponent 的實做一起表現出來 |
> **依靠`注入抽象模組`(高層模組),使用別類(低層模組)實現功能**
> 
### Decorate 設計 - 優缺點
* **Decorate 設計優點** :
1. 可以**減少里式原則帶來的,繼承時帶來的負荷** (一定要複寫該類 or 類別方法增多),子類無限擴張等問題
> 當然你需要切細每個類的方法
2. 用戶可依據需求使用,而不改變該類的方法,符合開閉原則
:::info
子類可以單獨發展,而不必受到繼承約束
:::
3. 裝飾、被裝飾類都可以獨立發展!不會相互依賴~ (裝飾類依賴的是抽象)
* **Decorate 設計缺點** :
1. 高層模組發生變化時勢必會影響到繼承的所有子類別
2. Debug 會更困難,因為**層層嵌套,邏輯變複雜** (可讀性降低)
3. 多層裝飾會導致效率變差 (仍是層層傳遞的問題)
## Decorate 實現
### Decorate 標準實現
1. **`Component` 類**:IWear 擁有最基礎、核心的方法抽象
```kotlin=
interface IWear {
fun wearClothes()
}
```
2. **`ConcreateComponent` 類**:待被裝飾的類(也可以說待被代理)
```kotlin=
class NoWorkPerson : IWear {
override fun wearClothes() {
println("Wear Underwear")
}
}
```
3. **`Decorator` 類**:繼承於 `IWear` 類,並持有一個 `IWear` 物件
```kotlin=
abstract class DecorClothes constructor(val realWear: IWear) : IWear {
override fun wearClothes() {
realWear.wearClothes()
}
}
```
4. **`ConcreateDecorator` 類**:這個類的重點是在於實現你自己的邏輯,你要如何裝飾傳入的實際類 (這裡創建兩個裝飾類)
```kotlin=
class WorkPerson constructor(realWear: IWear): DecorClothes(realWear) {
// 裝飾方法
private fun wearLittle() {
println("Wear shoes")
}
override fun wearLittle() {
wearLittle()
println("--- WorkPerson finish decor")
super.wearClothes()
}
}
class WorkHardPerson constructor(realWear: IWear): DecorClothes(realWear) {
// 裝飾方法
private fun wearMore() {
println("Wear T-short")
println("Wear paint")
}
override fun wearClothes() {
wearMore()
println("--- WorkHardPerson finish decor")
super.wearClothes()
}
}
```
* 作為使用者方,使用裝飾模式:
```kotlin=
fun main() {
// 一層一層裝飾,最終返回最後一個裝飾者
val finalMan = NoWorkPerson().let { noWorkPerson ->
WorkPerson(noWorkPerson)
}.let { workPerson ->
WorkHardPerson(workPerson)
}
// 其實最終 finalMan 類型是 WorkHardPerson
finalMan.wearClothes()
}
```
> 
## Android Source Context 分析
Context 物件以說是 Android 開發者最熟悉,也是最不熟悉的物件了,它應用於 Android 4 大組件中(`Activity`、`Service`、`ContentProvider`、`Broadcast`),而與 Context 最直接關聯、也最常見的就是 Activity 跟 Application
而 Context 本身是個抽象類,在使用時其實也用到了裝飾模式
```java=
// Context.java
public abstract class Context {
... 省略
}
```
:::success
以下分析的是 [**Android 10**](https://cs.android.com/android/platform/superproject/+/android-10.0.0_r40:) 的源碼
:::
### Context & ContextImpl 的裝飾角色
* 我們這邊先直接揭露 [**Context**](https://cs.android.com/android/platform/superproject/+/android-10.0.0_r40:frameworks/base/core/java/android/content/Context.java) & [**ContextImpl**](https://cs.android.com/android/platform/superproject/+/android-10.0.0_r40:frameworks/base/core/java/android/app/ContextImpl.java) 在裝飾模式下的角色,再進行分析
| 裝飾模式 Prototype 角色 | Android 實做類 | 功能說明 |
| ------- | - | -------- |
| **Component** | `Context` | 可以是一個抽象,或是接口,它是最原始的物件(有最基礎、最核心的方法) |
| **ConcreateComponent** | `ContextImpl` | 具體實現類,也是等待被裝飾(被代理)者,它實現了最基礎的方法 |
| **Decorator** | ``ContextImpl``、`ContextWrapper` | **它^1.^ 繼承 Component 類 並 ^2.^ 依賴抽象(Component 類)取代繼承**,內部持有一個抽象 Component 物件 |
Android Context 實現裝置模式的 UML 概念圖如下
> 
### Context & ContextImpl 裝飾結構分析
* Context 類作為裝飾模式中抽象的「原始物件」,它並不實做,但擁有許多核心的抽象方法,其中就包括 **資源管理**、**文件管理**、**包管理**、**類加載**、**權限管理**、**提供系統服務**… 等等功能,我們依照 4 大組件的方向去看看 Context 與其的重要性
1. **裝飾模式下 Context 是作為 `Component` 角色**
* 最常見 Context 提供「**Activity**」的相關功能如下
```java=
// Context.java
public abstract class Context {
... 省略
// 取得應用的訊息
public abstract ApplicationInfo getApplicationInfo();
// 取得 Application 的 Context
public abstract Context getApplicationContext();
// 啟動 Activity
public abstract void startActivity(@RequiresPermission Intent intent);
public abstract void startActivity(@RequiresPermission Intent intent,
@Nullable Bundle options);
// 啟動多個 Activity
public abstract void startActivities(@RequiresPermission Intent[] intents);
public abstract void startActivities(@RequiresPermission Intent[] intents, Bundle options);
}
```
* 最常見 Context 提供「**Service**」的相關功能如下
```java=
// Context.java
public abstract class Context {
... 省略
// 啟動 Service
@Nullable
public abstract ComponentName startService(Intent service);
// 停止 Service
public abstract boolean stopService(Intent service);
// 應用綁定 Service
public abstract boolean bindService(@RequiresPermission Intent service,
@NonNull ServiceConnection conn, @BindServiceFlags int flags);
// 解綁定 Service
public abstract void unbindService(@NonNull ServiceConnection conn);
}
```
* 最常見 Context 提供「**Broadcast**」的相關功能如下
```java=
// Context.java
public abstract class Context {
... 省略
// 註冊一個廣播接收者
@Nullable
public abstract Intent registerReceiver(@Nullable BroadcastReceiver receiver,
IntentFilter filter);
@Nullable
public abstract Intent registerReceiver(@Nullable BroadcastReceiver receiver,
IntentFilter filter,
@RegisterReceiverFlags int flags);
// 解註冊廣播接收者
public abstract void unregisterReceiver(BroadcastReceiver receiver);
// 發送廣播
public abstract void sendBroadcast(@RequiresPermission Intent intent);
// 發送帶有權限的廣播
public abstract void sendBroadcast(@RequiresPermission Intent intent,
@Nullable String receiverPermission);
public abstract void sendBroadcastMultiplePermissions(Intent intent,
String[] receiverPermissions);
// 發送順序廣播
public abstract void sendOrderedBroadcast(@RequiresPermission Intent intent,
@Nullable String receiverPermission);
// 發送黏性廣播,這意味著 Intent 會保留在廣播內
public abstract void sendStickyBroadcast(@RequiresPermission Intent intent);
public abstract void sendStickyOrderedBroadcast(@RequiresPermission Intent intent,
BroadcastReceiver resultReceiver,
@Nullable Handler scheduler, int initialCode, @Nullable String initialData,
@Nullable Bundle initialExtras);
}
```
* 最常見 Context 提供「**ContentProvider**」的相關功能如下
```java=
// Context.java
public abstract class Context {
... 省略
// 為該應用取得一個 ContentResolver 物件
public abstract ContentResolver getContentResolver();
}
```
* 最常見 Context 提供的重要功能:
```java=
// Context.java
public abstract class Context {
... 省略
public abstract Looper getMainLooper();
public abstract @Nullable Object getSystemService(@ServiceName @NonNull String name);
public abstract @Nullable Object getSystemService(@ServiceName @NonNull String name);
public abstract SharedPreferences getSharedPreferences(String name, @PreferencesMode int mode);
@Nullable
public abstract File getExternalFilesDir(@Nullable String type);
}
```
2. **裝飾模式下 ContextImpl 類,既可作為作為 `ConcreateComponent` 也可作為 `Decorator` 角色**,也就是它本身可以是個沒有裝飾功能的「實做」,也可以「裝飾類」
以下是 [**ContextImpl 的建構函數**](https://cs.android.com/android/platform/superproject/+/android-10.0.0_r40:frameworks/base/core/java/android/app/ContextImpl.java;l=2516):我們可以看到,它是可以傳入 `ContextImpl` 類,而這個參數其實也就透漏個它是一個裝飾設計(也就是說可以加強、減弱、使用… 傳入的 ContextImpl 物件)
```java=
// ContextImpl.java
private ContextImpl(@Nullable ContextImpl container,
@NonNull ActivityThread mainThread,
...) {
... 忽略部份
if (container != null) {
// 取外部 ContextImpl 物件來使用
mBasePackageName = container.mBasePackageName;
opPackageName = container.mOpPackageName;
setResources(container.mResources);
mDisplay = container.mDisplay;
} else {
... 忽略部份
}
... 忽略部份
}
```
* **ContextImpl 作為 `Decorator` 「實做」角色**:創建 System Context 時會傳入 `null`,代表的是 **不去裝飾**(也就是 **最外層的 Context**)
```java=
// ContextImpl.java
@UnsupportedAppUsage
static ContextImpl createSystemContext(ActivityThread mainThread) {
LoadedApk packageInfo = new LoadedApk(mainThread);
ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0,
null, null);
... 忽略
return context;
}
```
* **ContextImpl 作為 `ConcreateComponent` 「裝飾類」角色**:創建 Applicatoin Context 時會傳入 `this`,代表 **可以做裝飾**(也就是某一層的 Context)
```java=
// ContextImpl.java
@Override
public Context createApplicationContext(ApplicationInfo application, int flags)
throws NameNotFoundException {
LoadedApk pi = mMainThread.getPackageInfo(application, mResources.getCompatibilityInfo(),
flags | CONTEXT_REGISTER_PACKAGE);
if (pi != null) {
ContextImpl c = new ContextImpl(this, mMainThread, pi, null, mActivityToken,
new UserHandle(UserHandle.getUserId(application.uid)), flags, null, null
... 忽略
}
throw new PackageManager.NameNotFoundException(
"Application package " + application.packageName + " not found");
}
```
### 最明顯的裝飾 ContextWrapper
* 如果你還是沒辦法看出 Context 裝飾模式的使用的話,我們可以看看 [**`ContextWrapper` 類**](https://cs.android.com/android/platform/superproject/+/android-10.0.0_r40:frameworks/base/core/java/android/content/ContextWrapper.java),從這個類中我們可以很清晰的看到 ContextWrapper 其實沒做甚麽事情,單純的是把外部傳入的 Context 保存起來,並在需要的時候呼叫使用
我們看看最常使用的 `startActivity` 類就可以發現,源碼如下
```java=
// ContextWrapper.java
public class ContextWrapper extends Context {
// 外部傳入的 Context
@UnsupportedAppUsage
Context mBase;
public ContextWrapper(Context base) {
// 保存外部傳入的 Context
mBase = base;
}
@Override
public void startActivity(Intent intent) {
// 直接呼叫外部傳入的 Context
mBase.startActivity(intent);
}
... 省略部份
}
```
### Activity 綁定 Context 的重點
* 這邊我們進入 `performLaunchActivity` 函數:
:::warning
* 這裡我們忽略 Android 系統是如何啟動的細節,直接看創建 Activity 的關鍵代碼,也就是從 [**ActivityThread**](https://cs.android.com/android/platform/superproject/+/android-10.0.0_r40:frameworks/base/core/java/android/app/ActivityThread.java;l=3347)#`performLaunchActivity` 開始看
:::
```java=
// ActivityThread.java
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
... 省略部份
// 創建 ContextImpl 類
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
// 創建目標 Activity
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
} ... 省略 catch
try {
// 如果沒有 Application 的話,就創建 Application instacne
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
... 省略部份
if (activity != null) {
... 省略部份
// 連接 Context & Activity 的關係
appContext.setOuterContext(activity);
// 連接 Activity & Context & Application 類
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback,
r.assistToken);
... 省略部份
r.activity = activity;
}
} ... 省略 catch
}
```
可以主要的相關源碼中,我們可以看到並發現以下事情…
1. 先創建 `ContextImpl` 物件實例,再創建 `Activity` 實例
2. 之後透過 `ContextImpl`#`setOuterContext` 方法,來連接 Context 與 Activity 的關係(有就是在 Context 內放置 Activity 物件)
```mermaid
graph LR
subgraph Context 實例
a1(Activity)
end
```
3. **最後使用 `Activity`#`attach` 方法來連接 Application、Context 兩者之間的關係**,到了這裡我們就可以看到 Activity 是如何與 Context 產生關係的
```mermaid
graph LR
Activity --> |產生關聯| Application
Activity --> |產生關聯| Context
```
## 更多的物件導向設計
物件導向的設計基礎如下,如果是初學者或是不熟悉的各位,建議可以從這些基礎開始認識,打好基底才能走個更穩(在學習的時候也需要不斷回頭看)!
:::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 設計模式` `基礎進階`