---
title: 'interpreter - 解譯器模式'
disqus: kyleAlien
---
interpreter - 解譯器模式
===
## Overview of Content
直譯器定義了一個==表達式介面==,透過該介面直譯一個上下文,可透過資料結構的`Tree`、`Stack` 建構
:::success
* 如果喜歡讀更好看一點的網頁版本,可以到我新做的網站 [**DevTech Ascendancy Hub**](https://devtechascendancy.com/)
本篇文章對應的是 [**Interpreter 解譯器模式 | 解說實現 | Android Framework PackageManagerService**](https://devtechascendancy.com/object-oriented_design_interpreter_framework/)
:::
[TOC]
## Interpreter 解釋器定義
* 重複發生的問題,並且問題可以簡單分為終端、非終端
* 通常可使用在 **自定義語言的解析**,**定義語言的 ++形式++**
:::info
大多數是定義形式語言,但也是可以用來描述語言、文法 (但 Scope 就比較大)
:::
### 形式語言 - 符號
* **形式語言**:以下我們假設定義兩個符號,這兩個符號代表的意義如下
* **`::=`**:表示可推導
* **`*`**:表示閉包(`Closure`)
```shell=
# 來解釋以下這兩行形式語言
- S ::= abA*ef
- A ::= cd
```
* 先看 `S :== abA*ef`,其中有 `A` 符號,下一行則會定義 `A ::= cd`
* 因此可以把定義帶入,S 推導為 `S :== ab(cd)*ef`,使用正規表達來看,ab 之間的 cd 符號可以有 0~N 個,最後才是 ef 符號,以下定義則都成立
```shell=
## 以正則表達式的角度去看
## abef 之間有 0 個 cd
abef
## abef 之間有 1 個 cd
abcdef
## abef 之間有 3 個 cd
abcdcdcdef
```
* **終端符號**:無法再被推導的符號 (可再找到來源),從上面我們可以知道
| 符號 | 是否是終端符號 | 說明 |
| -------- | -------- | -------- |
| ab | Y | 不可在被推導 |
| cd | Y | 不可在被推導 |
| ef | Y | 不可在被推導 |
| S | N | 可推導出右邊表達式 |
| A | N | 可推導出右邊表達式 |
:::success
* 初始符號
由於整個推導式是從 `S` 開始,所以 `S` 也稱為 **初始符號**
:::
### Interpreter 使用場景
1. 一個語言,可以將該語句表達為一個 **抽象語法樹** 時;像是解釋 `a + b - c`語法時… 其中可以知道
- **終端符號**:`a`、`b`、`c` 符號
- 非終端符號:`+`、`-` 符號
> 
2. 特定領域的重複問題;ABC 轉為 abc ,這些符號全部都是終端符號
- **終端符號**:`A`、`B`、`C`、`a`、`b`、`c` 符號
### Interpreter 定義 & Interpreter UML
* Interpreter 定義
給一門語言定義它的語法的一種表示,並給定一個解釋器來解釋該語言的語法
* Interpreter UML 如下
| 類 | 功能 |
| -------- | -------- |
| `AbstractExoression` | 抽象表達式,定義抽象直譯方法,讓子類實做 |
| `TerminalExoression` | 終端終結表達式 |
| `NonTerminalExoression` | 繼承 `AbstractExoression`,並聚合 `AbstractExoression` |
> 
:::success
* `NonTerminalExoression` 使用依賴倒置的概念,讓抽象依賴於抽象
:::
### Interpreter 設計- 優缺點
* **Interpreter 設計優點** :
對於每個符號的**擴充性極高**,文法易新增
* **Interpreter 設計缺點** :
* 每個文法建立一個類別做處理,所以 **不適合大量複雜的文法**
* 類的膨脹
* 解釋器模式採用遞歸調用
* 會導致程式的可讀性降低、複查性提昇 (遞歸就是如此)
* 同時導致效率不佳
## Interpreter 實現
### 標準實現 - 加減運算
1. **`AbstractExoression` 類**:AbstractExpression 提取所有解釋器的共同行為作為抽象,這裡宣告解釋器都必須返回 int
```java=
// AbstractExoression 類
public abstract class AbstractExpression {
// 固定返回 int
public abstract int interpreter();
}
```
2. **`TerminalExoression` 類**:NumberExpression 代表了終端,也就是一般數字 (數字無法向下推導)
```java=
// TerminalExoression 類
public class NumberExpression extends AbstractExpression {
private final int num;
public NumberExpression(int num) {
this.num = num;
}
@Override
public int interpreter() {
return num;
}
}
```
3. **`NonTerminalExoression` 類**:再次定一個抽象,**內部聚合了 `AbstractExoression` 抽象**
```java=
// NonTerminalExoression 類
public abstract class OperatorExpression extends AbstractExpression {
protected final AbstractExpression a1;
protected final AbstractExpression a2;
public OperatorExpression(AbstractExpression a1, AbstractExpression a2) {
this.a1 = a1;
this.a2 = a2;
}
}
```
* 透過繼承 **OperatorExpression** 來完成不同符號操作
```java=
// 加法
public class AddExpression extends OperatorExpression {
public AddExpression(AbstractExpression a1, AbstractExpression a2) {
super(a1, a2);
}
@Override
public int interpreter() {
// 加法操作
return a1.interpreter() + a2.interpreter();
}
}
// ----------------------------------------------------------
// 減法
public class ReduceExpression extends OperatorExpression {
public ReduceExpression(AbstractExpression a1, AbstractExpression a2) {
super(a1, a2);
}
@Override
public int interpreter() {
// 減法操作
return a1.interpreter() - a2.interpreter();
}
}
```
:::success
* OperatorExpression 類設計,方便於拓展操作符號
:::
* 加減 interpreter 實現 UML
> 
### 套用 Stack - 計算
* 這裡使用 Stack 數據結構的特性 (先進後出),並使用 interpreter 設定做為 Stack element,來運算加減法
> 這裡規範使用 `空格` 來區隔每個符號
```java=
public class Calculation {
private final Stack<AbstractExpression> stack = new Stack<>();
public Calculation(String expression) {
initExpression(expression.split(" "));
}
private void initExpression(String[] symbols) {
for (int i = 0; i < symbols.length; i++) {
String symbol = symbols[i];
switch (symbol) {
// 非終端
case "+":
stack.push(
new AddExpression(
stack.pop(),
new NumberExpression(Integer.parseInt(symbols[++i]))
)
);
break;
// 非終端
case "-":
// 減號
stack.push(
new ReduceExpression(
stack.pop(),
new NumberExpression(Integer.parseInt(symbols[++i]))
)
);
break;
default:
// 一般數字(終端)
stack.push(new NumberExpression(Integer.parseInt(symbol)));
break;
}
}
}
public int getResult() {
return stack.isEmpty() ? -1 : stack.pop().interpreter();
}
}
```
* User 使用 Calculation 運算加減法 (必須要符合規範)
```java=
public class CalMain {
public static void main(String[] args) {
Calculation calculation = new Calculation("1 + 2 + 3 + 5 - 10");
System.out.println("Calculation result: " + calculation.getResult());
}
}
```
**--實做--**
> 
## Android Source
Interpreter 的實踐比較少,我們可以在 Android PackageManagerService 解析 APK 時看到類似概念;
在 Android APK 解析 AndroidManifest 文件時會從 `application` 標籤到 `四大組件` or 最外圍的 `Perssion`... 等等來解析
**==每個階段都是重複相同的行為 (解析),但實作又不同,這個概念就是 interpreter==**
:::info
* **AndroidManifest.xml** 文件
APK 的 **AndroidManifest.xml 就相當於一個 ++APK 的目錄++**,該 APK 有哪些功能都記錄在該文件上
:::
### [PakageParser2](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/pm/parsing/PackageParser2.java) - 開始解析 APK File
* 這邊我們跳過 PackageManagerService 掃描、安裝 APK 的階段 (請看以下簡單流程圖),直接從 Parser APK 這個文件開始看
> 
* `PackageParser2` 類:`parsePackage` 方法負責解析 APK File
```java=
// PakageParser2.java
// 解析包的工具類
private ParsingPackageUtils parsingUtils;
// 線程隔離的 ParseTypeImpl
private ThreadLocal<ParseTypeImpl> mSharedResult;
public PackageParser2(String[] separateProcesses, boolean onlyCoreApps,
DisplayMetrics displayMetrics, @Nullable File cacheDir, @NonNull Callback callback) {
... 省略部分
parsingUtils = new ParsingPackageUtils(onlyCoreApps, separateProcesses, displayMetrics,
splitPermissions, callback);
...
}
@AnyThread
public ParsedPackage parsePackage(File packageFile, int flags, boolean useCaches)
throws PackageParserException {
... 省略部分
ParseInput input = mSharedResult.get().reset();
// @ 追蹤 parsePackage 方法
ParseResult<ParsingPackage> result = parsingUtils.parsePackage(input, packageFile, flags);
// 錯誤則直接拋出
if (result.isError()) {
throw new PackageParserException(result.getErrorCode(), result.getErrorMessage(),
result.getException());
}
// 取得解析結果
ParsedPackage parsed = (ParsedPackage) result.getResult().hideAsParsed();
...
return parsed;
}
```
解析類 - 關係圖
> 
### [ParsingPackageUtils](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/content/pm/parsing/ParsingPackageUtils.java) 解析工具
* ParsingPackageUtils#parsePackage:負責解析檔案 (包括資料夾) 的 APK 檔案
| 解析方法 | 功能 |
| -------- | -------- |
| parseClusterPackage | 解析整個資料夾,最終會調用到 `parseMonolithicPackage` |
| parseMonolithicPackage | 解析單個 Flie |
```java=
// ParsingPackageUtils.java
public ParseResult<ParsingPackage> parsePackage(ParseInput input, File packageFile,
int flags)
throws PackageParserException {
// 判斷文件是否是資料夾
if (packageFile.isDirectory()) {
// 最終都會回調到 parseMonolithicPackage 方法
return parseClusterPackage(input, packageFile, flags);
} else {
// @ 追蹤 parseMonolithicPackage 方法
return parseMonolithicPackage(input, packageFile, flags);
}
}
```
* **ParsingPackageUtils#parseMonolithicPackage**:解析單個檔案,在這裡會輕量級解析 APK 文件,最後在呼叫parseBaseApk 方法
```java=
// ParsingPackageUtils.java
private ParseResult<ParsingPackage> parseMonolithicPackage(ParseInput input, File apkFile,
int flags) throws PackageParserException {
// 輕量級解析
final ParseResult<PackageLite> liteResult =
ApkLiteParseUtils.parseMonolithicPackageLite(input, apkFile, flags);
if (liteResult.isError()) {
return input.error(liteResult);
}
final PackageLite lite = liteResult.getResult();
... 省略失敗
final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags);
try {
// @ 追蹤 parseBaseApk 方法
final ParseResult<ParsingPackage> result = parseBaseApk(input,
apkFile,
apkFile.getCanonicalPath(),
assetLoader, flags);
if (result.isError()) {
return input.error(result);
}
return input.success(result.getResult()
.setUse32BitAbi(lite.isUse32bitAbi()));
} /* 省略 catch、finally */
}
```
* 當找到要分析的目標 APK 後,就會調用 ParsingPackageUtils#**parseBaseApk** 方法
1. 如果 baseApk 絕對路徑是以 `/mnt/expand/` 開頭,就取 `/mnt/expand/` 後的設定為 volumeUuid
2. 透過 AssetManager 解析 baseApk 中的 `AndroidManifest.xml` 文件
```java=
// ParsingPackageUtils.java
public static final String MNT_EXPAND = "/mnt/expand/";
public static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";
private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, File apkFile,
String codePath, SplitAssetLoader assetLoader, int flags)
throws PackageParserException {
// 取得 baseApk 絕對位置
final String apkPath = apkFile.getAbsolutePath();
// 1. 取得 volumeUuid
String volumeUuid = null;
if (apkPath.startsWith(MNT_EXPAND)) {
final int end = apkPath.indexOf('/', MNT_EXPAND.length());
volumeUuid = apkPath.substring(MNT_EXPAND.length(), end);
}
... log 訊息
// 取得 AssetManager
final AssetManager assets = assetLoader.getBaseAssetManager();
final int cookie = assets.findCookieForPath(apkPath);
if (cookie == 0) {
return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST,
"Failed adding asset path: " + apkPath);
}
// 2. 指定分析 AndroidManifest.xml 文件
try (XmlResourceParser parser = assets.openXmlResourceParser(cookie,
ANDROID_MANIFEST_FILENAME)) {
final Resources res = new Resources(assets, mDisplayMetrics, null);
// 3. @ 分析 parseBaseApk
ParseResult<ParsingPackage> result = parseBaseApk(input, apkPath, codePath, res,
parser, flags);
... 省略部分
// 用於以後標示這個解析後的 Package
pkg.setVolumeUuid(volumeUuid);
...
return input.success(pkg);
} /* 省略 catch */
}
```
3. 呼叫重載方法 ParsingPackageUtils#**parseBaseApk**
* 讀取 [**attrs_manifest.xml**](https://android.googlesource.com/platform/frameworks/base/+/master/core/res/res/values/attrs_manifest.xml) 內的屬性集
* 透過 parseBaseApkTags 方法分析 AndroidManifest 基本屬性
```java=
// ParsingPackageUtils.java
// ParseResult 方法重載
private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, String apkPath,
String codePath, Resources res, XmlResourceParser parser, int flags)
throws XmlPullParserException, IOException {
... 省略部分
// 屬性集在 attrs_manifest.xml
final TypedArray manifestArray = res.obtainAttributes(parser, R.styleable.AndroidManifest);
try {
final boolean isCoreApp =
parser.getAttributeBooleanValue(null, "coreApp", false);
final ParsingPackage pkg = mCallback.startParsingPackage(
pkgName, apkPath, codePath, manifestArray, isCoreApp);
// @ 分析 parseBaseApkTags
final ParseResult<ParsingPackage> result =
parseBaseApkTags(input, pkg, manifestArray, res, parser, flags);
if (result.isError()) {
return result;
}
return input.success(pkg);
} finally {
// TypedArray 必須回收
manifestArray.recycle();
}
}
```
> 
### 解析 AndroidManifest - Application 標籤
* ParsingPackageUtils#**parseBaseApkTags**:主要就是讀取 AndroidManifest 資源檔案,並進行解析
```java=
// ParsingPackageUtils.java
private ParseResult<ParsingPackage> parseBaseApkTags(ParseInput input, ParsingPackage pkg,
TypedArray sa, Resources res, XmlResourceParser parser, int flags)
throws XmlPullParserException, IOException {
ParseResult<ParsingPackage> sharedUserResult = parseSharedUser(input, pkg, sa);
... 省略部分
boolean foundApp = false;
final int depth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG
|| parser.getDepth() > depth)) {
if (type != XmlPullParser.START_TAG) {
continue;
}
String tagName = parser.getName();
final ParseResult result;
// application 有特別邏輯,所以需要另外處理
if (TAG_APPLICATION.equals(tagName)) {
if (foundApp) {
... 錯誤,一個應用只能有以個 Application 標籤
} else {
foundApp = true;
result = parseBaseApplication(input, pkg, res, parser, flags);
}
} else {
result = parseBaseApkTag(tagName, input, pkg, res, parser, flags);
}
if (result.isError()) {
return input.error(result);
}
}
... 省略部分
return input.success(pkg);
}
```
* 以下我們來看看它是如何解析 `activity` 標籤
```java=
// ParsingPackageUtils.java
private ParseResult<ParsingPackage> parseBaseApplication(ParseInput input,
ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags)
throws XmlPullParserException, IOException {
// 包名
final String pkgName = pkg.getPackageName();
// 目標 SDK 版本
int targetSdk = pkg.getTargetSdkVersion();
...
final int depth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG
|| parser.getDepth() > depth)) {
if (type != XmlPullParser.START_TAG) {
continue;
}
final ParseResult result;
String tagName = parser.getName();
boolean isActivity = false;
switch (tagName) {
case "activity":
isActivity = true;
// fall-through
case "receiver":
// 從這裡可以看出 activity、receive 的解析是同一個 function
ParseResult<ParsedActivity> activityResult =
ParsedActivityUtils.parseActivityOrReceiver(mSeparateProcesses, pkg,
res, parser, flags, sUseRoundIcon, input);
if (activityResult.isSuccess()) {
ParsedActivity activity = activityResult.getResult();
if (isActivity) {
hasActivityOrder |= (activity.getOrder() != 0);
pkg.addActivity(activity);
} else {
hasReceiverOrder |= (activity.getOrder() != 0);
pkg.addReceiver(activity);
}
}
result = activityResult;
break;
}
}
```
### [ParsingPackageImpl](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/content/pm/parsing/ParsingPackageImpl.java) - 儲存 Package 訊息
* Android 組件解析關係圖,解析的實作交給 Parsed 實作類 (`ParsedActivity`、`ParsedService`... 等等)
> 
* ParsingPackageImpl 類負責儲存 APK 最終分析的結果,其中就包含了 4 大組件、 packageName、versionCode... 等等訊息
1. 每個組件中都含有 xxxInfo 數據、該 Info 才是該組件的數據
> Activity 內會有 [**ActivityInfo**](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/content/pm/ActivityInfo.java)
> Service 內會有 [**ServiceInfo**](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/content/pm/ServiceInfo.java)
2. 四大組件的標籤內有包含 `<intent-filter\>`,用來過濾 Intent 訊息,其 **Package 結果也會保存在 ParsingPackageImpl 中** (透過 ParsedIntentInfoUtils 類解析)
:::info
其實就是保存了 **AndroidManifest.xml** 的訊息
:::
```java=
// ParsingPackageImpl.java
public class ParsingPackageImpl implements ParsingPackage, Parcelable {
private static final String TAG = "PackageImpl";
...
// Package 基本資料
protected int versionCode;
protected int versionCodeMajor;
private int baseRevisionCode;
@Nullable
@DataClass.ParcelWith(ForInternedString.class)
private String versionName;
private int compileSdkVersion;
@Nullable
@DataClass.ParcelWith(ForInternedString.class)
private String compileSdkVersionCodeName;
@NonNull
@DataClass.ParcelWith(ForInternedString.class)
protected String packageName;
@Nullable
@DataClass.ParcelWith(ForInternedString.class)
private String realPackage;
@NonNull
protected String mBaseApkPath;
// 四大組件 + 權限
@NonNull
protected List<ParsedActivity> activities = emptyList();
@NonNull
protected List<ParsedActivity> receivers = emptyList();
@NonNull
protected List<ParsedService> services = emptyList();
@NonNull
protected List<ParsedProvider> providers = emptyList();
@NonNull
private List<ParsedAttribution> attributions = emptyList();
@NonNull
protected List<ParsedPermission> permissions = emptyList();
@NonNull
protected List<ParsedPermissionGroup> permissionGroups = emptyList();
@NonNull
protected List<ParsedInstrumentation> instrumentations = emptyList();
// Intent info 訊息
@NonNull
@DataClass.ParcelWith(ParsedIntentInfo.ListParceler.class)
private List<Pair<String, ParsedIntentInfo>> preferredActivityFilters = emptyList();
@NonNull
private Map<String, ParsedProcess> processes = emptyMap();
... 省略部份
}
```
:::success
* 最終 Parser 完畢的 APK 訊息會同步到 PackageManagerService 中保存
:::
## 更多的物件導向設計
物件導向的設計基礎如下,如果是初學者或是不熟悉的各位,建議可以從這些基礎開始認識,打好基底才能走個更穩(在學習的時候也需要不斷回頭看)!
:::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 設計模式` `基礎進階`