mirror of
https://github.com/tiennm99/java-design-patterns.git
synced 2026-09-13 00:19:13 +00:00
feat: translage to traditional Chinese (zh-tw) (#3324)
* 修改完成 * Add Singleton pattern example
This commit is contained in:
@@ -11,7 +11,8 @@
|
||||
|
||||
<br/>
|
||||
|
||||
Read in different language : [**zh**](localization/zh/README.md), [**ko**](localization/ko/README.md), [**fr**](localization/fr/README.md), [**tr**](localization/tr/README.md), [**ar**](localization/ar/README.md), [**es**](localization/es/README.md), [**pt**](localization/pt/README.md), [**id**](localization/id/README.md), [**ru**](localization/ru/README.md), [**de**](localization/de/README.md), [**ja**](localization/ja/README.md), [**vi**](localization/vi/README.md), [**bn**](localization/bn/README.md), [**np**](localization/ne/README.md), [**it**](localization/it/README.md), [**da**](localization/da/README.md)
|
||||
Read in different language : [**zh**](localization/zh/README.md), [**zh-TW**](localization/zh-TW/README.md)
|
||||
,[**ko**](localization/ko/README.md), [**fr**](localization/fr/README.md), [**tr**](localization/tr/README.md), [**ar**](localization/ar/README.md), [**es**](localization/es/README.md), [**pt**](localization/pt/README.md), [**id**](localization/id/README.md), [**ru**](localization/ru/README.md), [**de**](localization/de/README.md), [**ja**](localization/ja/README.md), [**vi**](localization/vi/README.md), [**bn**](localization/bn/README.md), [**np**](localization/ne/README.md), [**it**](localization/it/README.md), [**da**](localization/da/README.md)
|
||||
<br/>
|
||||
|
||||
# Introduction
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
<!-- the line below needs to be an empty line C: (its because kramdown isnt
|
||||
that smart and dearly wants an empty line before a heading to be able to
|
||||
display it as such, e.g. website) -->
|
||||
|
||||
# 設計模式Java版
|
||||
|
||||
 [](https://raw.githubusercontent.com/iluwatar/java-design-patterns/master/LICENSE.md) [](https://gitter.im/iluwatar/java-design-patterns?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [](https://sonarcloud.io/dashboard?id=iluwatar_java-design-patterns)
|
||||
|
||||
# 介紹
|
||||
|
||||
設計模式是程式設計師在設計應用程式或系統時,可以用來解決常見問題的最佳且具形式化的實踐方法。
|
||||
|
||||
設計模式能透過提供經過測試與驗證的開發範式,加快開發流程。
|
||||
|
||||
重複使用設計模式有助於避免那些可能導致重大問題的細微錯誤,同時也能提升程式碼的可讀性,讓熟悉這些模式的開發人員與架構師更容易理解。
|
||||
|
||||
# 入門
|
||||
|
||||
這個網站展示了 Java 設計模式。這些解決方案由開放原始碼社群中有經驗的程式設計師與架構師所開發。使用者可以透過高層次的描述來瀏覽這些模式,或是直接查看其原始碼。程式碼範例都有詳細的註解,可以視為實作特定設計模式的教學範本。我們使用的是最受歡迎且經過實戰驗證的開放原始碼 Java 技術。
|
||||
|
||||
在深入學習這些內容之前,你應該先熟悉各種[軟體設計原則](https://java-design-patterns.com/principles/).
|
||||
|
||||
所有設計都應盡可能簡單。建議先從 KISS(Keep It Simple, Stupid)、YAGNI(You Aren’t Gonna Need It)以及 Do The Simplest Thing That Could Possibly Work 這些原則開始。只有在實際需要擴充性時,才應該引入複雜性與設計模式。
|
||||
|
||||
熟悉了這些概念,就可以透過以下任一種方式深入研究可用的設計模式:
|
||||
|
||||
- 依名稱搜尋特定模式。找不到的話?請[在這裡](https://github.com/iluwatar/java-design-patterns/issues)回報新模式。
|
||||
- 使用難度標籤: `Performance(效能)` , `Gang of Four(四人幫)`和`Data access(資料存取)` 。
|
||||
- 使用模式分類`Creational(建立型)` ,`Behavioral(行為型)`和其他类别。
|
||||
|
||||
|
||||
希望你能在這個網站上找到對你的架構有幫助的物件導向解決方案,並且在學習的過程中和我們開發這些方案時一樣感到愉快。
|
||||
|
||||
# 如何做出貢獻
|
||||
|
||||
如果你願意為這個專案做出貢獻,你可以在我們的[開發者 Wiki](https://github.com/iluwatar/java-design-patterns/wiki)找到相關資訊。我們也會在[Gitter聊天室](https://gitter.im/iluwatar/java-design-patterns)中協助你並回答你的問題。
|
||||
|
||||
# 許可證
|
||||
|
||||
該項目使用麻省理工學院許可證。
|
||||
|
||||
|
||||
+24
-22
@@ -2,34 +2,34 @@
|
||||
title: Abstract Document
|
||||
shortTitle: Abstract Document
|
||||
category: Structural
|
||||
language: zh
|
||||
language: zh-TW
|
||||
tag:
|
||||
- Extensibility
|
||||
---
|
||||
|
||||
## 目的
|
||||
|
||||
使用动态属性,并在保持类型安全的同时实现非类型化语言的灵活性。
|
||||
使用動態屬性,並在保持型別安全的同時實現非型別化語言的靈活性。
|
||||
|
||||
## 解释
|
||||
## 解釋
|
||||
|
||||
抽象文档模式使您能够处理其他非静态属性。 此模式使用特征的概念来实现类型安全,并将不同类的属性分离为一组接口。
|
||||
抽象文件模式 (Abstract Document Pattern) 使您能夠處理其他非靜態屬性。此模式使用特性 (trait) 的概念來實現型別安全,並將不同類別的屬性分離為一組介面。
|
||||
|
||||
真实世界例子
|
||||
真實世界範例
|
||||
|
||||
> 考虑由多个部分组成的汽车。 但是,我们不知道特定汽车是否真的拥有所有零件,或者仅仅是零件中的一部分。 我们的汽车是动态而且非常灵活的。
|
||||
> 考慮由多個部分組成的汽車。但是,我們不知道特定汽車是否真的擁有所有零件,或者僅僅是零件中的一部分。我們的汽車是動態而且非常靈活的。
|
||||
|
||||
通俗的说
|
||||
簡單的說
|
||||
|
||||
> 抽象文档模式允许在对象不知道的情况下将属性附加到对象。
|
||||
> 抽象文件模式允許在物件不知道的情況下將屬性附加到物件上。
|
||||
|
||||
维基百科说
|
||||
維基百科說
|
||||
|
||||
> 面向对象的结构设计模式,用于组织松散类型的键值存储中的对象并使用类型化的视图公开数据。 该模式的目的是在强类型语言中实现组件之间的高度灵活性,在这种语言中,可以在不丢失类型安全支持的情况下,将新属性动态地添加到对象树中。 该模式利用特征将类的不同属性分成不同的接口。
|
||||
> 物件導向的結構設計模式,用於組織鬆散型別的鍵值儲存中的物件,並使用型別化的視圖公開資料。該模式的目的是在強型別語言中實現元件之間的高度靈活性,在這種語言中,可以在不遺失型別安全支援的情況下,將新屬性動態地新增到物件樹中。該模式利用特性 (trait) 將類別的不同屬性分成不同的介面。
|
||||
|
||||
**程序示例**
|
||||
**程式碼範例**
|
||||
|
||||
让我们首先定义基类`Document`和`AbstractDocument`。 它们基本上使对象拥有属性映射和任意数量的子对象。
|
||||
讓我們先定義基礎類別`Document`和`AbstractDocument`。 它們基本上使物件擁有屬性對映 (map) 和任意數量的子物件。
|
||||
|
||||
```java
|
||||
public interface Document {
|
||||
@@ -74,7 +74,7 @@ public abstract class AbstractDocument implements Document {
|
||||
...
|
||||
}
|
||||
```
|
||||
接下来,我们定义一个枚举“属性”和一组类型,价格,模型和零件的接口。 这使我们能够为Car类创建静态外观的界面。
|
||||
接下來,我們定義一個列舉 `Property` 和一組關於型別、價格、模型和零件的介面。這使我們能夠為 `Car` 類別創建靜態外觀的介面。
|
||||
|
||||
```java
|
||||
public enum Property {
|
||||
@@ -110,7 +110,7 @@ public interface HasParts extends Document {
|
||||
}
|
||||
```
|
||||
|
||||
现在我们准备介绍`Car`。
|
||||
現在我們準備介紹`Car`。
|
||||
|
||||
```java
|
||||
public class Car extends AbstractDocument implements HasModel, HasPrice, HasParts {
|
||||
@@ -121,7 +121,7 @@ public class Car extends AbstractDocument implements HasModel, HasPrice, HasPart
|
||||
}
|
||||
```
|
||||
|
||||
最后是完整示例中的`Car`构造和使用方式。
|
||||
最後是完整範例中 `Car`的建構和使用方式。。
|
||||
|
||||
```java
|
||||
LOGGER.info("Constructing parts and car");
|
||||
@@ -162,19 +162,21 @@ public class Car extends AbstractDocument implements HasModel, HasPrice, HasPart
|
||||
// door/Lambo/300
|
||||
```
|
||||
|
||||
## 类图
|
||||
## 類別圖
|
||||
|
||||

|
||||
|
||||
## 适用性
|
||||
## 適用性
|
||||
|
||||
使用抽象文档模式当
|
||||
在以下情況使用抽象文件模式:
|
||||
|
||||
* 需要即时添加新属性
|
||||
* 你想要一种灵活的方式来以树状结构组织域
|
||||
* 你想要更宽松的耦合系统
|
||||
* 需要即時新增新屬性時
|
||||
|
||||
## 鸣谢
|
||||
* 想要一種靈活的方式以樹狀結構組織領域 (domain) 時
|
||||
|
||||
* 想要一個更鬆散耦合的系統時
|
||||
|
||||
## 參考資料和來源
|
||||
|
||||
* [Wikipedia: Abstract Document Pattern](https://en.wikipedia.org/wiki/Abstract_Document_Pattern)
|
||||
* [Martin Fowler: Dealing with properties](http://martinfowler.com/apsupp/properties.pdf)
|
||||
|
Before Width: | Height: | Size: 94 KiB After Width: | Height: | Size: 94 KiB |
@@ -0,0 +1,229 @@
|
||||
---
|
||||
title: Abstract Factory
|
||||
shortTitle: Abstract Factory
|
||||
category: Structural
|
||||
language: zh-TW
|
||||
tag:
|
||||
- Extensibility
|
||||
---
|
||||
|
||||
## 別稱
|
||||
|
||||
* Kit (套件)
|
||||
|
||||
## 抽象工廠設計模式的意圖
|
||||
|
||||
Java 中的抽象工廠模式 (Abstract Factory pattern) 提供一個介面,用於創建一系列相關或相依的物件家族,而無需指定其具體類別,從而增強了軟體設計的模組化與靈活性。
|
||||
|
||||
## 抽象工廠模式的詳細解釋與真實世界範例
|
||||
|
||||
真實世界範例
|
||||
|
||||
> 想像一家傢俱公司,它使用 Java 中的抽象工廠模式來生產多種風格的傢俱:現代風、維多利亞風和鄉村風。每種風格都包含椅子、桌子和沙發等產品。為了確保每種風格內部的一致性,該公司使用了抽象工廠模式。
|
||||
>
|
||||
> 在這個情境中,抽象工廠是一個用於創建相關傢俱物件家族(椅子、桌子、沙發)的介面。每個具體工廠(ModernFurnitureFactory、VictorianFurnitureFactory、RusticFurnitureFactory)都實作了這個抽象工廠介面,並創建一組符合特定風格的產品。如此一來,客戶端就可以創建一整套現代或維多利亞風格的傢俱,而無需擔心其實例化的細節。這不僅保持了風格的一致性,也讓更換不同風格的傢俱變得輕而易舉。
|
||||
|
||||
簡單來說
|
||||
|
||||
> 工廠中的工廠;一個將個別但相關/相依的工廠群組在一起,而無需指定其具體類別的工廠。
|
||||
|
||||
維基百科說
|
||||
|
||||
> 抽象工廠模式提供了一種方法,可以封裝一組具有共通主題的獨立工廠,而無需指定它們的具體類別。
|
||||
|
||||
類別圖
|
||||
|
||||

|
||||
|
||||
## Java 中的抽象工廠程式碼範例
|
||||
|
||||
為了使用抽象工廠模式來創建一個王國,我們需要具有共通主題的物件。精靈王國需要精靈國王、精靈城堡和精靈軍隊;而獸人王國則需要獸人國王、獸人城堡和獸人軍隊。王國中的這些物件之間存在著相依性。
|
||||
|
||||
將上述的王國範例轉換為程式碼。首先,我們為王國中的物件定義一些介面和實作。
|
||||
|
||||
```java
|
||||
public interface Castle {
|
||||
String getDescription();
|
||||
}
|
||||
|
||||
public interface King {
|
||||
String getDescription();
|
||||
}
|
||||
|
||||
public interface Army {
|
||||
String getDescription();
|
||||
}
|
||||
|
||||
// Elven implementations ->
|
||||
public class ElfCastle implements Castle {
|
||||
static final String DESCRIPTION = "This is the elven castle!";
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return DESCRIPTION;
|
||||
}
|
||||
}
|
||||
|
||||
public class ElfKing implements King {
|
||||
static final String DESCRIPTION = "This is the elven king!";
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return DESCRIPTION;
|
||||
}
|
||||
}
|
||||
|
||||
public class ElfArmy implements Army {
|
||||
static final String DESCRIPTION = "This is the elven Army!";
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return DESCRIPTION;
|
||||
}
|
||||
}
|
||||
|
||||
// Orcish implementations similarly -> ...
|
||||
```
|
||||
|
||||
接著,我們為王國工廠定義抽象和實作。
|
||||
|
||||
```java
|
||||
public interface KingdomFactory {
|
||||
Castle createCastle();
|
||||
|
||||
King createKing();
|
||||
|
||||
Army createArmy();
|
||||
}
|
||||
|
||||
public class ElfKingdomFactory implements KingdomFactory {
|
||||
|
||||
@Override
|
||||
public Castle createCastle() {
|
||||
return new ElfCastle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public King createKing() {
|
||||
return new ElfKing();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Army createArmy() {
|
||||
return new ElfArmy();
|
||||
}
|
||||
}
|
||||
|
||||
// 獸人 (Orcish) 的實作也類似 -> ...
|
||||
```
|
||||
現在,我們可以為我們不同的王國工廠設計一個工廠。在這個範例中,我們創建了 `FactoryMaker`,它負責返回 `ElfKingdomFactory` 或 O`OrcKingdomFactory` 的實例。客戶端可以使用 `FactoryMaker` 來創建所需的具體工廠,而該工廠又會生產出不同的具體物件(繼承自 `Army`, `King`, `Castle`)。在這個範例中,我們還使用了一個 enum 來參數化客戶端將請求哪種類型的王國工廠。
|
||||
|
||||
```java
|
||||
public static class FactoryMaker {
|
||||
|
||||
public enum KingdomType {
|
||||
ELF, ORC
|
||||
}
|
||||
|
||||
public static KingdomFactory makeFactory(KingdomType type) {
|
||||
return switch (type) {
|
||||
case ELF -> new ElfKingdomFactory();
|
||||
case ORC -> new OrcKingdomFactory();
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
這是我們範例應用程式的主函式:
|
||||
|
||||
```java
|
||||
LOGGER.info("elf kingdom");
|
||||
createKingdom(Kingdom.FactoryMaker.KingdomType.ELF);
|
||||
LOGGER.info(kingdom.getArmy().getDescription());
|
||||
LOGGER.info(kingdom.getCastle().getDescription());
|
||||
LOGGER.info(kingdom.getKing().getDescription());
|
||||
|
||||
LOGGER.info("orc kingdom");
|
||||
createKingdom(Kingdom.FactoryMaker.KingdomType.ORC);
|
||||
LOGGER.info(kingdom.getArmy().getDescription());
|
||||
LOGGER.info(kingdom.getCastle().getDescription());
|
||||
LOGGER.info(kingdom.getKing().getDescription());
|
||||
```
|
||||
|
||||
程式輸出:
|
||||
|
||||
```
|
||||
07:35:46.340 [main] INFO com.iluwatar.abstractfactory.App -- elf kingdom
|
||||
07:35:46.343 [main] INFO com.iluwatar.abstractfactory.App -- This is the elven army!
|
||||
07:35:46.343 [main] INFO com.iluwatar.abstractfactory.App -- This is the elven castle!
|
||||
07:35:46.343 [main] INFO com.iluwatar.abstractfactory.App -- This is the elven king!
|
||||
07:35:46.343 [main] INFO com.iluwatar.abstractfactory.App -- orc kingdom
|
||||
07:35:46.343 [main] INFO com.iluwatar.abstractfactory.App -- This is the orc army!
|
||||
07:35:46.343 [main] INFO com.iluwatar.abstractfactory.App -- This is the orc castle!
|
||||
07:35:46.343 [main] INFO com.iluwatar.abstractfactory.App -- This is the orc king!
|
||||
```
|
||||
|
||||
## 在 Java 中何時使用抽象工廠模式
|
||||
在 Java 中,當您遇到以下情況時,請使用抽象工廠模式::
|
||||
|
||||
* 系統應與其產品的創建、組合和表示方式無關。
|
||||
|
||||
* 您需要用多個產品家族中的其中一個來配置系統。
|
||||
|
||||
* 必須同時使用一系列相關的產品物件,以強制保持一致性。
|
||||
|
||||
* 您希望提供一個產品的類別庫,只暴露它們的介面,而不是它們的實作。
|
||||
|
||||
* 相依物件的生命週期比消費者的生命週期短。
|
||||
|
||||
* 相依物件需要使用執行時期的值或參數來建構。
|
||||
|
||||
* 您需要在執行時期從一個家族中選擇要使用的產品。
|
||||
|
||||
* 新增新產品或家族時,不應要求修改現有程式碼。
|
||||
|
||||
## Java 抽象工廠模式教學
|
||||
|
||||
* [Abstract Factory Design Pattern in Java (DigitalOcean)](https://www.digitalocean.com/community/tutorials/abstract-factory-design-pattern-in-java)
|
||||
* [Abstract Factory(Refactoring Guru)](https://refactoring.guru/design-patterns/abstract-factory)
|
||||
|
||||
## 抽象工廠模式的優點與權衡
|
||||
|
||||
優點:
|
||||
|
||||
* 靈活性:無需修改程式碼即可輕鬆切換產品家族。
|
||||
|
||||
* 解耦:客戶端程式碼只與抽象介面互動,提升了可移植性和可維護性。
|
||||
|
||||
* 可重用性:抽象工廠和產品有助於跨專案的元件重用。
|
||||
|
||||
* 可維護性:對個別產品家族的變更是局部的,簡化了更新過程。
|
||||
|
||||
權衡:
|
||||
|
||||
* 複雜性:定義抽象介面和具體工廠會增加初期的開發開銷。
|
||||
|
||||
* 間接性:客戶端程式碼透過工廠間接與產品互動,可能會降低透明度。
|
||||
|
||||
## Java 中抽象工廠模式的實際應用
|
||||
|
||||
* Java Swing 的 `LookAndFeel` 類別,用於提供不同的外觀與感覺選項。
|
||||
|
||||
* Java Abstract Window Toolkit (AWT) 中的各種實作,用於創建不同的 GUI 元件。
|
||||
|
||||
* [javax.xml.parsers.DocumentBuilderFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/parsers/DocumentBuilderFactory.html)
|
||||
* [javax.xml.transform.TransformerFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/transform/TransformerFactory.html#newInstance--)
|
||||
* [javax.xml.xpath.XPathFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/xpath/XPathFactory.html#newInstance--)
|
||||
|
||||
## 相關的 Java 設計模式
|
||||
|
||||
* [Factory Method](https://java-design-patterns.com/patterns/factory-method/): 抽象工廠使用工廠方法來創建產品。
|
||||
* [Singleton](https://java-design-patterns.com/patterns/singleton/): 抽象工廠類別通常被實作為單例
|
||||
* [Factory Kit](https://java-design-patterns.com/patterns/factory-kit/): 與抽象工廠類似,但更專注於以靈活的方式配置和管理一組相關物件。
|
||||
|
||||
## 參考資料與致謝
|
||||
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
|
||||
* [Design Patterns in Java](https://amzn.to/3Syw0vC)
|
||||
* [Head First Design Patterns: Building Extensible and Maintainable Object-Oriented Software](https://amzn.to/49NGldq)
|
||||
* [Java Design Patterns: A Hands-On Experience with Real-World Examples](https://amzn.to/3HWNf4U)
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 80 KiB |
@@ -0,0 +1,101 @@
|
||||
@startuml
|
||||
package com.iluwatar.abstractfactory {
|
||||
class App {
|
||||
- LOGGER : Logger {static}
|
||||
- army : Army
|
||||
- castle : Castle
|
||||
- king : King
|
||||
+ App()
|
||||
+ createKingdom(factory : KingdomFactory)
|
||||
+ getArmy() : Army
|
||||
~ getArmy(factory : KingdomFactory) : Army
|
||||
+ getCastle() : Castle
|
||||
~ getCastle(factory : KingdomFactory) : Castle
|
||||
+ getKing() : King
|
||||
~ getKing(factory : KingdomFactory) : King
|
||||
+ main(args : String[]) {static}
|
||||
- setArmy(army : Army)
|
||||
- setCastle(castle : Castle)
|
||||
- setKing(king : King)
|
||||
}
|
||||
class FactoryMaker {
|
||||
+ FactoryMaker()
|
||||
+ makeFactory(type : KingdomType) : KingdomFactory {static}
|
||||
}
|
||||
enum KingdomType {
|
||||
+ ELF {static}
|
||||
+ ORC {static}
|
||||
+ valueOf(name : String) : KingdomType {static}
|
||||
+ values() : KingdomType[] {static}
|
||||
}
|
||||
interface Army {
|
||||
+ getDescription() : String {abstract}
|
||||
}
|
||||
interface Castle {
|
||||
+ getDescription() : String {abstract}
|
||||
}
|
||||
class ElfArmy {
|
||||
~ DESCRIPTION : String {static}
|
||||
+ ElfArmy()
|
||||
+ getDescription() : String
|
||||
}
|
||||
class ElfCastle {
|
||||
~ DESCRIPTION : String {static}
|
||||
+ ElfCastle()
|
||||
+ getDescription() : String
|
||||
}
|
||||
class ElfKing {
|
||||
~ DESCRIPTION : String {static}
|
||||
+ ElfKing()
|
||||
+ getDescription() : String
|
||||
}
|
||||
class ElfKingdomFactory {
|
||||
+ ElfKingdomFactory()
|
||||
+ createArmy() : Army
|
||||
+ createCastle() : Castle
|
||||
+ createKing() : King
|
||||
}
|
||||
interface King {
|
||||
+ getDescription() : String {abstract}
|
||||
}
|
||||
interface KingdomFactory {
|
||||
+ createArmy() : Army {abstract}
|
||||
+ createCastle() : Castle {abstract}
|
||||
+ createKing() : King {abstract}
|
||||
}
|
||||
class OrcArmy {
|
||||
~ DESCRIPTION : String {static}
|
||||
+ OrcArmy()
|
||||
+ getDescription() : String
|
||||
}
|
||||
class OrcCastle {
|
||||
~ DESCRIPTION : String {static}
|
||||
+ OrcCastle()
|
||||
+ getDescription() : String
|
||||
}
|
||||
class OrcKing {
|
||||
~ DESCRIPTION : String {static}
|
||||
+ OrcKing()
|
||||
+ getDescription() : String
|
||||
}
|
||||
class OrcKingdomFactory {
|
||||
+ OrcKingdomFactory()
|
||||
+ createArmy() : Army
|
||||
+ createCastle() : Castle
|
||||
+ createKing() : King
|
||||
}
|
||||
}
|
||||
KingdomType ..+ FactoryMaker
|
||||
App --> "-castle" Castle
|
||||
FactoryMaker ..+ App
|
||||
App --> "-king" King
|
||||
App --> "-army" Army
|
||||
ElfArmy ..|> Army
|
||||
ElfCastle ..|> Castle
|
||||
ElfKing ..|> King
|
||||
ElfKingdomFactory ..|> KingdomFactory
|
||||
OrcArmy ..|> Army
|
||||
OrcCastle ..|> Castle
|
||||
OrcKing ..|> King
|
||||
OrcKingdomFactory ..|> KingdomFactory
|
||||
@enduml
|
||||
@@ -0,0 +1,123 @@
|
||||
---
|
||||
title: Active Object
|
||||
shortTitle: Active Object
|
||||
category: Concurrency
|
||||
language: zh-TW
|
||||
tag:
|
||||
- Performance
|
||||
---
|
||||
|
||||
|
||||
## 目的
|
||||
主動式物件設計模式 (Active Object Design Pattern) 將物件的方法執行與方法呼叫脫鉤,每個物件都駐留在其自身的控制執行緒中。其目的是透過使用非同步方法呼叫和用於處理請求的排程器來引入並行性 (concurrency)。
|
||||
|
||||
## 解釋
|
||||
|
||||
實作主動式物件模式的類別將包含自我同步機制,而無需使用 synchronized 方法。
|
||||
|
||||
真實世界範例
|
||||
|
||||
>獸人以其野性和頑強的靈魂而著稱。牠們的行為似乎由一個獨立的控制執行緒所驅動,該執行緒基於先前的動作做出反應。
|
||||
|
||||
為了實作出一個擁有自身控制執行緒、僅公開其 API 而不暴露其執行細節的物件,我們可以使用主動式物件模式。
|
||||
|
||||
**程序示例**
|
||||
|
||||
```java
|
||||
public abstract class ActiveCreature{
|
||||
private final Logger logger = LoggerFactory.getLogger(ActiveCreature.class.getName());
|
||||
|
||||
private BlockingQueue<Runnable> requests;
|
||||
|
||||
private String name;
|
||||
|
||||
private Thread thread;
|
||||
|
||||
public ActiveCreature(String name) {
|
||||
this.name = name;
|
||||
this.requests = new LinkedBlockingQueue<Runnable>();
|
||||
thread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
try {
|
||||
requests.take().run();
|
||||
} catch (InterruptedException e) {
|
||||
logger.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
thread.start();
|
||||
}
|
||||
|
||||
public void eat() throws InterruptedException {
|
||||
requests.put(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
logger.info("{} is eating!",name());
|
||||
logger.info("{} has finished eating!",name());
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public void roam() throws InterruptedException {
|
||||
requests.put(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
logger.info("{} has started to roam and the wastelands.",name());
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return this.name;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
我們可以看到,任何繼承 ActiveCreature 的類別都將擁有自己的控制執行緒來執行和呼叫方法。
|
||||
|
||||
如,Orc (獸人) 類別:
|
||||
|
||||
```java
|
||||
public class Orc extends ActiveCreature {
|
||||
|
||||
public Orc(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
現在,我們可以建立多個生物 (例如獸人),命令牠們進食和漫遊,然後牠們將在各自的控制執行緒上執行這些動作:
|
||||
|
||||
```java
|
||||
public static void main(String[] args) {
|
||||
var app = new App();
|
||||
app.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
ActiveCreature creature;
|
||||
try {
|
||||
for (int i = 0;i < creatures;i++) {
|
||||
creature = new Orc(Orc.class.getSimpleName().toString() + i);
|
||||
creature.eat();
|
||||
creature.roam();
|
||||
}
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
logger.error(e.getMessage());
|
||||
}
|
||||
Runtime.getRuntime().exit(1);
|
||||
}
|
||||
```
|
||||
|
||||
## 類別圖
|
||||
|
||||

|
||||
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
@@ -0,0 +1,193 @@
|
||||
---
|
||||
title: Actor Model
|
||||
shortTitle: Actor Model
|
||||
category: Concurrency
|
||||
language: zh-TW
|
||||
tag:
|
||||
- Performance
|
||||
---
|
||||
|
||||
## 亦稱為
|
||||
|
||||
- 訊息傳遞並行 (Message-passing concurrency)
|
||||
|
||||
- 基於 Actor 的並行 (Actor-based concurrency)
|
||||
|
||||
---
|
||||
|
||||
## Actor Model 模式的意圖
|
||||
Actor Model 模式利用隔離的元件 (actor),透過非同步訊息傳遞進行互動,來建構高並行、分散式和容錯的系統。
|
||||
|
||||
---
|
||||
|
||||
## Actor Model 模式的詳細解釋與真實世界範例
|
||||
|
||||
### 📦 現實世界範例
|
||||
|
||||
想像一個客戶服務系統:
|
||||
- 每一位 **客戶支援專員** 都是一個**actor**.
|
||||
- 客戶 **傳送問題 (訊息) ** 給專員。
|
||||
- 每位專員一次處理一個請求,並且能夠 **非同步地回覆** 而不會干擾到其他專員。
|
||||
|
||||
---
|
||||
|
||||
### 🧠 白話解釋
|
||||
|
||||
> 「Actors 就像是獨立的工人,從不共享記憶體,只透過訊息來溝通。」
|
||||
|
||||
---
|
||||
|
||||
### 📖 維基百科說
|
||||
|
||||
> [Actor model](https://en.wikipedia.org/wiki/Actor_model) 是一種並行運算的數學模型,將「actor」視為並行運算的普世基本元素。
|
||||
|
||||
---
|
||||
|
||||
### 🧹 架構圖
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## Java 的 Actor Model 模式程式範例
|
||||
|
||||
### Actor.java
|
||||
|
||||
```java
|
||||
public abstract class Actor implements Runnable {
|
||||
|
||||
@Setter @Getter private String actorId;
|
||||
private final BlockingQueue<Message> mailbox = new LinkedBlockingQueue<>();
|
||||
private volatile boolean active = true;
|
||||
|
||||
|
||||
public void send(Message message) {
|
||||
mailbox.add(message);
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
active = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
}
|
||||
|
||||
protected abstract void onReceive(Message message);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Message.java
|
||||
|
||||
```java
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
public class Message {
|
||||
private final String content;
|
||||
private final String senderId;
|
||||
}
|
||||
```
|
||||
|
||||
### ActorSystem.java
|
||||
|
||||
```java
|
||||
public class ActorSystem {
|
||||
public void startActor(Actor actor) {
|
||||
String actorId = "actor-" + idCounter.incrementAndGet(); // Generate a new and unique ID
|
||||
actor.setActorId(actorId); // assign the actor it's ID
|
||||
actorRegister.put(actorId, actor); // Register and save the actor with it's ID
|
||||
executor.submit(actor); // Run the actor in a thread
|
||||
}
|
||||
public Actor getActorById(String actorId) {
|
||||
return actorRegister.get(actorId); // Find by Id
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
executor.shutdownNow(); // Stop all threads
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### App.java
|
||||
|
||||
```java
|
||||
public class App {
|
||||
public static void main(String[] args) {
|
||||
ActorSystem system = new ActorSystem();
|
||||
Actor srijan = new ExampleActor(system);
|
||||
Actor ansh = new ExampleActor2(system);
|
||||
|
||||
system.startActor(srijan);
|
||||
system.startActor(ansh);
|
||||
ansh.send(new Message("Hello ansh", srijan.getActorId()));
|
||||
srijan.send(new Message("Hello srijan!", ansh.getActorId()));
|
||||
|
||||
Thread.sleep(1000); // Give time for messages to process
|
||||
|
||||
srijan.stop(); // Stop the actor gracefully
|
||||
ansh.stop();
|
||||
system.shutdown(); // Stop the actor system
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 何時在 Java 中使用 Actor Model 模式
|
||||
- 建立 **並行或分散式系統**時
|
||||
- 您希望 **沒有共享的可變狀態**時
|
||||
- 你需要 **非同步、訊息驅動的通訊**時
|
||||
- 元件應 **隔離且鬆散耦合**
|
||||
|
||||
---
|
||||
|
||||
## Actor Model 模式的 Java 教學
|
||||
|
||||
- [Baeldung – Akka with Java](https://www.baeldung.com/java-akka)
|
||||
- [Vaughn Vernon – Reactive Messaging Patterns](https://vaughnvernon.co/?p=1143)
|
||||
|
||||
---
|
||||
|
||||
## Actor Model 模式的真實世界應用
|
||||
|
||||
- [Akka 框架](https://akka.io/)
|
||||
- [Erlang and Elixir concurrency](https://www.erlang.org/)
|
||||
- [Microsoft Orleans](https://learn.microsoft.com/en-us/dotnet/orleans/)
|
||||
- J基於 JVM 的遊戲引擎和模擬器。
|
||||
|
||||
---
|
||||
|
||||
## Actor Model 模式的優點與權衡
|
||||
|
||||
### ✅ 優點
|
||||
- 支援高並行性。
|
||||
- 易於跨執行緒或機器進行擴展。
|
||||
- 容錯隔離與恢復。
|
||||
- 保證 Actor 內部的訊息順序。
|
||||
|
||||
### ⚠️ 權衡
|
||||
- 由於非同步行為,偵錯更困難。
|
||||
- 由於訊息佇列,會有輕微的效能開銷。
|
||||
- 設計上比簡單的方法呼叫更複雜。
|
||||
---
|
||||
|
||||
## 相關的 Java 設計模式
|
||||
|
||||
- [Command Pattern](../command)
|
||||
- [Mediator Pattern](../mediator)
|
||||
- [Event-Driven Architecture](../event-driven-architecture)
|
||||
- [Observer Pattern](../observer)
|
||||
|
||||
---
|
||||
|
||||
## 參考資料與致謝
|
||||
|
||||
- *Programming Erlang*, Joe Armstrong
|
||||
- *Reactive Design Patterns*, Roland Kuhn
|
||||
- *The Actor Model in 10 Minutes*, [InfoQ Article](https://www.infoq.com/articles/actor-model/)
|
||||
- [Akka Documentation](https://doc.akka.io/docs/akka/current/index.html)
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
@@ -0,0 +1,35 @@
|
||||
@startuml actor-model
|
||||
|
||||
title Actor Model - UML Class Diagram
|
||||
|
||||
class ActorSystem {
|
||||
+actorOf(actor: Actor): Actor
|
||||
+shutdown(): void
|
||||
}
|
||||
|
||||
class Actor {
|
||||
-mailbox: BlockingQueue<Message>
|
||||
-active: boolean
|
||||
+send(message: Message): void
|
||||
+stop(): void
|
||||
+run(): void
|
||||
#onReceive(message: Message): void
|
||||
}
|
||||
|
||||
class ExampleActor {
|
||||
+onReceive(message: Message): void
|
||||
}
|
||||
|
||||
class Message {
|
||||
-content: String
|
||||
-sender: Actor
|
||||
+getContent(): String
|
||||
+getSender(): Actor
|
||||
}
|
||||
|
||||
ActorSystem --> Actor : creates
|
||||
Actor <|-- ExampleActor : extends
|
||||
Actor --> Message : processes
|
||||
ExampleActor --> Message : uses
|
||||
|
||||
@enduml
|
||||
Reference in New Issue
Block a user