diff --git a/.github/workflows/presubmit.yml b/.github/workflows/presubmit.yml index bb0ed7e116ae..800441be73fb 100644 --- a/.github/workflows/presubmit.yml +++ b/.github/workflows/presubmit.yml @@ -21,8 +21,8 @@ jobs: echo "Error: LLM_API_KEY secret is not configured" exit 1 fi - - uses: presubmit/ai-reviewer@latest + - uses: presubmit/ai-reviewer@v0.2.5 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} LLM_API_KEY: ${{ secrets.LLM_API_KEY }} - LLM_MODEL: "gpt-5-nano" \ No newline at end of file + # LLM_MODEL removed to avoid model-specific schema/validation issues diff --git a/localization/zh-TW/abstract-document/README.md b/localization/zh-TW/abstract-document/README.md index f71ff845a97f..33db74114f5e 100644 --- a/localization/zh-TW/abstract-document/README.md +++ b/localization/zh-TW/abstract-document/README.md @@ -13,11 +13,11 @@ tag: ## 解釋 -抽象文件模式 (Abstract Document Pattern) 使您能夠處理其他非靜態屬性。此模式使用特性 (trait) 的概念來實現型別安全,並將不同類別的屬性分離為一組介面。 +抽象文件模式 (Abstract Document Pattern) 使你能夠處理其他非靜態屬性。此模式使用特性 (trait) 的概念來實現型別安全,並將不同類別的屬性分離為一組介面。 真實世界範例 -> 考慮由多個部分組成的汽車。但是,我們不知道特定汽車是否真的擁有所有零件,或者僅僅是零件中的一部分。我們的汽車是動態而且非常靈活的。 +> 考慮由多個部分組成的汽車。但,我們不知道特定汽車是否真的擁有所有零件,或者僅僅是零件中的一部分。我們的汽車是動態而且非常靈活的。 簡單的說 diff --git a/localization/zh-TW/abstract-factory/README.md b/localization/zh-TW/abstract-factory/README.md index 414c1a8f0100..12e0e0332aa5 100644 --- a/localization/zh-TW/abstract-factory/README.md +++ b/localization/zh-TW/abstract-factory/README.md @@ -164,21 +164,21 @@ LOGGER.info(kingdom.getKing().getDescription()); ``` ## 在 Java 中何時使用抽象工廠模式 -在 Java 中,當您遇到以下情況時,請使用抽象工廠模式:: +在 Java 中,當你遇到以下情況時,請使用抽象工廠模式:: * 系統應與其產品的創建、組合和表示方式無關。 -* 您需要用多個產品家族中的其中一個來配置系統。 +* 你需要用多個產品家族中的其中一個來配置系統。 * 必須同時使用一系列相關的產品物件,以強制保持一致性。 -* 您希望提供一個產品的類別庫,只暴露它們的介面,而不是它們的實作。 +* 你希望提供一個產品的類別庫,只暴露它們的介面,而不是它們的實作。 * 相依物件的生命週期比消費者的生命週期短。 * 相依物件需要使用執行時期的值或參數來建構。 -* 您需要在執行時期從一個家族中選擇要使用的產品。 +* 你需要在執行時期從一個家族中選擇要使用的產品。 * 新增新產品或家族時,不應要求修改現有程式碼。 diff --git a/localization/zh-TW/actor-model/README.md b/localization/zh-TW/actor-model/README.md index 652696ce8931..5b4f047d0815 100644 --- a/localization/zh-TW/actor-model/README.md +++ b/localization/zh-TW/actor-model/README.md @@ -139,7 +139,7 @@ public class App { ## 何時在 Java 中使用 Actor Model 模式 - 建立 **並行或分散式系統**時 -- 您希望 **沒有共享的可變狀態**時 +- 你希望 **沒有共享的可變狀態**時 - 你需要 **非同步、訊息驅動的通訊**時 - 元件應 **隔離且鬆散耦合** diff --git a/localization/zh-TW/acyclic-visitor/README.md b/localization/zh-TW/acyclic-visitor/README.md new file mode 100644 index 000000000000..0e92526e6978 --- /dev/null +++ b/localization/zh-TW/acyclic-visitor/README.md @@ -0,0 +1,156 @@ +--- +title: Acyclic Visitor +shortTitle: Acyclic Visitor +category: Behavioral +language: zh +tag: + - Extensibility +--- + +## 目的 + +允許將新功能新增到現有的類層次結構中,而不會影響這些層次結構,也不會有四人幫訪客模式中那樣迴圈依賴的問題。 + +## 解釋 + +真實世界例子 + +> 我們有一個調變解調器類的層次結構。 需要使用基於過濾條件的外部演算法(是Unix或DOS相容的調變解調器)來訪問此層次結構中的調變解調器。 + +通俗地說 + +> 非迴圈訪問者允許將功能新增到現有的類層次結構中,而無需修改層次結構 + +[WikiWikiWeb](https://wiki.c2.com/?AcyclicVisitor) 上說 + +> 非迴圈訪客模式允許將新功能新增到現有的類層次結構中,而不會影響這些層次結構,也不會建立四人幫訪客模式中固有的迴圈依賴問題。 + +**程式示例** + +這是調變解調器的層次結構。 + +```java +public abstract class Modem { + public abstract void accept(ModemVisitor modemVisitor); +} + +public class Zoom extends Modem { + ... + @Override + public void accept(ModemVisitor modemVisitor) { + if (modemVisitor instanceof ZoomVisitor) { + ((ZoomVisitor) modemVisitor).visit(this); + } else { + LOGGER.info("Only ZoomVisitor is allowed to visit Zoom modem"); + } + } +} + +public class Hayes extends Modem { + ... + @Override + public void accept(ModemVisitor modemVisitor) { + if (modemVisitor instanceof HayesVisitor) { + ((HayesVisitor) modemVisitor).visit(this); + } else { + LOGGER.info("Only HayesVisitor is allowed to visit Hayes modem"); + } + } +} +``` + +下面我們介紹`調變解調器訪問者`類結構。 + +```java +public interface ModemVisitor { +} + +public interface HayesVisitor extends ModemVisitor { + void visit(Hayes hayes); +} + +public interface ZoomVisitor extends ModemVisitor { + void visit(Zoom zoom); +} + +public interface AllModemVisitor extends ZoomVisitor, HayesVisitor { +} + +public class ConfigureForDosVisitor implements AllModemVisitor { + ... + @Override + public void visit(Hayes hayes) { + LOGGER.info(hayes + " used with Dos configurator."); + } + @Override + public void visit(Zoom zoom) { + LOGGER.info(zoom + " used with Dos configurator."); + } +} + +public class ConfigureForUnixVisitor implements ZoomVisitor { + ... + @Override + public void visit(Zoom zoom) { + LOGGER.info(zoom + " used with Unix configurator."); + } +} +``` + +最後,這裡是訪問者的實踐。 + +```java + var conUnix = new ConfigureForUnixVisitor(); + var conDos = new ConfigureForDosVisitor(); + var zoom = new Zoom(); + var hayes = new Hayes(); + hayes.accept(conDos); + zoom.accept(conDos); + hayes.accept(conUnix); + zoom.accept(conUnix); +``` + +程式輸出: + +``` + // Hayes modem used with Dos configurator. + // Zoom modem used with Dos configurator. + // Only HayesVisitor is allowed to visit Hayes modem + // Zoom modem used with Unix configurator. +``` + +## 類圖 + +![alt text](./etc/acyclic-visitor.png "Acyclic Visitor") + +## 適用性 + +以下情況可以使用此模式: + +* 需要在現有層次結構中新增新功能而無需更改或影響該層次結構時。 +* 當某些功能在層次結構上執行,但不屬於層次結構本身時。 例如 ConfigureForDOS / ConfigureForUnix / ConfigureForX問題。 +* 當你需要根據物件的型別對物件執行非常不同的操作時。 +* 當訪問的類層次結構將經常使用元素類的新派生進行擴充套件時。 +* 當重新編譯,重新連結,重新測試或重新分發派生元素非常昂貴時。 + +## 結果 + +好處: + +* 類層次結構之間沒有依賴關係迴圈。 +* 如果新增了新訪客,則無需重新編譯所有訪客。 +* 如果類層次結構具有新成員,則不會導致現有訪問者中的編譯失敗。 + +壞處: + +* 透過證明它可以接受所有訪客,但實際上僅對特定訪客感興趣,從而違反了[Liskov的替代原則](https://java-design-patterns.com/principles/#liskov-substitution-principle) +* 必須為可訪問的類層次結構中的所有成員建立訪問者的並行層次結構。 + +## 相關的模式 + +* [Visitor Pattern](https://java-design-patterns.com/patterns/visitor/) + +## 鳴謝 + +* [Acyclic Visitor by Robert C. Martin](http://condor.depaul.edu/dmumaugh/OOT/Design-Principles/acv.pdf) +* [Acyclic Visitor in WikiWikiWeb](https://wiki.c2.com/?AcyclicVisitor) diff --git a/localization/zh-TW/acyclic-visitor/etc/acyclic-visitor.png b/localization/zh-TW/acyclic-visitor/etc/acyclic-visitor.png new file mode 100644 index 000000000000..7b4df13d80f8 Binary files /dev/null and b/localization/zh-TW/acyclic-visitor/etc/acyclic-visitor.png differ diff --git a/localization/zh-TW/adapter/README.md b/localization/zh-TW/adapter/README.md new file mode 100644 index 000000000000..a9f6787b6b13 --- /dev/null +++ b/localization/zh-TW/adapter/README.md @@ -0,0 +1,136 @@ +--- +title: Adapter +shortTitle: Adapter +category: Structural +language: zh +tag: + - Gang of Four +--- + +## 又被稱為 +包裝器 + +## 目的 +將一個介面轉換成另一個客戶所期望的介面。介面卡讓那些本來因為介面不相容的類可以合作無間。 + +## 解釋 + +現實世界例子 + +> 考慮有這麼一種情況,在你的儲存卡中有一些照片,你想將其傳到你的電腦中。為了傳送資料,你需要某種能夠相容你電腦介面的介面卡以便你的儲存卡能連上你的電腦。在這種情況下,讀卡器就是一個介面卡。 +> 另一個例子就是註明的電源介面卡;三腳插頭不能插在兩腳插座上,需要一個電源介面卡來使其能夠插在兩腳插座上。 +> 還有一個例子就是翻譯官,他翻譯一個人對另一個人說的話。 + +用直白的話來說 + +> 介面卡模式讓你可以把不相容的物件包在介面卡中,以讓其相容其他類。 + +維基百科中說 + +> 在軟體工程中,介面卡模式是一種可以讓現有類的介面把其作為其他介面來使用的設計模式。它經常用來使現有的類和其他類能夠工作並且不用修改其他類的原始碼。 + +**程式設計樣例(物件介面卡)** + +假如有一個船長他只會划船,但不會航行。 + +首先我們有介面`RowingBoat`和`FishingBoat` + +```java +public interface RowingBoat { + void row(); +} + +@Slf4j +public class FishingBoat { + public void sail() { + LOGGER.info("The fishing boat is sailing"); + } +} +``` +船長希望有一個`RowingBoat`介面的實現,這樣就可以移動 + +```java +public class Captain { + + private final RowingBoat rowingBoat; + // default constructor and setter for rowingBoat + public Captain(RowingBoat rowingBoat) { + this.rowingBoat = rowingBoat; + } + + public void row() { + rowingBoat.row(); + } +} +``` + +現在海盜來了,我們的船長需要逃跑但是隻有一個漁船可用。我們需要建立一個可以讓船長使用其划船技能來操作漁船的介面卡。 + +```java +@Slf4j +public class FishingBoatAdapter implements RowingBoat { + + private final FishingBoat boat; + + public FishingBoatAdapter() { + boat = new FishingBoat(); + } + + @Override + public void row() { + boat.sail(); + } +} + +``` + +現在 `船長` 可以使用`FishingBoat`介面來逃離海盜了。 + +```java +var captain = new Captain(new FishingBoatAdapter()); +captain.row(); +``` + +## 類圖 +![alt text](./etc/adapter.urm.png "Adapter class diagram") + + +## 應用 +使用介面卡模式當 + +* 你想使用一個已有類,但是它的介面不能和你需要的所匹配 +* 你需要建立一個可重用類,該類與不相關或不可預見的類進行協作,即不一定具有相容介面的類 +* 你需要使用一些現有的子類,但是子類化他們每一個的子類來進行介面的適配是不現實的。一個物件介面卡可以適配他們父類的介面。 +* 大多數使用第三方類庫的應用使用介面卡作為一個在應用和第三方類庫間的中間層來使應用和類庫解耦。如果必須使用另一個庫,則只需使用一個新庫的介面卡而無需改變應用程式的程式碼。 + +## 後果: +類和物件介面卡有不同的權衡取捨。一個類介面卡 + +* 適配被適配者到目標介面,需要保證只有一個具體的被適配者類。作為結果,當我們想適配一個類和它所有的子類時,類介面卡將不會起作用。 +* 可以讓介面卡重寫一些被適配者的行為,因為介面卡是被適配者的子類。 +* 只引入了一個物件,並且不需要其他指標間接訪問被適配者。 + +物件介面卡 + +* 一個介面卡可以和許多被適配者工作,也就是被適配者自己和所有它的子類。介面卡同時可以為所有被適配者新增功能。 +* 覆蓋被適配者的行為變得更難。需要子類化被適配者然後讓介面卡引用這個子類不是被適配者。 + + +## 現實世界的案例 + +* [java.util.Arrays#asList()](http://docs.oracle.com/javase/8/docs/api/java/util/Arrays.html#asList%28T...%29) +* [java.util.Collections#list()](https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#list-java.util.Enumeration-) +* [java.util.Collections#enumeration()](https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#enumeration-java.util.Collection-) +* [javax.xml.bind.annotation.adapters.XMLAdapter](http://docs.oracle.com/javase/8/docs/api/javax/xml/bind/annotation/adapters/XmlAdapter.html#marshal-BoundType-) + + +## 鳴謝 + +* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59) +* [J2EE Design Patterns](https://www.amazon.com/gp/product/0596004273/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596004273&linkCode=as2&tag=javadesignpat-20&linkId=48d37c67fb3d845b802fa9b619ad8f31) +* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b) +* [Refactoring to Patterns](https://www.amazon.com/gp/product/0321213351/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321213351&linkCode=as2&tag=javadesignpat-20&linkId=2a76fcb387234bc71b1c61150b3cc3a7) + +``` + +``` \ No newline at end of file diff --git a/localization/zh-TW/adapter/etc/adapter.urm.png b/localization/zh-TW/adapter/etc/adapter.urm.png new file mode 100644 index 000000000000..341ad67699d9 Binary files /dev/null and b/localization/zh-TW/adapter/etc/adapter.urm.png differ diff --git a/localization/zh-TW/aggregator-microservices/README.md b/localization/zh-TW/aggregator-microservices/README.md new file mode 100644 index 000000000000..4c4c1d4887ac --- /dev/null +++ b/localization/zh-TW/aggregator-microservices/README.md @@ -0,0 +1,106 @@ +--- +title: Aggregator Microservices +shortTitle: Aggregator Microservices +category: Architectural +language: zh +tag: +- Cloud distributed +- Decoupling +- Microservices +--- + +## 意圖 + +使用者對聚合器服務進行一次呼叫,然後聚合器將呼叫每個相關的微服務。 + +## 解釋 + +真實世界例子 + +> 我們的網路市場需要有關產品及其當前庫存的資訊。 它呼叫聚合服務,聚合服務依次呼叫產品資訊微服務和產品庫存微服務,返回組合資訊。 + +通俗地說 + +> 聚合器微服務從各種微服務中收集資料,並返回一個聚合資料以進行處理。 + +Stack Overflow上說 + +> 聚合器微服務呼叫多個服務以實現應用程式所需的功能。 + +**程式示例** + +讓我們從資料模型開始。 這是我們的`產品`。 + +```java +public class Product { + private String title; + private int productInventories; + // getters and setters -> + ... +} +``` + +接下來,我們將介紹我們的聚合器微服務。 它包含用於呼叫相應微服務的客戶端`ProductInformationClient`和` ProductInventoryClient`。 + +```java +@RestController +public class Aggregator { + + @Resource + private ProductInformationClient informationClient; + + @Resource + private ProductInventoryClient inventoryClient; + + @RequestMapping(path = "/product", method = RequestMethod.GET) + public Product getProduct() { + + var product = new Product(); + var productTitle = informationClient.getProductTitle(); + var productInventory = inventoryClient.getProductInventories(); + + //Fallback to error message + product.setTitle(requireNonNullElse(productTitle, "Error: Fetching Product Title Failed")); + + //Fallback to default error inventory + product.setProductInventories(requireNonNullElse(productInventory, -1)); + + return product; + } +} +``` + +這是產品資訊微服務的精華實現。 庫存微服務類似,它只返回庫存計數。 + +```java +@RestController +public class InformationController { + @RequestMapping(value = "/information", method = RequestMethod.GET) + public String getProductTitle() { + return "The Product Title."; + } +} +``` + +Now calling our `Aggregator` REST API returns the product information. + +現在呼叫我們的聚合器 REST API會返回產品資訊。 + +```bash +curl http://localhost:50004/product +{"title":"The Product Title.","productInventories":5} +``` + +## 類圖 + +![alt text](./etc/aggregator-service.png "Aggregator Microservice") + +## 適用性 + +當需要各種微服務的統一API時,無論客戶端裝置如何,都可以使用Aggregator微服務模式。 + +## 鳴謝 + +* [Microservice Design Patterns](http://web.archive.org/web/20190705163602/http://blog.arungupta.me/microservice-design-patterns/) +* [Microservices Patterns: With examples in Java](https://www.amazon.com/gp/product/1617294543/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=1617294543&linkId=8b4e570267bc5fb8b8189917b461dc60) +* [Architectural Patterns: Uncover essential patterns in the most indispensable realm of enterprise architecture](https://www.amazon.com/gp/product/B077T7V8RC/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=B077T7V8RC&linkId=c34d204bfe1b277914b420189f09c1a4) diff --git a/localization/zh-TW/aggregator-microservices/etc/aggregator-service.png b/localization/zh-TW/aggregator-microservices/etc/aggregator-service.png new file mode 100644 index 000000000000..75ee82328bbe Binary files /dev/null and b/localization/zh-TW/aggregator-microservices/etc/aggregator-service.png differ diff --git a/localization/zh-TW/ambassador/README.md b/localization/zh-TW/ambassador/README.md new file mode 100644 index 000000000000..b79ca5889e1b --- /dev/null +++ b/localization/zh-TW/ambassador/README.md @@ -0,0 +1,195 @@ +--- +title: Ambassador +shortTitle: Ambassador +category: Structural +language: zh +tag: + - Decoupling + - Cloud distributed +--- + +## 目的 + +在客戶端上提供幫助程式服務例項,並從共享資源上轉移常用功能。 + +## 解釋 + +真實世界例子 + +> 遠端服務有許多客戶端訪問它提供的功能。 該服務是舊版應用程式,無法更新。 使用者的大量請求導致連線問題。新的請求頻率規則需要同時實現延遲檢測和客戶端日誌功能。 + +通俗的說 + +> 使用“大使”模式,我們可以實現來自客戶端的頻率較低的輪詢以及延遲檢查和日誌記錄。 + +微軟文件做了如下闡述 + +> 可以將大使服務視為與客戶端位於同一位置的程序外代理。 此模式對於以語言不可知的方式減輕常見的客戶端連線任務(例如監視,日誌記錄,路由,安全性(如TLS)和彈性模式)的工作很有用。 它通常與舊版應用程式或其他難以修改的應用程式一起使用,以擴充套件其網路功能。 它還可以使專業團隊實現這些功能。 + +**程式示例** + +有了上面的介紹我們將在這個例子中模仿功能。我們有一個用遠端服務實現的介面,同時也是大使服務。 + +```java +interface RemoteServiceInterface { + long doRemoteFunction(int value) throws Exception; +} +``` + +表示為單例的遠端服務。 + +```java +public class RemoteService implements RemoteServiceInterface { + + private static final Logger LOGGER = LoggerFactory.getLogger(RemoteService.class); + private static RemoteService service = null; + + static synchronized RemoteService getRemoteService() { + if (service == null) { + service = new RemoteService(); + } + return service; + } + + private RemoteService() {} + + @Override + public long doRemoteFunction(int value) { + long waitTime = (long) Math.floor(Math.random() * 1000); + + try { + sleep(waitTime); + } catch (InterruptedException e) { + LOGGER.error("Thread sleep interrupted", e); + } + + return waitTime >= 200 ? value * 10 : -1; + } +} +``` + +服務大使新增了像日誌和延遲檢測的額外功能 + +```java +public class ServiceAmbassador implements RemoteServiceInterface { + + private static final Logger LOGGER = LoggerFactory.getLogger(ServiceAmbassador.class); + private static final int RETRIES = 3; + private static final int DELAY_MS = 3000; + + ServiceAmbassador() { + } + + @Override + public long doRemoteFunction(int value) { + return safeCall(value); + } + + private long checkLatency(int value) { + var startTime = System.currentTimeMillis(); + var result = RemoteService.getRemoteService().doRemoteFunction(value); + var timeTaken = System.currentTimeMillis() - startTime; + + LOGGER.info("Time taken (ms): " + timeTaken); + return result; + } + + private long safeCall(int value) { + var retries = 0; + var result = (long) FAILURE; + + for (int i = 0; i < RETRIES; i++) { + if (retries >= RETRIES) { + return FAILURE; + } + + if ((result = checkLatency(value)) == FAILURE) { + LOGGER.info("Failed to reach remote: (" + (i + 1) + ")"); + retries++; + try { + sleep(DELAY_MS); + } catch (InterruptedException e) { + LOGGER.error("Thread sleep state interrupted", e); + } + } else { + break; + } + } + return result; + } +} +``` + +客戶端具有用於與遠端服務進行互動的本地服務大使: + +```java +public class Client { + + private static final Logger LOGGER = LoggerFactory.getLogger(Client.class); + private final ServiceAmbassador serviceAmbassador = new ServiceAmbassador(); + + long useService(int value) { + var result = serviceAmbassador.doRemoteFunction(value); + LOGGER.info("Service result: " + result); + return result; + } +} +``` + +這是兩個使用該服務的客戶端。 + +```java +public class App { + public static void main(String[] args) { + var host1 = new Client(); + var host2 = new Client(); + host1.useService(12); + host2.useService(73); + } +} +``` + +Here's the output for running the example: + +```java +Time taken (ms): 111 +Service result: 120 +Time taken (ms): 931 +Failed to reach remote: (1) +Time taken (ms): 665 +Failed to reach remote: (2) +Time taken (ms): 538 +Failed to reach remote: (3) +Service result: -1 +``` + +## 類圖 + +![alt text](./etc/ambassador.urm.png "Ambassador class diagram") + +## 適用性 + +大使適用於無法修改或極難修改的舊式遠端服務。 可以在客戶端上實現連線性的功能,而無需更改遠端服務。 + +* 大使提供了用於遠端服務的本地介面。 +* 大使在客戶端上提供日誌記錄,斷路,重試和安全性。 + +## 典型用例 + +* 控制對另一個物件的訪問 +* 實現日誌 +* 解除安裝遠端服務任務 +* 簡化網路連線 + +## 已知使用 + +* [Kubernetes-native API gateway for microservices](https://github.com/datawire/ambassador) + +## 相關模式 + +* [Proxy](https://java-design-patterns.com/patterns/proxy/) + +## 鳴謝 + +* [Ambassador pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/ambassador) +* [Designing Distributed Systems: Patterns and Paradigms for Scalable, Reliable Services](https://books.google.co.uk/books?id=6BJNDwAAQBAJ&pg=PT35&lpg=PT35&dq=ambassador+pattern+in+real+world&source=bl&ots=d2e7GhYdHi&sig=Lfl_MDnCgn6lUcjzOg4GXrN13bQ&hl=en&sa=X&ved=0ahUKEwjk9L_18rrbAhVpKcAKHX_KA7EQ6AEIWTAI#v=onepage&q=ambassador%20pattern%20in%20real%20world&f=false) diff --git a/localization/zh-TW/ambassador/etc/ambassador.urm.png b/localization/zh-TW/ambassador/etc/ambassador.urm.png new file mode 100644 index 000000000000..9b50a02ad356 Binary files /dev/null and b/localization/zh-TW/ambassador/etc/ambassador.urm.png differ diff --git a/localization/zh-TW/api-gateway/README.md b/localization/zh-TW/api-gateway/README.md new file mode 100644 index 000000000000..7e728a26591a --- /dev/null +++ b/localization/zh-TW/api-gateway/README.md @@ -0,0 +1,137 @@ +--- +title: API Gateway +shortTitle: API Gateway +category: Architectural +language: zh +tag: + - Cloud distributed + - Decoupling + - Microservices +--- + +## 目的 + +API閘道器將所有對微服務的呼叫聚合到一起。使用者對API閘道器進行一次呼叫,然後API閘道器呼叫每個相關的微服務。 + +## 解釋 + +使用微服務模式,客戶端可能需要來自多個不同微服務的資料。 如果客戶端直接呼叫每個微服務,則可能會導致更長的載入時間,因為客戶端將不得不為每個呼叫的微服務發出網路請求。此外,讓客戶端呼叫每個微服務會直接將客戶端與該微服務相關聯-如果微服務的內部實現發生了變化(例如,如果將來某個時候合併了兩個微服務),或者微服務的位置(主機和埠) 更改,則必須更新使用這些微服務的每個客戶端。 + +API閘道器模式的目的是緩解其中的一些問題。 在API閘道器模式中,在客戶端和微服務之間放置了一個附加實體(API閘道器)。API閘道器的工作是將對微服務的呼叫進行聚合。 客戶端不是一次單獨呼叫每個微服務,而是一次呼叫API閘道器。 然後,API閘道器呼叫客戶端所需的每個微服務。 + +真實世界例子 + +> 我們正在為電子商務站點實現微服務和API閘道器模式。 在此係統中,API閘道器呼叫Image和Price微服務。 + +通俗地說 + +> 對於使用微服務架構實現的系統,API是聚合微服務呼叫的入口點。 + +維基百科說 + +> API閘道器是充當API前置,接收API請求,執行限制和安全策略,將請求傳遞到後端服務,然後將響應傳遞迴請求者的伺服器。閘道器通常包括一個轉換引擎,以實時地編排和修改請求和響應。 閘道器可以提供收集分析資料和提供快取等功能。閘道器還可以提供支援身份驗證,授權,安全性,審計和法規遵從性的功能。 + +**程式示例** + +此實現展示了電子商務站點的API閘道器模式。` ApiGateway`分別使用` ImageClientImpl`和` PriceClientImpl`來呼叫Image和Price微服務。 在桌面裝置上檢視該網站的客戶可以看到價格資訊和產品圖片,因此` ApiGateway`會呼叫這兩種微服務並在`DesktopProduct`模型中彙總資料。 但是,移動使用者只能看到價格資訊。 他們看不到產品圖片。 對於移動使用者,`ApiGateway`僅檢索價格資訊,並將其用於填充`MobileProduct`模型。 + +這個是影象微服務的實現。 + +```java +public interface ImageClient { + String getImagePath(); +} + +public class ImageClientImpl implements ImageClient { + @Override + public String getImagePath() { + var httpClient = HttpClient.newHttpClient(); + var httpGet = HttpRequest.newBuilder() + .GET() + .uri(URI.create("http://localhost:50005/image-path")) + .build(); + + try { + var httpResponse = httpClient.send(httpGet, BodyHandlers.ofString()); + return httpResponse.body(); + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + } + + return null; + } +} +``` + +這裡是價格服務的實現。 + +```java +public interface PriceClient { + String getPrice(); +} + +public class PriceClientImpl implements PriceClient { + + @Override + public String getPrice() { + var httpClient = HttpClient.newHttpClient(); + var httpGet = HttpRequest.newBuilder() + .GET() + .uri(URI.create("http://localhost:50006/price")) + .build(); + + try { + var httpResponse = httpClient.send(httpGet, BodyHandlers.ofString()); + return httpResponse.body(); + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + } + + return null; + } +} +``` + +在這裡,我們可以看到API閘道器如何將請求對映到微服務。 + +```java +public class ApiGateway { + + @Resource + private ImageClient imageClient; + + @Resource + private PriceClient priceClient; + + @RequestMapping(path = "/desktop", method = RequestMethod.GET) + public DesktopProduct getProductDesktop() { + var desktopProduct = new DesktopProduct(); + desktopProduct.setImagePath(imageClient.getImagePath()); + desktopProduct.setPrice(priceClient.getPrice()); + return desktopProduct; + } + + @RequestMapping(path = "/mobile", method = RequestMethod.GET) + public MobileProduct getProductMobile() { + var mobileProduct = new MobileProduct(); + mobileProduct.setPrice(priceClient.getPrice()); + return mobileProduct; + } +} +``` + +## 類圖 +![alt text](./etc/api-gateway.png "API Gateway") + +## 適用性 + +在以下情況下使用API閘道器模式 + +* 你正在使用微服務架構,並且需要聚合單點來進行微服務呼叫。 + +## 鳴謝 + +* [microservices.io - API Gateway](http://microservices.io/patterns/apigateway.html) +* [NGINX - Building Microservices: Using an API Gateway](https://www.nginx.com/blog/building-microservices-using-an-api-gateway/) +* [Microservices Patterns: With examples in Java](https://www.amazon.com/gp/product/1617294543/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=1617294543&linkId=ac7b6a57f866ac006a309d9086e8cfbd) +* [Building Microservices: Designing Fine-Grained Systems](https://www.amazon.com/gp/product/1491950358/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=1491950358&linkId=4c95ca9831e05e3f0dadb08841d77bf1) diff --git a/localization/zh-TW/api-gateway/etc/api-gateway.png b/localization/zh-TW/api-gateway/etc/api-gateway.png new file mode 100644 index 000000000000..bb3ec2e2e1a8 Binary files /dev/null and b/localization/zh-TW/api-gateway/etc/api-gateway.png differ diff --git a/localization/zh-TW/arrange-act-assert/README.md b/localization/zh-TW/arrange-act-assert/README.md new file mode 100644 index 000000000000..e98c986e822c --- /dev/null +++ b/localization/zh-TW/arrange-act-assert/README.md @@ -0,0 +1,138 @@ +--- +title: Arrange/Act/Assert +shortTitle: Arrange/Act/Assert +category: Idiom +language: zh +tag: + - Testing +--- + +## 或稱 + +Given/When/Then + +## 意圖 + +安排/執行/斷言(AAA)是組織單元測試的一種模式。 + +它將測試分為三個清晰而獨特的步驟: + +1. 安排:執行測試所需的設定和初始化。 +2. 執行:採取測試所需的行動。 +3. 斷言:驗證測試結果。 + +## 解釋 + +這種模式有幾個明顯的好處。 它在測試的設定,操作和結果之間建立了清晰的分隔。 這種結構使程式碼更易於閱讀和理解。 如果按順序排列步驟並格式化程式碼以將它們分開,則可以掃描測試並快速瞭解其功能。 + +當你編寫測試時,它還會強制執行一定程度的紀律。 你必須清楚地考慮你的測試將執行的三個步驟。 由於你已經有了大綱,因此可以使同時編寫測試變得更加自然。 + +真實世界例子 + +> 我們需要為一個類編寫全面而清晰的單元測試套件。 + +通俗地說 + +> 安排/執行/斷言是一種測試模式,將測試分為三個清晰的步驟以方便維護。 + +WikiWikiWeb 上說 + +> 安排/執行/斷言是用於在單元測試方法中排列和格式化程式碼的模式。 + +**程式示例** + +讓我們首先介紹要進行單元測試的`Cash`類。 + +```java +public class Cash { + + private int amount; + + Cash(int amount) { + this.amount = amount; + } + + void plus(int addend) { + amount += addend; + } + + boolean minus(int subtrahend) { + if (amount >= subtrahend) { + amount -= subtrahend; + return true; + } else { + return false; + } + } + + int count() { + return amount; + } +} +``` + +然後我們根據安排/ 執行 / 斷言模式編寫單元測試。 注意每個單元測試的步驟是分開的清晰的。 + +```java +class CashAAATest { + + @Test + void testPlus() { + //Arrange + var cash = new Cash(3); + //Act + cash.plus(4); + //Assert + assertEquals(7, cash.count()); + } + + @Test + void testMinus() { + //Arrange + var cash = new Cash(8); + //Act + var result = cash.minus(5); + //Assert + assertTrue(result); + assertEquals(3, cash.count()); + } + + @Test + void testInsufficientMinus() { + //Arrange + var cash = new Cash(1); + //Act + var result = cash.minus(6); + //Assert + assertFalse(result); + assertEquals(1, cash.count()); + } + + @Test + void testUpdate() { + //Arrange + var cash = new Cash(5); + //Act + cash.plus(6); + var result = cash.minus(3); + //Assert + assertTrue(result); + assertEquals(8, cash.count()); + } +} +``` + +## 適用性 + +使用 安排/執行/斷言 模式當 + +* 你需要結構化你的單元測試程式碼這樣它們可以更好的閱讀,維護和增強。 + +## 鳴謝 + +* [Arrange, Act, Assert: What is AAA Testing?](https://blog.ncrunch.net/post/arrange-act-assert-aaa-testing.aspx) +* [Bill Wake: 3A – Arrange, Act, Assert](https://xp123.com/articles/3a-arrange-act-assert/) +* [Martin Fowler: GivenWhenThen](https://martinfowler.com/bliki/GivenWhenThen.html) +* [xUnit Test Patterns: Refactoring Test Code](https://www.amazon.com/gp/product/0131495054/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=0131495054&linkId=99701e8f4af2f7e8dd50d720c9b63dbf) +* [Unit Testing Principles, Practices, and Patterns](https://www.amazon.com/gp/product/1617296279/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=1617296279&linkId=74c75cf22a63c3e4758ae08aa0a0cc35) +* [Test Driven Development: By Example](https://www.amazon.com/gp/product/0321146530/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=0321146530&linkId=5c63a93d8c1175b84ca5087472ef0e05) diff --git a/localization/zh-TW/async-method-invocation/README.md b/localization/zh-TW/async-method-invocation/README.md new file mode 100644 index 000000000000..51a7e39e876a --- /dev/null +++ b/localization/zh-TW/async-method-invocation/README.md @@ -0,0 +1,158 @@ +--- +title: Async Method Invocation +shortTitle: Async Method Invocation +category: Concurrency +language: zh +tag: + - Reactive +--- + +## 意圖 + +非同步方法呼叫是一個呼叫執行緒在等待任務結果時不會阻塞的模式。模式為多個獨立的任務提供並行的處理方式並且透過回撥或等到它們全部完成來接收任務結果。 + +## 解釋 + +真實世界例子 + +> 發射火箭是一項令人激動的事務。任務指揮官發出了發射命令,經過一段不確定的時間後,火箭要麼成功發射,要麼慘遭失敗。 + +通俗地說 + +> 非同步方法呼叫開始任務處理,並在任務完成之前立即返回。 任務處理的結果稍後返回給呼叫方。 + +維基百科說 + +> 在多執行緒計算機程式設計中,非同步方法呼叫(AMI),也稱為非同步方法呼叫或非同步模式,是一種設計模式,其中在等待被呼叫的程式碼完成時不會阻塞呼叫站點。 而是在執行結果到達時通知呼叫執行緒。輪詢呼叫結果是不希望的選項。 + +**程式示例** + +在此示例中,我們正在發射太空火箭並部署月球漫遊車。該應用演示了非同步方法呼叫模式。 模式的關鍵部分是`AsyncResult`(用於非同步評估值的中間容器),`AsyncCallback`(可以在任務完成時被執行)和`AsyncExecutor`(用於管理非同步任務的執行)。 + +```java +public interface AsyncResult { + boolean isCompleted(); + T getValue() throws ExecutionException; + void await() throws InterruptedException; +} +``` + +```java +public interface AsyncCallback { + void onComplete(T value, Optional ex); +} +``` + +```java +public interface AsyncExecutor { + AsyncResult startProcess(Callable task); + AsyncResult startProcess(Callable task, AsyncCallback callback); + T endProcess(AsyncResult asyncResult) throws ExecutionException, InterruptedException; +} +``` + +`ThreadAsyncExecutor`是`AsyncExecutor`的實現。 接下來將突出顯示其一些關鍵部分。 + +```java +public class ThreadAsyncExecutor implements AsyncExecutor { + + @Override + public AsyncResult startProcess(Callable task) { + return startProcess(task, null); + } + + @Override + public AsyncResult startProcess(Callable task, AsyncCallback callback) { + var result = new CompletableResult<>(callback); + new Thread( + () -> { + try { + result.setValue(task.call()); + } catch (Exception ex) { + result.setException(ex); + } + }, + "executor-" + idx.incrementAndGet()) + .start(); + return result; + } + + @Override + public T endProcess(AsyncResult asyncResult) + throws ExecutionException, InterruptedException { + if (!asyncResult.isCompleted()) { + asyncResult.await(); + } + return asyncResult.getValue(); + } +} +``` + +然後,我們準備發射一些火箭,看看一切是如何協同工作的。 + +```java +public static void main(String[] args) throws Exception { + // 構造一個將執行非同步任務的新執行程式 + var executor = new ThreadAsyncExecutor(); + + // 以不同的處理時間開始一些非同步任務,最後兩個使用回撥處理程式 + final var asyncResult1 = executor.startProcess(lazyval(10, 500)); + final var asyncResult2 = executor.startProcess(lazyval("test", 300)); + final var asyncResult3 = executor.startProcess(lazyval(50L, 700)); + final var asyncResult4 = executor.startProcess(lazyval(20, 400), callback("Deploying lunar rover")); + final var asyncResult5 = + executor.startProcess(lazyval("callback", 600), callback("Deploying lunar rover")); + + // 在當前執行緒中模擬非同步任務正在它們自己的執行緒中執行 + Thread.sleep(350); // 哦,兄弟,我們在這很辛苦 + log("Mission command is sipping coffee"); + + // 等待任務完成 + final var result1 = executor.endProcess(asyncResult1); + final var result2 = executor.endProcess(asyncResult2); + final var result3 = executor.endProcess(asyncResult3); + asyncResult4.await(); + asyncResult5.await(); + + // log the results of the tasks, callbacks log immediately when complete + // 記錄任務結果的日誌, 回撥的日誌會在回撥完成時立刻記錄 + log("Space rocket <" + result1 + "> launch complete"); + log("Space rocket <" + result2 + "> launch complete"); + log("Space rocket <" + result3 + "> launch complete"); +} +``` + +這是程式控制臺的輸出。 + +```java +21:47:08.227 [executor-2] INFO com.iluwatar.async.method.invocation.App - Space rocket launched successfully +21:47:08.269 [main] INFO com.iluwatar.async.method.invocation.App - Mission command is sipping coffee +21:47:08.318 [executor-4] INFO com.iluwatar.async.method.invocation.App - Space rocket <20> launched successfully +21:47:08.335 [executor-4] INFO com.iluwatar.async.method.invocation.App - Deploying lunar rover <20> +21:47:08.414 [executor-1] INFO com.iluwatar.async.method.invocation.App - Space rocket <10> launched successfully +21:47:08.519 [executor-5] INFO com.iluwatar.async.method.invocation.App - Space rocket launched successfully +21:47:08.519 [executor-5] INFO com.iluwatar.async.method.invocation.App - Deploying lunar rover +21:47:08.616 [executor-3] INFO com.iluwatar.async.method.invocation.App - Space rocket <50> launched successfully +21:47:08.617 [main] INFO com.iluwatar.async.method.invocation.App - Space rocket <10> launch complete +21:47:08.617 [main] INFO com.iluwatar.async.method.invocation.App - Space rocket launch complete +21:47:08.618 [main] INFO com.iluwatar.async.method.invocation.App - Space rocket <50> launch complete +``` + +# 類圖 + +![alt text](./etc/async-method-invocation.png "Async Method Invocation") + +## 適用性 + +在以下情況下使用非同步方法呼叫模式 + +* 你有多個可以並行執行的獨立任務 +* 你需要提高一組順序任務的效能 +* 你的處理能力或長時間執行的任務數量有限,並且呼叫方不應等待任務執行完畢 + +## 真實世界例子 + +* [FutureTask](http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/FutureTask.html) +* [CompletableFuture](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html) +* [ExecutorService](http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html) +* [Task-based Asynchronous Pattern](https://msdn.microsoft.com/en-us/library/hh873175.aspx) diff --git a/localization/zh-TW/async-method-invocation/etc/async-method-invocation.png b/localization/zh-TW/async-method-invocation/etc/async-method-invocation.png new file mode 100644 index 000000000000..764895d7a217 Binary files /dev/null and b/localization/zh-TW/async-method-invocation/etc/async-method-invocation.png differ diff --git a/localization/zh-TW/balking/README.md b/localization/zh-TW/balking/README.md new file mode 100644 index 000000000000..3b5b8b3c5070 --- /dev/null +++ b/localization/zh-TW/balking/README.md @@ -0,0 +1,129 @@ +--- +title: Balking +shortTitle: Balking +category: Concurrency +language: zh +tag: + - Decoupling +--- + +## 意圖 + +止步模式用於防止物件在不完整或不合適的狀態下執行某些程式碼。 + +## 解釋 + +真實世界例子 + +> 洗衣機中有一個開始按鈕,用於啟動衣物洗滌。當洗衣機處於非活動狀態時,按鈕將按預期工作,但是如果已經在洗滌,則按鈕將不起任何作用。 + +通俗地說 + +> 使用止步模式,僅當物件處於特定狀態時才執行特定程式碼。 + +維基百科說 + +> 禁止模式是一種軟體設計模式,僅當物件處於特定狀態時才對物件執行操作。例如,一個物件讀取zip壓縮檔案並在壓縮檔案沒開啟的時候呼叫get方法,物件將在請求的時候”止步“。 + +**程式示例** + +在此示例中,` WashingMachine`是一個具有兩個狀態的物件,可以處於兩種狀態:ENABLED和WASHING。 如果機器已啟用,則使用執行緒安全方法將狀態更改為WASHING。 另一方面,如果已經進行了清洗並且任何其他執行緒執行`wash()`,則它將不執行該操作,而是不執行任何操作而返回。 + +這裡是`WashingMachine` 類相關的部分。 + +```java +@Slf4j +public class WashingMachine { + + private final DelayProvider delayProvider; + private WashingMachineState washingMachineState; + + public WashingMachine(DelayProvider delayProvider) { + this.delayProvider = delayProvider; + this.washingMachineState = WashingMachineState.ENABLED; + } + + public WashingMachineState getWashingMachineState() { + return washingMachineState; + } + + public void wash() { + synchronized (this) { + var machineState = getWashingMachineState(); + LOGGER.info("{}: Actual machine state: {}", Thread.currentThread().getName(), machineState); + if (this.washingMachineState == WashingMachineState.WASHING) { + LOGGER.error("Cannot wash if the machine has been already washing!"); + return; + } + this.washingMachineState = WashingMachineState.WASHING; + } + LOGGER.info("{}: Doing the washing", Thread.currentThread().getName()); + this.delayProvider.executeAfterDelay(50, TimeUnit.MILLISECONDS, this::endOfWashing); + } + + public synchronized void endOfWashing() { + washingMachineState = WashingMachineState.ENABLED; + LOGGER.info("{}: Washing completed.", Thread.currentThread().getId()); + } +} +``` + +這裡是一個`WashingMachine`所使用的`DelayProvider`簡單介面。 + +```java +public interface DelayProvider { + void executeAfterDelay(long interval, TimeUnit timeUnit, Runnable task); +} +``` + +現在,我們使用`WashingMachine`介紹該應用程式。 + +```java + public static void main(String... args) { + final var washingMachine = new WashingMachine(); + var executorService = Executors.newFixedThreadPool(3); + for (int i = 0; i < 3; i++) { + executorService.execute(washingMachine::wash); + } + executorService.shutdown(); + try { + executorService.awaitTermination(10, TimeUnit.SECONDS); + } catch (InterruptedException ie) { + LOGGER.error("ERROR: Waiting on executor service shutdown!"); + Thread.currentThread().interrupt(); + } + } +``` + +下面是程式的輸出。 + +``` +14:02:52.268 [pool-1-thread-2] INFO com.iluwatar.balking.WashingMachine - pool-1-thread-2: Actual machine state: ENABLED +14:02:52.272 [pool-1-thread-2] INFO com.iluwatar.balking.WashingMachine - pool-1-thread-2: Doing the washing +14:02:52.272 [pool-1-thread-3] INFO com.iluwatar.balking.WashingMachine - pool-1-thread-3: Actual machine state: WASHING +14:02:52.273 [pool-1-thread-3] ERROR com.iluwatar.balking.WashingMachine - Cannot wash if the machine has been already washing! +14:02:52.273 [pool-1-thread-1] INFO com.iluwatar.balking.WashingMachine - pool-1-thread-1: Actual machine state: WASHING +14:02:52.273 [pool-1-thread-1] ERROR com.iluwatar.balking.WashingMachine - Cannot wash if the machine has been already washing! +14:02:52.324 [pool-1-thread-2] INFO com.iluwatar.balking.WashingMachine - 14: Washing completed. +``` + +## 類圖 + +![alt text](./etc/balking.png "Balking") + +## 適用性 + + +使用止步模式當 + +* 你只想在物件處於特定狀態時才對其呼叫操作 +* 物件通常僅處於容易暫時停止但狀態未知的狀態 + +## 相關模式 + +* [保護性暫掛模式](https://java-design-patterns.com/patterns/guarded-suspension/) +* [雙重檢查鎖模式](https://java-design-patterns.com/patterns/double-checked-locking/) + +## 鳴謝 + +* [Patterns in Java: A Catalog of Reusable Design Patterns Illustrated with UML, 2nd Edition, Volume 1](https://www.amazon.com/gp/product/0471227293/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=0471227293&linkId=0e39a59ffaab93fb476036fecb637b99) diff --git a/localization/zh-TW/balking/etc/balking.png b/localization/zh-TW/balking/etc/balking.png new file mode 100644 index 000000000000..f409eaacbb95 Binary files /dev/null and b/localization/zh-TW/balking/etc/balking.png differ diff --git a/localization/zh-TW/bridge/README.md b/localization/zh-TW/bridge/README.md new file mode 100644 index 000000000000..199289460d17 --- /dev/null +++ b/localization/zh-TW/bridge/README.md @@ -0,0 +1,204 @@ +--- +title: Bridge +shortTitle: Bridge +category: Structural +language: zh +tag: + - Gang of Four +--- + +## 又被稱為 + +手柄/身體模式 + +## 目的 + +將抽象與其實現分離,以便二者可以獨立變化。 + +## 解釋 + +真實世界例子 + +> 考慮一下你擁有一種具有不同附魔的武器,並且應該允許將具有不同附魔的不同武器混合使用。 你會怎麼做? 為每個附魔建立每種武器的多個副本,還是隻是建立單獨的附魔並根據需要為武器設定它? 橋接模式使你可以進行第二次操作。 + +通俗的說 + +> 橋接模式是一個更推薦組合而不是繼承的模式。將實現細節從一個層次結構推送到具有單獨層次結構的另一個物件。 + +維基百科說 + +> 橋接模式是軟體工程中使用的一種設計模式,旨在“將抽象與其實現分離,從而使兩者可以獨立變化” + +**程式示例** + +翻譯一下上面的武器示例。下面我們有武器的類層級: + +```java +public interface Weapon { + void wield(); + void swing(); + void unwield(); + Enchantment getEnchantment(); +} + +public class Sword implements Weapon { + + private final Enchantment enchantment; + + public Sword(Enchantment enchantment) { + this.enchantment = enchantment; + } + + @Override + public void wield() { + LOGGER.info("The sword is wielded."); + enchantment.onActivate(); + } + + @Override + public void swing() { + LOGGER.info("The sword is swinged."); + enchantment.apply(); + } + + @Override + public void unwield() { + LOGGER.info("The sword is unwielded."); + enchantment.onDeactivate(); + } + + @Override + public Enchantment getEnchantment() { + return enchantment; + } +} + +public class Hammer implements Weapon { + + private final Enchantment enchantment; + + public Hammer(Enchantment enchantment) { + this.enchantment = enchantment; + } + + @Override + public void wield() { + LOGGER.info("The hammer is wielded."); + enchantment.onActivate(); + } + + @Override + public void swing() { + LOGGER.info("The hammer is swinged."); + enchantment.apply(); + } + + @Override + public void unwield() { + LOGGER.info("The hammer is unwielded."); + enchantment.onDeactivate(); + } + + @Override + public Enchantment getEnchantment() { + return enchantment; + } +} +``` + +這裡是單獨的附魔類結構: + +```java +public interface Enchantment { + void onActivate(); + void apply(); + void onDeactivate(); +} + +public class FlyingEnchantment implements Enchantment { + + @Override + public void onActivate() { + LOGGER.info("The item begins to glow faintly."); + } + + @Override + public void apply() { + LOGGER.info("The item flies and strikes the enemies finally returning to owner's hand."); + } + + @Override + public void onDeactivate() { + LOGGER.info("The item's glow fades."); + } +} + +public class SoulEatingEnchantment implements Enchantment { + + @Override + public void onActivate() { + LOGGER.info("The item spreads bloodlust."); + } + + @Override + public void apply() { + LOGGER.info("The item eats the soul of enemies."); + } + + @Override + public void onDeactivate() { + LOGGER.info("Bloodlust slowly disappears."); + } +} +``` + +這裡是兩種層次結構的實踐: + +```java +var enchantedSword = new Sword(new SoulEatingEnchantment()); +enchantedSword.wield(); +enchantedSword.swing(); +enchantedSword.unwield(); +// The sword is wielded. +// The item spreads bloodlust. +// The sword is swinged. +// The item eats the soul of enemies. +// The sword is unwielded. +// Bloodlust slowly disappears. + +var hammer = new Hammer(new FlyingEnchantment()); +hammer.wield(); +hammer.swing(); +hammer.unwield(); +// The hammer is wielded. +// The item begins to glow faintly. +// The hammer is swinged. +// The item flies and strikes the enemies finally returning to owner's hand. +// The hammer is unwielded. +// The item's glow fades. +``` + + + +## 類圖 + +![alt text](./etc/bridge.urm.png "Bridge class diagram") + +## 適用性 + +使用橋接模式當 + +* 你想永久性的避免抽象和他的實現之間的繫結。有可能是這種情況,當實現需要被選擇或者在執行時切換。 +* 抽象和他們的實現應該能透過寫子類來擴充套件。這種情況下,橋接模式讓你可以組合不同的抽象和實現並獨立的擴充套件他們。 +* 對抽象的實現的改動應當不會對客戶產生影響;也就是說,他們的程式碼不必重新編譯。 +* 你有種類繁多的類。這樣的類層次結構表明需要將一個物件分為兩部分。Rumbaugh 使用術語“巢狀歸納”來指代這種類層次結構。 +* 你想在多個物件間分享一種實現(可能使用引用計數),這個事實應該對客戶隱藏。一個簡單的示例是Coplien的String類,其中多個物件可以共享同一字串表示形式 + +## 教程 + +* [Bridge Pattern Tutorial](https://www.journaldev.com/1491/bridge-design-pattern-java) + +## 鳴謝 + +* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59) +* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b) diff --git a/localization/zh-TW/bridge/etc/bridge.urm.png b/localization/zh-TW/bridge/etc/bridge.urm.png new file mode 100644 index 000000000000..785585bf8163 Binary files /dev/null and b/localization/zh-TW/bridge/etc/bridge.urm.png differ diff --git a/localization/zh-TW/builder/README.md b/localization/zh-TW/builder/README.md new file mode 100644 index 000000000000..d2fb01d6597f --- /dev/null +++ b/localization/zh-TW/builder/README.md @@ -0,0 +1,138 @@ +--- +title: Builder +shortTitle: Builder +category: Creational +language: zh +tag: + - Gang of Four + +--- + +## 目的 + +將複雜物件的構造與其表示分開,以便同一構造過程可以建立不同的表示。 + +## 解釋 + +現實世界例子 + +> 想象一個角色扮演遊戲的角色生成器。最簡單的選擇是讓計算機為你建立角色。但是如果你想選擇一些像專業,性別,髮色等角色細節時,這個角色生成就變成了一個漸進的過程。當所有選擇完成時,該過程也將完成。 + +用通俗的話說 + +> 允許你建立不同口味的物件同時避免構造器汙染。當一個物件可能有幾種口味,或者一個物件的建立涉及到很多步驟時會很有用。 + +維基百科說 + +> 建造者模式是一種物件建立的軟體設計模式,旨在為伸縮構造器反模式尋找一個解決方案。 + +說了這麼多,讓我補充一下什麼是伸縮建構函式反模式。我們肯定都見過像下面這樣的構造器: + +```java +public Hero(Profession profession, String name, HairType hairType, HairColor hairColor, Armor armor, Weapon weapon) { +} +``` + +就像你看到的構造器引數的數量很快就會失控同時引數的排列方式可能變得難以理解。另外,如果你將來希望新增更多選項,則此引數列表可能會繼續增長。這就被稱伸縮構造器反模式。 + +**程式設計示例** + +明智的選擇是使用建造者模式。首先我們有一個英雄要建立。 + +```java +public final class Hero { + private final Profession profession; + private final String name; + private final HairType hairType; + private final HairColor hairColor; + private final Armor armor; + private final Weapon weapon; + + private Hero(Builder builder) { + this.profession = builder.profession; + this.name = builder.name; + this.hairColor = builder.hairColor; + this.hairType = builder.hairType; + this.weapon = builder.weapon; + this.armor = builder.armor; + } +} +``` + +然後我們有建立者 + +```java + public static class Builder { + private final Profession profession; + private final String name; + private HairType hairType; + private HairColor hairColor; + private Armor armor; + private Weapon weapon; + + public Builder(Profession profession, String name) { + if (profession == null || name == null) { + throw new IllegalArgumentException("profession and name can not be null"); + } + this.profession = profession; + this.name = name; + } + + public Builder withHairType(HairType hairType) { + this.hairType = hairType; + return this; + } + + public Builder withHairColor(HairColor hairColor) { + this.hairColor = hairColor; + return this; + } + + public Builder withArmor(Armor armor) { + this.armor = armor; + return this; + } + + public Builder withWeapon(Weapon weapon) { + this.weapon = weapon; + return this; + } + + public Hero build() { + return new Hero(this); + } + } +``` + +然後可以這樣使用 + +```java +var mage = new Hero.Builder(Profession.MAGE, "Riobard").withHairColor(HairColor.BLACK).withWeapon(Weapon.DAGGER).build(); +``` + +## 類圖 + +![alt text](./etc/builder.urm.png "Builder class diagram") + +## 適用性 + +使用建造者模式當 + +* 建立複雜物件的演算法應獨立於組成物件的零件及其組裝方式 +* 構造過程必須允許所構造的物件具有不同的表示形式 + +## Java世界例子 + +* [java.lang.StringBuilder](http://docs.oracle.com/javase/8/docs/api/java/lang/StringBuilder.html) +* [java.nio.ByteBuffer](http://docs.oracle.com/javase/8/docs/api/java/nio/ByteBuffer.html#put-byte-) as well as similar buffers such as FloatBuffer, IntBuffer and so on. +* [java.lang.StringBuffer](http://docs.oracle.com/javase/8/docs/api/java/lang/StringBuffer.html#append-boolean-) +* All implementations of [java.lang.Appendable](http://docs.oracle.com/javase/8/docs/api/java/lang/Appendable.html) +* [Apache Camel builders](https://github.com/apache/camel/tree/0e195428ee04531be27a0b659005e3aa8d159d23/camel-core/src/main/java/org/apache/camel/builder) +* [Apache Commons Option.Builder](https://commons.apache.org/proper/commons-cli/apidocs/org/apache/commons/cli/Option.Builder.html) + +## 鳴謝 + +* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59) +* [Effective Java](https://www.amazon.com/gp/product/0134685997/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0134685997&linkCode=as2&tag=javadesignpat-20&linkId=4e349f4b3ff8c50123f8147c828e53eb) +* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b) +* [Refactoring to Patterns](https://www.amazon.com/gp/product/0321213351/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321213351&linkCode=as2&tag=javadesignpat-20&linkId=2a76fcb387234bc71b1c61150b3cc3a7) \ No newline at end of file diff --git a/localization/zh-TW/builder/etc/builder.urm.png b/localization/zh-TW/builder/etc/builder.urm.png new file mode 100644 index 000000000000..d77808d36097 Binary files /dev/null and b/localization/zh-TW/builder/etc/builder.urm.png differ diff --git a/localization/zh-TW/business-delegate/README.md b/localization/zh-TW/business-delegate/README.md new file mode 100644 index 000000000000..9de7f2ee20ae --- /dev/null +++ b/localization/zh-TW/business-delegate/README.md @@ -0,0 +1,154 @@ +--- +title: Business Delegate +shortTitle: Business Delegate +category: Structural +language: zh +tag: + - Decoupling +--- + +## 意圖 + +業務委託模式在表示層和業務層之間新增了一個抽象層。 透過使用該模式,我們獲得了各層之間的鬆散耦合,並封裝了有關如何定位,連線到組成應用程式的業務物件以及與之互動的邏輯。 + +## 解釋 + +真實世界例子 + +> 手機應用程式承諾將現有的任何電影流式傳輸到你的手機。它捕獲使用者的搜尋字串,並將其傳遞給業務委託層。業務委託層選擇最合適的影片流服務,然後從那裡播放影片。 + +通俗的說 + +> 業務委託模式在表示層和業務層之間新增了一個抽象層。 + +維基百科說 + +> 業務委託是一種Java EE設計模式。 該模式旨在減少業務服務與連線的表示層之間的耦合,並隱藏服務的實現細節(包括EJB體系結構的查詢和可訪問性)。 業務代表充當介面卡,以從表示層呼叫業務物件。 + +**程式示例** + +首先,我們有影片流服務的抽象類和一些它的實現。 + +```java +public interface VideoStreamingService { + void doProcessing(); +} + +@Slf4j +public class NetflixService implements VideoStreamingService { + @Override + public void doProcessing() { + LOGGER.info("NetflixService is now processing"); + } +} + +@Slf4j +public class YouTubeService implements VideoStreamingService { + @Override + public void doProcessing() { + LOGGER.info("YouTubeService is now processing"); + } +} +``` + +然後我們有一個查詢服務來決定我們使用哪個影片流服務。 + +```java +@Setter +public class BusinessLookup { + + private NetflixService netflixService; + private YouTubeService youTubeService; + + public VideoStreamingService getBusinessService(String movie) { + if (movie.toLowerCase(Locale.ROOT).contains("die hard")) { + return netflixService; + } else { + return youTubeService; + } + } +} +``` + +業務委託類使用業務查詢服務將電影播放請求路由到合適的影片流服務。 + +```java +@Setter +public class BusinessDelegate { + + private BusinessLookup lookupService; + + public void playbackMovie(String movie) { + VideoStreamingService videoStreamingService = lookupService.getBusinessService(movie); + videoStreamingService.doProcessing(); + } +} +``` + +移動客戶端利用業務委託來呼叫業務層。 + +```java +public class MobileClient { + + private final BusinessDelegate businessDelegate; + + public MobileClient(BusinessDelegate businessDelegate) { + this.businessDelegate = businessDelegate; + } + + public void playbackMovie(String movie) { + businessDelegate.playbackMovie(movie); + } +} +``` + +最後我們展示完整示例。 + +```java + public static void main(String[] args) { + + // prepare the objects + var businessDelegate = new BusinessDelegate(); + var businessLookup = new BusinessLookup(); + businessLookup.setNetflixService(new NetflixService()); + businessLookup.setYouTubeService(new YouTubeService()); + businessDelegate.setLookupService(businessLookup); + + // create the client and use the business delegate + var client = new MobileClient(businessDelegate); + client.playbackMovie("Die Hard 2"); + client.playbackMovie("Maradona: The Greatest Ever"); + } +``` + +這是控制檯的輸出。 + +``` +21:15:33.790 [main] INFO com.iluwatar.business.delegate.NetflixService - NetflixService is now processing +21:15:33.794 [main] INFO com.iluwatar.business.delegate.YouTubeService - YouTubeService is now processing +``` + +## 類圖 + +![alt text](./etc/business-delegate.urm.png "Business Delegate") + +## 相關模式 + +* [服務定位器模式](https://java-design-patterns.com/patterns/service-locator/) + +## 適用性 + +使用業務委託模式當 + +* 你希望表示層和業務層之間的鬆散耦合 +* 你想編排對多個業務服務的呼叫 +* 你希望封裝查詢服務和服務呼叫 + +## 教程 + +* [Business Delegate Pattern at TutorialsPoint](https://www.tutorialspoint.com/design_pattern/business_delegate_pattern.htm) + +## 鳴謝 + +* [J2EE Design Patterns](https://www.amazon.com/gp/product/0596004273/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596004273&linkCode=as2&tag=javadesignpat-20&linkId=48d37c67fb3d845b802fa9b619ad8f31) +* [Core J2EE Patterns: Best Practices and Design Strategies](https://www.amazon.com/gp/product/0130648841/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=0130648841&linkId=a0100de2b28c71ede8db1757fb2b5947) diff --git a/localization/zh-TW/business-delegate/etc/business-delegate.urm.png b/localization/zh-TW/business-delegate/etc/business-delegate.urm.png new file mode 100644 index 000000000000..4dca6c263b99 Binary files /dev/null and b/localization/zh-TW/business-delegate/etc/business-delegate.urm.png differ diff --git a/localization/zh-TW/bytecode/README.md b/localization/zh-TW/bytecode/README.md new file mode 100644 index 000000000000..4704228866c7 --- /dev/null +++ b/localization/zh-TW/bytecode/README.md @@ -0,0 +1,233 @@ +--- +title: Bytecode +shortTitle: Bytecode +category: Behavioral +language: zh +tag: + - Game programming +--- + +## 意圖 + +允許編碼行為作為虛擬機器的指令。 + +## 解釋 + +真實世界例子 + +> 一個團隊正在開發一款新的巫師對戰遊戲。巫師的行為需要經過精心的調整和上百次的遊玩測試。每次當遊戲設計師想改變巫師行為時都讓程式設計師去修改程式碼這是不妥的,所以巫師行為以資料驅動的虛擬機器方式實現。 + +通俗地說 + +> 位元組碼模式支援由資料而不是程式碼驅動的行為。 + +[Gameprogrammingpatterns.com](https://gameprogrammingpatterns.com/bytecode.html) 中做了如下闡述: + +> 指令集定義了可以執行的低階操作。一系列指令被編碼為位元組序列。虛擬機器一次一條地執行這些指令,中間的值用棧處理。透過組合指令,可以定義複雜的高階行為。 + +**程式示例** + +其中最重要的遊戲物件是`巫師`類。 + +```java +@AllArgsConstructor +@Setter +@Getter +@Slf4j +public class Wizard { + + private int health; + private int agility; + private int wisdom; + private int numberOfPlayedSounds; + private int numberOfSpawnedParticles; + + public void playSound() { + LOGGER.info("Playing sound"); + numberOfPlayedSounds++; + } + + public void spawnParticles() { + LOGGER.info("Spawning particles"); + numberOfSpawnedParticles++; + } +} +``` + +下面我們展示虛擬機器可用的指令。每個指令對於如何操作棧中的資料都有自己的語義。例如,增加指令,其取得棧頂的兩個元素並把結果壓入棧中。 + +```java +@AllArgsConstructor +@Getter +public enum Instruction { + + LITERAL(1), // e.g. "LITERAL 0", push 0 to stack + SET_HEALTH(2), // e.g. "SET_HEALTH", pop health and wizard number, call set health + SET_WISDOM(3), // e.g. "SET_WISDOM", pop wisdom and wizard number, call set wisdom + SET_AGILITY(4), // e.g. "SET_AGILITY", pop agility and wizard number, call set agility + PLAY_SOUND(5), // e.g. "PLAY_SOUND", pop value as wizard number, call play sound + SPAWN_PARTICLES(6), // e.g. "SPAWN_PARTICLES", pop value as wizard number, call spawn particles + GET_HEALTH(7), // e.g. "GET_HEALTH", pop value as wizard number, push wizard's health + GET_AGILITY(8), // e.g. "GET_AGILITY", pop value as wizard number, push wizard's agility + GET_WISDOM(9), // e.g. "GET_WISDOM", pop value as wizard number, push wizard's wisdom + ADD(10), // e.g. "ADD", pop 2 values, push their sum + DIVIDE(11); // e.g. "DIVIDE", pop 2 values, push their division + // ... +} +``` + +我們示例的核心是`虛擬機器`類。 它將指令作為輸入並執行它們以提供遊戲物件行為。 + +```java +@Getter +@Slf4j +public class VirtualMachine { + + private final Stack stack = new Stack<>(); + + private final Wizard[] wizards = new Wizard[2]; + + public VirtualMachine() { + wizards[0] = new Wizard(randomInt(3, 32), randomInt(3, 32), randomInt(3, 32), + 0, 0); + wizards[1] = new Wizard(randomInt(3, 32), randomInt(3, 32), randomInt(3, 32), + 0, 0); + } + + public VirtualMachine(Wizard wizard1, Wizard wizard2) { + wizards[0] = wizard1; + wizards[1] = wizard2; + } + + public void execute(int[] bytecode) { + for (var i = 0; i < bytecode.length; i++) { + Instruction instruction = Instruction.getInstruction(bytecode[i]); + switch (instruction) { + case LITERAL: + // Read the next byte from the bytecode. + int value = bytecode[++i]; + // Push the next value to stack + stack.push(value); + break; + case SET_AGILITY: + var amount = stack.pop(); + var wizard = stack.pop(); + setAgility(wizard, amount); + break; + case SET_WISDOM: + amount = stack.pop(); + wizard = stack.pop(); + setWisdom(wizard, amount); + break; + case SET_HEALTH: + amount = stack.pop(); + wizard = stack.pop(); + setHealth(wizard, amount); + break; + case GET_HEALTH: + wizard = stack.pop(); + stack.push(getHealth(wizard)); + break; + case GET_AGILITY: + wizard = stack.pop(); + stack.push(getAgility(wizard)); + break; + case GET_WISDOM: + wizard = stack.pop(); + stack.push(getWisdom(wizard)); + break; + case ADD: + var a = stack.pop(); + var b = stack.pop(); + stack.push(a + b); + break; + case DIVIDE: + a = stack.pop(); + b = stack.pop(); + stack.push(b / a); + break; + case PLAY_SOUND: + wizard = stack.pop(); + getWizards()[wizard].playSound(); + break; + case SPAWN_PARTICLES: + wizard = stack.pop(); + getWizards()[wizard].spawnParticles(); + break; + default: + throw new IllegalArgumentException("Invalid instruction value"); + } + LOGGER.info("Executed " + instruction.name() + ", Stack contains " + getStack()); + } + } + + public void setHealth(int wizard, int amount) { + wizards[wizard].setHealth(amount); + } + // other setters -> + // ... +} +``` + +現在我們可以展示使用虛擬機器的完整示例。 + +```java + public static void main(String[] args) { + + var vm = new VirtualMachine( + new Wizard(45, 7, 11, 0, 0), + new Wizard(36, 18, 8, 0, 0)); + + vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0")); + vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0")); + vm.execute(InstructionConverterUtil.convertToByteCode("GET_HEALTH")); + vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0")); + vm.execute(InstructionConverterUtil.convertToByteCode("GET_AGILITY")); + vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0")); + vm.execute(InstructionConverterUtil.convertToByteCode("GET_WISDOM")); + vm.execute(InstructionConverterUtil.convertToByteCode("ADD")); + vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 2")); + vm.execute(InstructionConverterUtil.convertToByteCode("DIVIDE")); + vm.execute(InstructionConverterUtil.convertToByteCode("ADD")); + vm.execute(InstructionConverterUtil.convertToByteCode("SET_HEALTH")); + } +``` + +下面是控制檯輸出。 + +``` +16:20:10.193 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed LITERAL, Stack contains [0] +16:20:10.196 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed LITERAL, Stack contains [0, 0] +16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed GET_HEALTH, Stack contains [0, 45] +16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed LITERAL, Stack contains [0, 45, 0] +16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed GET_AGILITY, Stack contains [0, 45, 7] +16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed LITERAL, Stack contains [0, 45, 7, 0] +16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed GET_WISDOM, Stack contains [0, 45, 7, 11] +16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed ADD, Stack contains [0, 45, 18] +16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed LITERAL, Stack contains [0, 45, 18, 2] +16:20:10.198 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed DIVIDE, Stack contains [0, 45, 9] +16:20:10.198 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed ADD, Stack contains [0, 54] +16:20:10.198 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed SET_HEALTH, Stack contains [] +``` + +## 類圖 + +![alt text](./etc/bytecode.urm.png "Bytecode class diagram") + +## 適用性 + + + +當你需要定義很多行為並且遊戲的實現語言不合適時,請使用位元組碼模式,因為: + +* 它的等級太低,使得程式設計變得乏味或容易出錯。 +* 由於編譯時間慢或其他工具問題,迭代它需要很長時間。 +* 它有太多的信任。 如果你想確保定義的行為不會破壞遊戲,你需要將其與程式碼庫的其餘部分進行沙箱化。 + +## 相關模式 + +* [Interpreter](https://java-design-patterns.com/patterns/interpreter/) + +## 鳴謝 + +* [Game programming patterns](http://gameprogrammingpatterns.com/bytecode.html) diff --git a/localization/zh-TW/bytecode/etc/bytecode.urm.png b/localization/zh-TW/bytecode/etc/bytecode.urm.png new file mode 100644 index 000000000000..51335fa0a4b4 Binary files /dev/null and b/localization/zh-TW/bytecode/etc/bytecode.urm.png differ diff --git a/localization/zh-TW/caching/README.md b/localization/zh-TW/caching/README.md new file mode 100644 index 000000000000..c32ecc8570d1 --- /dev/null +++ b/localization/zh-TW/caching/README.md @@ -0,0 +1,26 @@ +--- +title: Caching +shortTitle: Caching +category: Behavioral +language: zh +tag: + - Performance + - Cloud distributed +--- + +## 目的 +為了避免昂貴的資源重新獲取,方法是在資源使用後不立即釋放資源。資源保留其身份,保留在某些快速訪問的儲存中,並被重新使用,以避免再次獲取它們。 + +## 類圖 +![alt text](./etc/caching.png "Caching") + +## 適用性 +在以下情況下使用快取模式 + +* 重複獲取,初始化和釋放同一資源會導致不必要的效能開銷。 + +## 鳴謝 + +* [Write-through, write-around, write-back: Cache explained](http://www.computerweekly.com/feature/Write-through-write-around-write-back-Cache-explained) +* [Read-Through, Write-Through, Write-Behind, and Refresh-Ahead Caching](https://docs.oracle.com/cd/E15357_01/coh.360/e15723/cache_rtwtwbra.htm#COHDG5177) +* [Cache-Aside pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/cache-aside) diff --git a/localization/zh-TW/caching/etc/caching.png b/localization/zh-TW/caching/etc/caching.png new file mode 100644 index 000000000000..b6ed703ab8b7 Binary files /dev/null and b/localization/zh-TW/caching/etc/caching.png differ diff --git a/localization/zh-TW/callback/README.md b/localization/zh-TW/callback/README.md new file mode 100644 index 000000000000..c7b56306e5e6 --- /dev/null +++ b/localization/zh-TW/callback/README.md @@ -0,0 +1,78 @@ +--- +title: Callback +shortTitle: Callback +category: Idiom +language: zh +tag: + - Reactive +--- + +## 目的 +回撥是一部分被當為引數來傳遞給其他程式碼的可執行程式碼,接收方的程式碼可以在一些方便的時候來呼叫它。 + +## 解釋 + +真實世界例子 + +> 我們需要被通知當執行的任務結束時。我們為呼叫者傳遞一個回撥方法然後等它呼叫通知我們。 + +通俗的講 + + +> 回撥是一個用來傳遞給呼叫者的方法,它將在定義的時刻被呼叫。 + +維基百科說 + +> 在計算機程式設計中,回撥又被稱為“稍後呼叫”函式,可以是任何可執行的程式碼用來作為引數傳遞給其他程式碼;其它程式碼被期望在給定時間內呼叫回撥方法。 + +**程式設計示例** + +回撥是一個只有一個方法的簡單介面。 + +```java +public interface Callback { + + void call(); +} +``` + +下面我們定義一個任務它將在任務執行完成後執行回撥。 + +```java +public abstract class Task { + + final void executeWith(Callback callback) { + execute(); + Optional.ofNullable(callback).ifPresent(Callback::call); + } + + public abstract void execute(); +} + +public final class SimpleTask extends Task { + + private static final Logger LOGGER = getLogger(SimpleTask.class); + + @Override + public void execute() { + LOGGER.info("Perform some important activity and after call the callback method."); + } +} +``` + +最後這裡是我們如何執行一個任務然後接收一個回撥當它完成時。 + +```java + var task = new SimpleTask(); + task.executeWith(() -> LOGGER.info("I'm done now.")); +``` +## 類圖 +![alt text](./etc/callback.png "Callback") + +## 適用性 +使用回撥模式當 +* 當一些同步或非同步架構動作必須在一些定義好的活動執行後執行時。 + +## Java例子 + +* [CyclicBarrier](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CyclicBarrier.html#CyclicBarrier%28int,%20java.lang.Runnable%29) 建構函式可以接受回撥,該回撥將在每次障礙被觸發時觸發。 diff --git a/localization/zh-TW/callback/etc/callback.png b/localization/zh-TW/callback/etc/callback.png new file mode 100644 index 000000000000..7b499f79fcaa Binary files /dev/null and b/localization/zh-TW/callback/etc/callback.png differ diff --git a/localization/zh-TW/chain/README.md b/localization/zh-TW/chain/README.md new file mode 100644 index 000000000000..df49eefadbc6 --- /dev/null +++ b/localization/zh-TW/chain/README.md @@ -0,0 +1,158 @@ +--- +title: Chain of responsibility +shortTitle: Chain of responsibility +category: Behavioral +language: zh +tag: + - Gang of Four +--- + +## 目的 +透過給多個物件一個處理請求的機會,避免請求的傳送者和它的接收者耦合。串聯接收物件並在鏈條中傳遞請求直到一個物件處理它。 + +## 解釋 + +真實世界例子 + +> 獸王大聲命令他的軍隊。最近響應的是指揮官,然後是軍官,然後是士兵。指揮官,軍官,士兵這裡就形成了一個責任鏈。 + +通俗的說 + +> 它幫助構建一串物件。請求從一個物件中進入並結束然後進入到一個個物件中直到找到合適的處理器。 + +維基百科說 + +> 在物件導向設計中,責任鏈模式是一種由源命令物件和一系列處理物件組成的設計模式。每個處理物件包含了其定義的可處理的命令物件型別的邏輯。剩下的會傳遞給鏈條中的下一個處理物件。 + +**程式示例** + +用上面的獸人來翻譯我們的示例。首先我們有請求類 + +```java +public class Request { + + private final RequestType requestType; + private final String requestDescription; + private boolean handled; + + public Request(final RequestType requestType, final String requestDescription) { + this.requestType = Objects.requireNonNull(requestType); + this.requestDescription = Objects.requireNonNull(requestDescription); + } + + public String getRequestDescription() { return requestDescription; } + + public RequestType getRequestType() { return requestType; } + + public void markHandled() { this.handled = true; } + + public boolean isHandled() { return this.handled; } + + @Override + public String toString() { return getRequestDescription(); } +} + +public enum RequestType { + DEFEND_CASTLE, TORTURE_PRISONER, COLLECT_TAX +} +``` + +然後是請求處理器的層次結構 + +```java +@Slf4j +public abstract class RequestHandler { + private final RequestHandler next; + + public RequestHandler(RequestHandler next) { + this.next = next; + } + + public void handleRequest(Request req) { + if (next != null) { + next.handleRequest(req); + } + } + + protected void printHandling(Request req) { + LOGGER.info("{} handling request \"{}\"", this, req); + } + + @Override + public abstract String toString(); +} + +public class OrcCommander extends RequestHandler { + public OrcCommander(RequestHandler handler) { + super(handler); + } + + @Override + public void handleRequest(Request req) { + if (req.getRequestType().equals(RequestType.DEFEND_CASTLE)) { + printHandling(req); + req.markHandled(); + } else { + super.handleRequest(req); + } + } + + @Override + public String toString() { + return "Orc commander"; + } +} + +// OrcOfficer和OrcSoldier的定義與OrcCommander類似 + +``` + +然後我們有獸王下達命令並形成鏈條 + +```java +public class OrcKing { + RequestHandler chain; + + public OrcKing() { + buildChain(); + } + + private void buildChain() { + chain = new OrcCommander(new OrcOfficer(new OrcSoldier(null))); + } + + public void makeRequest(Request req) { + chain.handleRequest(req); + } +} +``` + +然後這樣使用它 + +```java +var king = new OrcKing(); +king.makeRequest(new Request(RequestType.DEFEND_CASTLE, "defend castle")); // Orc commander handling request "defend castle" +king.makeRequest(new Request(RequestType.TORTURE_PRISONER, "torture prisoner")); // Orc officer handling request "torture prisoner" +king.makeRequest(new Request(RequestType.COLLECT_TAX, "collect tax")); // Orc soldier handling request "collect tax" +``` + +## 類圖 +![alt text](./etc/chain-of-responsibility.urm.png "Chain of Responsibility class diagram") + +## 適用性 +使用責任鏈模式當 + +* 多於一個物件可能要處理請求,並且處理器並不知道一個優先順序。處理器應自動確定。 +* 你想對多個物件之一發出請求而無需明確指定接收者 +* 處理請求的物件集合應該被動態指定時 + +## Java世界例子 + +* [java.util.logging.Logger#log()](http://docs.oracle.com/javase/8/docs/api/java/util/logging/Logger.html#log%28java.util.logging.Level,%20java.lang.String%29) +* [Apache Commons Chain](https://commons.apache.org/proper/commons-chain/index.html) +* [javax.servlet.Filter#doFilter()](http://docs.oracle.com/javaee/7/api/javax/servlet/Filter.html#doFilter-javax.servlet.ServletRequest-javax.servlet.ServletResponse-javax.servlet.FilterChain-) + +## 鳴謝 + +* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59) +* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b) diff --git a/localization/zh-TW/chain/etc/chain-of-responsibility.urm.png b/localization/zh-TW/chain/etc/chain-of-responsibility.urm.png new file mode 100644 index 000000000000..c3a4c80ba322 Binary files /dev/null and b/localization/zh-TW/chain/etc/chain-of-responsibility.urm.png differ diff --git a/localization/zh-TW/circuit-breaker/README.md b/localization/zh-TW/circuit-breaker/README.md new file mode 100644 index 000000000000..62aecb090b95 --- /dev/null +++ b/localization/zh-TW/circuit-breaker/README.md @@ -0,0 +1,314 @@ +--- +title: Circuit Breaker +shortTitle: Circuit Breaker +category: Behavioral +language: zh +tag: + - Performance + - Decoupling + - Cloud distributed +--- + +## 意圖 + +以這樣一種方式處理昂貴的遠端服務呼叫,即單個服務/元件的故障不會導致整個應用程式宕機,我們可以儘快重新連線到服務。 + +## 解釋 + +真實世界例子 + +> 想象一個 Web 應用程式,它同時具有用於獲取資料的本地檔案/影象和遠端服務。 這些遠端服務有時可能健康且響應迅速,或者由於各種原因可能在某 個時間點變得緩慢和無響應。因此,如果其中一個遠端服務緩慢或未成功響應,我們的應用程式將嘗試使用多個執行緒/程序從遠端服務獲取響應,很快它們都會掛起(也稱為 [執行緒飢餓][thread starvation](https://en.wikipedia.org/wiki/Starvation_(computer_science)))導致我們的整個 Web 應用程式崩潰。我們應該能夠檢測到這種情況並向使用者顯示適當的訊息,以便他/她可以探索不受遠端服務故障影響的應用程式的其他部分。 同時,其他正常工作的服務應保持正常執行,不受此故障的影響。 +> + +通俗地說 + +> 斷路器允許優雅地處理失敗的遠端服務。當我們應用程式的所有部分彼此高度解耦時,它特別有用,一個元件的故障並不意味著其他部分將停止工作。 + +維基百科說 + +> 斷路器是現代軟體開發中使用的一種設計模式。 它用於檢測故障並封裝防止故障不斷重複發生、維護期間、臨時外部系統故障或意外系統困難的邏輯。 + +## 程式示例 + +So, how does this all come together? With the above example in mind we will imitate the +functionality in a simple example. A monitoring service mimics the web app and makes both local and +remote calls. + +那麼,這一切是如何結合在一起的呢? 記住上面的例子,我們將在一個簡單的例子中模仿這個功能。 監控服務模仿 Web 應用程式並進行本地和遠端呼叫。 + +服務架構如下: + +![alt text](./etc/ServiceDiagram.png "Service Diagram") + +在程式碼方面,終端使用者應用程式是: + +```java +@Slf4j +public class App { + + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + + /** + * Program entry point. + * + * @param args command line args + */ + public static void main(String[] args) { + + var serverStartTime = System.nanoTime(); + + var delayedService = new DelayedRemoteService(serverStartTime, 5); + var delayedServiceCircuitBreaker = new DefaultCircuitBreaker(delayedService, 3000, 2, + 2000 * 1000 * 1000); + + var quickService = new QuickRemoteService(); + var quickServiceCircuitBreaker = new DefaultCircuitBreaker(quickService, 3000, 2, + 2000 * 1000 * 1000); + + // 建立一個可以進行本地和遠端呼叫的監控服務物件 + var monitoringService = new MonitoringService(delayedServiceCircuitBreaker, + quickServiceCircuitBreaker); + + // 獲取本地資源 + LOGGER.info(monitoringService.localResourceResponse()); + + // 從延遲服務中獲取響應 2 次,以滿足失敗閾值 + LOGGER.info(monitoringService.delayedServiceResponse()); + LOGGER.info(monitoringService.delayedServiceResponse()); + + // 在超過故障閾值限制後獲取延遲服務斷路器的當前狀態 + // 現在是開啟狀態 + LOGGER.info(delayedServiceCircuitBreaker.getState()); + + // 同時,延遲服務宕機,從健康快速服務獲取響應 + LOGGER.info(monitoringService.quickServiceResponse()); + LOGGER.info(quickServiceCircuitBreaker.getState()); + + // 等待延遲的服務響應 + try { + LOGGER.info("Waiting for delayed service to become responsive"); + Thread.sleep(5000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + // 檢查延時斷路器的狀態,應該是HALF_OPEN + LOGGER.info(delayedServiceCircuitBreaker.getState()); + + // 從延遲服務中獲取響應,現在應該是健康的 + LOGGER.info(monitoringService.delayedServiceResponse()); + // 獲取成功響應後,它的狀態應該是關閉。 + LOGGER.info(delayedServiceCircuitBreaker.getState()); + } +} +``` + +監控服務類: + +```java +public class MonitoringService { + + private final CircuitBreaker delayedService; + + private final CircuitBreaker quickService; + + public MonitoringService(CircuitBreaker delayedService, CircuitBreaker quickService) { + this.delayedService = delayedService; + this.quickService = quickService; + } + + // 假設:本地服務不會失敗,無需將其包裝在斷路器邏輯中 + public String localResourceResponse() { + return "Local Service is working"; + } + + /** + * Fetch response from the delayed service (with some simulated startup time). + * + * @return response string + */ + public String delayedServiceResponse() { + try { + return this.delayedService.attemptRequest(); + } catch (RemoteServiceException e) { + return e.getMessage(); + } + } + + /** + * Fetches response from a healthy service without any failure. + * + * @return response string + */ + public String quickServiceResponse() { + try { + return this.quickService.attemptRequest(); + } catch (RemoteServiceException e) { + return e.getMessage(); + } + } +} +``` +可以看出,它直接呼叫獲取本地資源,但它將對遠端(昂貴)服務的呼叫包裝在斷路器物件中,防止故障如下: + +```java +public class DefaultCircuitBreaker implements CircuitBreaker { + + private final long timeout; + private final long retryTimePeriod; + private final RemoteService service; + long lastFailureTime; + private String lastFailureResponse; + int failureCount; + private final int failureThreshold; + private State state; + private final long futureTime = 1000 * 1000 * 1000 * 1000; + + /** + * Constructor to create an instance of Circuit Breaker. + * + * @param timeout Timeout for the API request. Not necessary for this simple example + * @param failureThreshold Number of failures we receive from the depended service before changing + * state to 'OPEN' + * @param retryTimePeriod Time period after which a new request is made to remote service for + * status check. + */ + DefaultCircuitBreaker(RemoteService serviceToCall, long timeout, int failureThreshold, + long retryTimePeriod) { + this.service = serviceToCall; + // 我們從關閉狀態開始希望一切都是正常的 + this.state = State.CLOSED; + this.failureThreshold = failureThreshold; + // API的超時時間. + // 用於在超過限制時中斷對遠端資源的呼叫 + this.timeout = timeout; + this.retryTimePeriod = retryTimePeriod; + //An absurd amount of time in future which basically indicates the last failure never happened + this.lastFailureTime = System.nanoTime() + futureTime; + this.failureCount = 0; + } + + // 重置所有 + @Override + public void recordSuccess() { + this.failureCount = 0; + this.lastFailureTime = System.nanoTime() + futureTime; + this.state = State.CLOSED; + } + + @Override + public void recordFailure(String response) { + failureCount = failureCount + 1; + this.lastFailureTime = System.nanoTime(); + // Cache the failure response for returning on open state + this.lastFailureResponse = response; + } + + // 根據 failureThreshold、failureCount 和 lastFailureTime 評估當前狀態。 + protected void evaluateState() { + if (failureCount >= failureThreshold) { //Then something is wrong with remote service + if ((System.nanoTime() - lastFailureTime) > retryTimePeriod) { + // 我們已經等得夠久了,應該嘗試檢查服務是否已啟動 + state = State.HALF_OPEN; + } else { + // 服務可能仍會出現故障 + state = State.OPEN; + } + } else { + // 一切正常 + state = State.CLOSED; + } + } + + @Override + public String getState() { + evaluateState(); + return state.name(); + } + + /** + * Break the circuit beforehand if it is known service is down Or connect the circuit manually if + * service comes online before expected. + * + * @param state State at which circuit is in + */ + @Override + public void setState(State state) { + this.state = state; + switch (state) { + case OPEN -> { + this.failureCount = failureThreshold; + this.lastFailureTime = System.nanoTime(); + } + case HALF_OPEN -> { + this.failureCount = failureThreshold; + this.lastFailureTime = System.nanoTime() - retryTimePeriod; + } + default -> this.failureCount = 0; + } + } + + /** + * Executes service call. + * + * @return Value from the remote resource, stale response or a custom exception + */ + @Override + public String attemptRequest() throws RemoteServiceException { + evaluateState(); + if (state == State.OPEN) { + // 如果電路處於開啟狀態,則返回快取的響應 + return this.lastFailureResponse; + } else { + // 如果電路未開啟,則發出 API 請求 + try { + //在實際應用程式中,這將線上程中執行,並且將利用斷路器的超時引數來了解服務 + // 是否正在工作。 在這裡,我們根據伺服器響應本身模擬 + var response = service.call(); + // api 響應正常,重置所有。 + recordSuccess(); + return response; + } catch (RemoteServiceException ex) { + recordFailure(ex.getMessage()); + throw ex; + } + } + } +} +``` + +上述模式如何防止失敗? 讓我們透過它實現的這個有限狀態機來理解。 + +![alt text](./etc/StateDiagram.png "State Diagram") + +- 我們使用某些引數初始化斷路器物件:`timeout`、`failureThreshold` 和 `retryTimePeriod`,這有助於確定 API 的彈性。 +- 最初,我們處於“關閉”狀態,沒有發生對 API 的遠端呼叫。 +- 每次呼叫成功時,我們都會將狀態重置為開始時的狀態。 +- 如果失敗次數超過某個閾值,我們將進入“open”狀態,這就像開路一樣,阻止遠端服務呼叫,從而節省資源。 (這裡,我們從 API 返回名為 ```stale response``` 的響應) +- 一旦超過重試超時時間,我們就會進入“半開”狀態並再次呼叫遠端服務以檢查服務是否正常工作,以便我們可以提供新鮮內容。 失敗將其設定回“開啟”狀態,並在重試超時時間後進行另一次嘗試,而成功將其設定為“關閉”狀態,以便一切重新開始正常工作。 + +## 類圖 + +![alt text](./etc/circuit-breaker.urm.png "Circuit Breaker class diagram") + +## 適用性 + +在以下情況下使用斷路器模式 + +- 構建一個容錯應用程式,其中某些服務的故障不應導致整個應用程式宕機。 +- 構建一個持續執行(永遠線上)的應用程式,這樣它的元件就可以在不完全關閉的情況下升級。 + +## 相關模式 + +- [Retry Pattern](https://github.com/iluwatar/java-design-patterns/tree/master/retry) + +## 真實世界例子 + +* [Spring Circuit Breaker module](https://spring.io/guides/gs/circuit-breaker) +* [Netflix Hystrix API](https://github.com/Netflix/Hystrix) + +## 鳴謝 + +* [Understanding Circuit Breaker Pattern](https://itnext.io/understand-circuitbreaker-design-pattern-with-simple-practical-example-92a752615b42) +* [Martin Fowler on Circuit Breaker](https://martinfowler.com/bliki/CircuitBreaker.html) +* [Fault tolerance in a high volume, distributed system](https://medium.com/netflix-techblog/fault-tolerance-in-a-high-volume-distributed-system-91ab4faae74a) +* [Circuit Breaker pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/circuit-breaker) diff --git a/localization/zh-TW/circuit-breaker/etc/ServiceDiagram.png b/localization/zh-TW/circuit-breaker/etc/ServiceDiagram.png new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/localization/zh-TW/circuit-breaker/etc/StateDiagram.png b/localization/zh-TW/circuit-breaker/etc/StateDiagram.png new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/localization/zh-TW/circuit-breaker/etc/circuit-breaker.urm.png b/localization/zh-TW/circuit-breaker/etc/circuit-breaker.urm.png new file mode 100644 index 000000000000..fc90318ec8da Binary files /dev/null and b/localization/zh-TW/circuit-breaker/etc/circuit-breaker.urm.png differ diff --git a/localization/zh-TW/cloud-static-content-hosting/README.md b/localization/zh-TW/cloud-static-content-hosting/README.md new file mode 100644 index 000000000000..6eb6335b5f9d --- /dev/null +++ b/localization/zh-TW/cloud-static-content-hosting/README.md @@ -0,0 +1,132 @@ +--- +title: Static Content Hosting +shortTitle: Static Content Hosting +category: Cloud +language: zh +tag: +- Cloud distributed +--- + +## 意圖 + +將靜態內容部署到基於雲的儲存服務,該服務可以將它們直接交付給客戶端。 這可以減少對昂貴計算例項的需求。 + +## 解釋 + +真實世界例子 + +> 全球性的營銷網站(靜態內容)需要快速的部署以開始吸引潛在的客戶。為了將託管費用和維護成本降至最低,使用雲託管儲存服務和內容交付網路。 + +通俗地說 + +> 靜態內容託管模式利用雲原生儲存服務來儲存內容和全球內容交付網路,將其快取在世界各地的多個資料中心。 在靜態網站上,單個網頁包含靜態內容。 它們還可能包含客戶端指令碼,例如 Javascript。相比之下,動態網站依賴於伺服器端處理,包括伺服器端指令碼,如 PHP、JSP 或 ASP.NET。 + +維基百科說 + +> 與由 Web 應用程式生成的動態網頁相反,靜態網頁(有時稱為平面網頁或固定網頁)是完全按照儲存的方式傳送到使用者的網頁瀏覽器的網頁。靜態網頁適用於從不或很少需要更新的內容,儘管現代 +> Web 模板系統正在改變這一點。可以將大量靜態頁面作為檔案進行維護,沒有自動化工具(例如靜態站點生成器)是不切實際的。 + +**示例** + +![alt text](./etc/static-content-hosting.png "Static Content Hosting") + +在這個例子中我們使用AWS S3建立一個靜態網站,並利用 AWS Cloudfront 在全球範圍內分發內容。 + +1. 首先你需要一個AWS賬戶,你可以在這個建立一個免費的:[AWS Free Tier](https://aws.amazon.com/free/free-tier/) + +2. 登陸 [AWS控制檯](https://console.aws.amazon.com/console/home?nc2=h_ct&src=header-signin) + +3. 進入身份和接入管理服務 (IAM) . + +4. 建立一個僅具有此應用程式必要許可權的IAM使用者。 + + * 點選 `使用者` + * 點選 `新增使用者`. 選擇你想要的 `使用者名稱`, `接入型別`應該是 `程式設計式接入`. 點選 `下一步: 許可權`. + * 選擇 `直接附加已存在的策略`. 選擇 `AmazonS3FullAccess` 和 `CloudFrontFullAccess`. Click `下一步: 標籤`. + * 沒有需要的標籤, 所以直接點選 `下一步: 回顧`. + * 檢查呈現的資訊,沒問題的話點選`建立使用者` + * 完成這個示例所需要的`訪問秘鑰Id`和`訪問秘鑰密碼`將會呈現在你面前,請妥善保管。 + * 點選 `關閉`. + +5. [安裝AWS 命令列工具 (AWS CLI)](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv1.html) 來獲得程式設計式訪問AWS雲。 + +6. 使用`aws configure`命令來配置AWS CLI [說明書](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-quickstart.html#cli-configure-quickstart-config) + +7. 為web站點建立AWS S3 bucket。 注意S3 bucket名字必須要在全球範圍內唯一。 + + + * 語法是 `aws s3 mb ` [說明書](https://docs.aws.amazon.com/cli/latest/userguide/cli-services-s3-commands.html#using-s3-commands-managing-buckets-creating) + * 比如 `aws s3 mb s3://my-static-website-jh34jsjmg` + * 使用列出現有儲存桶的命令`aws s3 ls`驗證儲存桶是否已成功建立 + +8. 使用命令`aws s3 website`來配置bucket作為web站點。 [說明書](https://docs.aws.amazon.com/cli/latest/reference/s3/website.html). + + * 比如`aws s3 website s3://my-static-website-jh34jsjmg --index-document index.html --error-document error.html` + +9. 上傳內容到bucket中。 + * 首先建立內容,至少包含`index.html`和`error.html`文件。 + * 上傳內容到你的bucket中。 [說明書](https://docs.aws.amazon.com/cli/latest/userguide/cli-services-s3-commands.html#using-s3-commands-managing-objects-copy) + * 比如`aws s3 cp index.html s3://my-static-website-jh34jsjmg` and `aws s3 cp error.html s3://my-static-website-jh34jsjmg` + +10. 然後我們需要設定bucket的策略以允許讀取訪問。 + + * 使用以下內容建立`policy.json`(注意需要將bucket名稱替換為自己的)。 + + ```json + { + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "PublicReadGetObject", + "Effect": "Allow", + "Principal": "*", + "Action": "s3:GetObject", + "Resource": "arn:aws:s3:::my-static-website-jh34jsjmg/*" + } + ] + } + ``` + + * 根據這些設定桶策略[說明書](https://docs.aws.amazon.com/cli/latest/reference/s3api/put-bucket-policy.html) + * 比如 `aws s3api put-bucket-policy --bucket my-static-website-jh34jsjmg --policy file://policy.json` + +11. 使用瀏覽器測試web站點。 + + * web站點的URL格式是 `http://.s3-website-.amazonaws.com` + * 比如 這個站點建立在 `eu-west-1` 區域 ,名字是 `my-static-website-jh34jsjmg` 所以它可以透過 `http://my-static-website-jh34jsjmg.s3-website-eu-west-1.amazonaws.com`來訪問。 + +12. 為web站點建立CloudFormation 分發。 + + * 語法文件在這裡 [this reference](https://docs.aws.amazon.com/cli/latest/reference/cloudfront/create-distribution.html) + * 比如,最簡單的方式是使用命令l `aws cloudfront create-distribution --origin-domain-name my-static-website-jh34jsjmg.s3.amazonaws.com --default-root-object index.html` + * 也支援JSON格式的配置 比如使用 `--distribution-config file://dist-config.json` 來傳遞分發的配置檔案引數 + * 命令的舒勇將顯示準確的分配配置項,包括包括可用於測試的生成的 CloudFront 域名,例如 `d2k3xwnaqa8nqx.cloudfront.net` + * CloudFormation 分發部署需要一些時間,但一旦完成,你的網站就會從全球各地的資料中心提供服務! + +13. 就是這樣! 你已經實現了一個靜態網站,其內容分發網路以閃電般的速度在世界各地提供服務。 + + * 要更新網站,你需要更新 S3 儲存桶中的物件並使 CloudFront 分配中的物件無效 + * 要從 AWS CLI 執行此操作,請參閱 [this reference](https://docs.aws.amazon.com/cli/latest/reference/cloudfront/create-invalidation.html) + * 你可能想要做的進一步開發是透過 https 提供內容併為你的站點新增域名 + +## 適用性 + +當你想要執行以下操作時,請使用靜態內容託管模式: + +* 最小化包含一些靜態資源的網站和應用程式的託管成本。 +* 使用靜態內容構建全球可用的網站 +* 監控網站流量、頻寬使用、成本等。 + +## 典型用例 + +* 具有全球影響力的網站 +* 靜態網站生成器生成的內容 +* 沒有動態內容要求的網站 + +## 真實世界例子 + +* [Java Design Patterns web site](https://java-design-patterns.com) + +## 鳴謝 + +* [Static Content Hosting pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/static-content-hosting) diff --git a/localization/zh-TW/cloud-static-content-hosting/etc/static-content-hosting.png b/localization/zh-TW/cloud-static-content-hosting/etc/static-content-hosting.png new file mode 100644 index 000000000000..6e0baa95e0f9 Binary files /dev/null and b/localization/zh-TW/cloud-static-content-hosting/etc/static-content-hosting.png differ diff --git a/localization/zh-TW/collection-pipeline/README.md b/localization/zh-TW/collection-pipeline/README.md new file mode 100644 index 000000000000..3acf96e30814 --- /dev/null +++ b/localization/zh-TW/collection-pipeline/README.md @@ -0,0 +1,28 @@ +--- +title: Collection Pipeline +shortTitle: Collection Pipeline +category: Functional +language: zh +tag: + - Reactive +--- + +## 釋義 +**集合管道(Collection Pipeline)**包含**函式組合(Function Composition)**和**集合管道(Collection Pipeline)**兩組合概念,這是兩種函數語言程式設計模式,你可以在程式碼中結合這兩種模式來進行集合迭代。 +在函數語言程式設計中,可以透過一系列較小的模組化函式或操作來編排複雜的操作。這一系列函式被稱為函式組合。當一個資料集合流經一個函式組合時,它就成為一個集合管道。函式組合和集合管道是函數語言程式設計中經常使用的兩種設計模式。 + +## 類圖 +![alt text](./etc/collection-pipeline.png "Collection Pipeline") + +## 適用場景 +在以下場景適用集合管道模式: + +* 當你想執行一組連續的運算元操作,其中一個運算元收集的輸出需要被輸入到下一個運算元中 +* 當你在程式碼中需要使用大量的中間狀態語句時 +* 當你在程式碼中使用大量的迴圈語句時 + +## 引用 + +* [Function composition and the Collection Pipeline pattern](https://www.ibm.com/developerworks/library/j-java8idioms2/index.html) +* [Martin Fowler](https://martinfowler.com/articles/collection-pipeline/) +* [Java8 Streams](https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html) \ No newline at end of file diff --git a/localization/zh-TW/collection-pipeline/etc/collection-pipeline.png b/localization/zh-TW/collection-pipeline/etc/collection-pipeline.png new file mode 100644 index 000000000000..67d52629cb86 Binary files /dev/null and b/localization/zh-TW/collection-pipeline/etc/collection-pipeline.png differ diff --git a/localization/zh-TW/combinator/README.md b/localization/zh-TW/combinator/README.md new file mode 100644 index 000000000000..6fbab09b0e6a --- /dev/null +++ b/localization/zh-TW/combinator/README.md @@ -0,0 +1,208 @@ +--- +title: Combinator +shortTitle: Combinator +category: Idiom +language: zh +tag: + - Reactive +--- + +## 或稱 + +構圖模式 + +## 目的 + +功能模式代表了一種以組合功能為中心的圖書館組織風格。 +簡單地說,有一些型別 T,一些用於構造型別 T 的“原始”值的函式,以及一些可以以各種方式組合型別 T 的值以構建更復雜的型別 T 值的“組合器”。 + +## 解釋 + +真實世界例子 + +> 在電腦科學中,組合邏輯被用作計算的簡化模型,用於可計算性理論和證明理論。 儘管組合邏輯很簡單,但它捕獲了計算的許多基本特徵。 +> + +通俗的說 +> 組合器允許從先前定義的“事物”建立新的“事物”。 +> + +維基百科說 + +> 組合器是一個高階函式,僅使用函式應用程式和之前定義的組合器來定義其引數的結果。 +> + +**程式示例** + +翻譯上面的組合器示例。 首先,我們有一個由幾個方法`contains`, `not`, `or`, `and`組成的介面。 + +```java +// 用於查詢文字中的行的功能介面。 +public interface Finder { + + // 在文字中查詢行的函式。 + List find(String text); + + // 函式{@link #find(String)}的簡單實現。 + static Finder contains(String word) { + return txt -> Stream.of(txt.split("\n")) + .filter(line -> line.toLowerCase().contains(word.toLowerCase())) + .collect(Collectors.toList()); + } + + // 組合器:not。 + default Finder not(Finder notFinder) { + return txt -> { + List res = this.find(txt); + res.removeAll(notFinder.find(txt)); + return res; + }; + } + + // 組合器:or。 + default Finder or(Finder orFinder) { + return txt -> { + List res = this.find(txt); + res.addAll(orFinder.find(txt)); + return res; + }; + } + + // 組合器:and。 + default Finder and(Finder andFinder) { + return + txt -> this + .find(txt) + .stream() + .flatMap(line -> andFinder.find(line).stream()) + .collect(Collectors.toList()); + } + ... +} +``` + +然後我們還有另一個組合器用於一些複雜的查詢器`advancedFinder`, `filteredFinder`, `specializedFinder`和`expandedFinder`。 + +```java +// 由簡單取景器組成的複雜取景器。 +public class Finders { + + private Finders() { + } + + // Finder 用於查詢複雜的查詢。 + public static Finder advancedFinder(String query, String orQuery, String notQuery) { + return + Finder.contains(query) + .or(Finder.contains(orQuery)) + .not(Finder.contains(notQuery)); + } + + // 過濾查詢器也會查詢包含排除查詢的查詢。 + public static Finder filteredFinder(String query, String... excludeQueries) { + var finder = Finder.contains(query); + + for (String q : excludeQueries) { + finder = finder.not(Finder.contains(q)); + } + return finder; + } + + // 專門查詢。 每個下一個查詢都會在上一個結果中查詢。 + public static Finder specializedFinder(String... queries) { + var finder = identMult(); + + for (String query : queries) { + finder = finder.and(Finder.contains(query)); + } + return finder; + } + + // 擴充套件查詢。 尋找替代品。 + public static Finder expandedFinder(String... queries) { + var finder = identSum(); + + for (String query : queries) { + finder = finder.or(Finder.contains(query)); + } + return finder; + } + ... +} +``` + +現在我們已經建立了組合器的介面和方法。 現在我們有一個處理這些組合器的應用程式。 + +```java +var queriesOr = new String[]{"many", "Annabel"}; +var finder = Finders.expandedFinder(queriesOr); +var res = finder.find(text()); +LOGGER.info("the result of expanded(or) query[{}] is {}", queriesOr, res); + +var queriesAnd = new String[]{"Annabel", "my"}; +finder = Finders.specializedFinder(queriesAnd); +res = finder.find(text()); +LOGGER.info("the result of specialized(and) query[{}] is {}", queriesAnd, res); + +finder = Finders.advancedFinder("it was", "kingdom", "sea"); +res = finder.find(text()); +LOGGER.info("the result of advanced query is {}", res); + +res = Finders.filteredFinder(" was ", "many", "child").find(text()); +LOGGER.info("the result of filtered query is {}", res); + +private static String text() { + return + "It was many and many a year ago,\n" + + "In a kingdom by the sea,\n" + + "That a maiden there lived whom you may know\n" + + "By the name of ANNABEL LEE;\n" + + "And this maiden she lived with no other thought\n" + + "Than to love and be loved by me.\n" + + "I was a child and she was a child,\n" + + "In this kingdom by the sea;\n" + + "But we loved with a love that was more than love-\n" + + "I and my Annabel Lee;\n" + + "With a love that the winged seraphs of heaven\n" + + "Coveted her and me."; + } +``` + +**程式輸出:** + +```java +the result of expanded(or) query[[many, Annabel]] is [It was many and many a year ago,, By the name of ANNABEL LEE;, I and my Annabel Lee;] +the result of specialized(and) query[[Annabel, my]] is [I and my Annabel Lee;] +the result of advanced query is [It was many and many a year ago,] +the result of filtered query is [But we loved with a love that was more than love-] +``` + +現在我們可以設計我們的應用程式,使其具有查詢查詢功能`expandedFinder`, `specializedFinder`, `advancedFinder`, `filteredFinder`,這些功能均派生自`contains`, `or`, `not`, `and`。 + + +## 類圖 +![alt text](./etc/combinator.urm.png "Combinator class diagram") + +## 適用性 +在以下情況下使用組合器模式: + +- 你可以從更簡單的值建立更復雜的值,但具有相同的型別(它們的組合) + +## 好處 + +- 從開發人員的角度來看,API 由領域中的術語組成。 +- 組合階段和應用階段之間有明顯的區別。 +- 首先構造一個例項,然後執行它。 +- 這使得該模式適用於並行環境。 + + +## 現實世界的例子 + +- java.util.function.Function#compose +- java.util.function.Function#andThen + +## 鳴謝 + +- [Example for java](https://gtrefs.github.io/code/combinator-pattern/) +- [Combinator pattern](https://wiki.haskell.org/Combinator_pattern) +- [Combinatory logic](https://wiki.haskell.org/Combinatory_logic) diff --git a/localization/zh-TW/combinator/etc/combinator.urm.png b/localization/zh-TW/combinator/etc/combinator.urm.png new file mode 100644 index 000000000000..80fdd36340bb Binary files /dev/null and b/localization/zh-TW/combinator/etc/combinator.urm.png differ diff --git a/localization/zh-TW/command/README.md b/localization/zh-TW/command/README.md new file mode 100644 index 000000000000..8954aa9c832b --- /dev/null +++ b/localization/zh-TW/command/README.md @@ -0,0 +1,252 @@ +--- +title: Command +shortTitle: Command +category: Behavioral +language: zh +tag: + - Gang of Four +--- + +## 或稱 +行動, 事務模式 + +## 目的 +將請求封裝為物件,從而使你可以將具有不同請求的客戶端引數化,佇列或記錄請求,並且支援可撤銷操作。 + +## 解釋 +真實世界例子 + +> 有一個巫師在地精上施放咒語。咒語在地精上一一執行。第一個咒語使地精縮小,第二個使他不可見。然後巫師將咒語一個個的反轉。這裡的每一個咒語都是一個可撤銷的命令物件。 + +用通俗的話說 + +> 用命令物件的方式儲存請求以在將來時可以執行它或撤銷它。 + +維基百科說 + +> 在物件導向程式設計中,命令模式是一種行為型設計模式,它把在稍後執行的一個動作或觸發的一個事件所需要的所有資訊封裝到一個物件中。 + +**程式設計示例** + +這是巫師和地精的示例程式碼。讓我們從巫師類開始。 + +```java +public class Wizard { + + private static final Logger LOGGER = LoggerFactory.getLogger(Wizard.class); + + private final Deque undoStack = new LinkedList<>(); + private final Deque redoStack = new LinkedList<>(); + + public Wizard() {} + + public void castSpell(Command command, Target target) { + LOGGER.info("{} casts {} at {}", this, command, target); + command.execute(target); + undoStack.offerLast(command); + } + + public void undoLastSpell() { + if (!undoStack.isEmpty()) { + var previousSpell = undoStack.pollLast(); + redoStack.offerLast(previousSpell); + LOGGER.info("{} undoes {}", this, previousSpell); + previousSpell.undo(); + } + } + + public void redoLastSpell() { + if (!redoStack.isEmpty()) { + var previousSpell = redoStack.pollLast(); + undoStack.offerLast(previousSpell); + LOGGER.info("{} redoes {}", this, previousSpell); + previousSpell.redo(); + } + } + + @Override + public String toString() { + return "Wizard"; + } +} +``` + +接下來我們介紹咒語層級 + +```java +public interface Command { + + void execute(Target target); + + void undo(); + + void redo(); + + String toString(); +} + +public class InvisibilitySpell implements Command { + + private Target target; + + @Override + public void execute(Target target) { + target.setVisibility(Visibility.INVISIBLE); + this.target = target; + } + + @Override + public void undo() { + if (target != null) { + target.setVisibility(Visibility.VISIBLE); + } + } + + @Override + public void redo() { + if (target != null) { + target.setVisibility(Visibility.INVISIBLE); + } + } + + @Override + public String toString() { + return "Invisibility spell"; + } +} + +public class ShrinkSpell implements Command { + + private Size oldSize; + private Target target; + + @Override + public void execute(Target target) { + oldSize = target.getSize(); + target.setSize(Size.SMALL); + this.target = target; + } + + @Override + public void undo() { + if (oldSize != null && target != null) { + var temp = target.getSize(); + target.setSize(oldSize); + oldSize = temp; + } + } + + @Override + public void redo() { + undo(); + } + + @Override + public String toString() { + return "Shrink spell"; + } +} +``` + +最後我們有咒語的目標地精。 + +```java +public abstract class Target { + + private static final Logger LOGGER = LoggerFactory.getLogger(Target.class); + + private Size size; + + private Visibility visibility; + + public Size getSize() { + return size; + } + + public void setSize(Size size) { + this.size = size; + } + + public Visibility getVisibility() { + return visibility; + } + + public void setVisibility(Visibility visibility) { + this.visibility = visibility; + } + + @Override + public abstract String toString(); + + public void printStatus() { + LOGGER.info("{}, [size={}] [visibility={}]", this, getSize(), getVisibility()); + } +} + +public class Goblin extends Target { + + public Goblin() { + setSize(Size.NORMAL); + setVisibility(Visibility.VISIBLE); + } + + @Override + public String toString() { + return "Goblin"; + } + +} +``` + +最後是整個示例的實踐。 + +```java +var wizard = new Wizard(); +var goblin = new Goblin(); +goblin.printStatus(); +// Goblin, [size=normal] [visibility=visible] +wizard.castSpell(new ShrinkSpell(), goblin); +// Wizard casts Shrink spell at Goblin +goblin.printStatus(); +// Goblin, [size=small] [visibility=visible] +wizard.castSpell(new InvisibilitySpell(), goblin); +// Wizard casts Invisibility spell at Goblin +goblin.printStatus(); +// Goblin, [size=small] [visibility=invisible] +wizard.undoLastSpell(); +// Wizard undoes Invisibility spell +goblin.printStatus(); +// Goblin, [size=small] [visibility=visible] +``` + +## 類圖 +![alt text](./etc/command.png "Command") + +## 適用性 +使用命令模式當你想 + +* 透過操作將物件引數化。你可以使用回撥函式(即,已在某處註冊以便稍後呼叫的函式)以過程語言表示這種引數化。命令是回撥的一種物件導向替代方案。 +* 在不同的時間指定,排隊和執行請求。一個命令物件的生存期可以獨立於原始請求。如果請求的接收方可以以地址空間無關的方式來表示,那麼你可以將請求的命令物件傳輸到其他程序並在那裡執行請求。 +* 支援撤銷。命令的執行操作可以在命令本身中儲存狀態以反轉其效果。命令介面必須有新增的反執行操作,該操作可以逆轉上一次執行呼叫的效果。執行的命令儲存在歷史列表中。無限撤消和重做透過分別向後和向前遍歷此列表來實現,分別呼叫unexecute和execute。 +* 支援日誌記錄更改,以便在系統崩潰時可以重新應用它們。透過使用載入和儲存操作擴充套件命令介面,你可以保留更改的永久日誌。從崩潰中恢復涉及從磁碟重新載入記錄的命令,並透過執行操作重新執行它們。 +* 透過原始的操作來構建一個以高階操作圍繞的系統。這種結構在支援事務的資訊系統中很常見。事務封裝了一組資料更改。命令模式提供了一種對事務進行建模的方法。命令具有公共介面,讓你以相同的方式呼叫所有事務。該模式還可以透過新的事務來輕鬆擴充套件系統。 + +## 典型用例 + +* 保留請求歷史 +* 實現回撥功能 +* 實現撤銷功能 + +## Java世界例子 + +* [java.lang.Runnable](http://docs.oracle.com/javase/8/docs/api/java/lang/Runnable.html) +* [org.junit.runners.model.Statement](https://github.com/junit-team/junit4/blob/master/src/main/java/org/junit/runners/model/Statement.java) +* [Netflix Hystrix](https://github.com/Netflix/Hystrix/wiki) +* [javax.swing.Action](http://docs.oracle.com/javase/8/docs/api/javax/swing/Action.html) + +## 鳴謝 + +* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59) +* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b) +* [Refactoring to Patterns](https://www.amazon.com/gp/product/0321213351/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321213351&linkCode=as2&tag=javadesignpat-20&linkId=2a76fcb387234bc71b1c61150b3cc3a7) +* [J2EE Design Patterns](https://www.amazon.com/gp/product/0596004273/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596004273&linkCode=as2&tag=javadesignpat-20&linkId=f27d2644fbe5026ea448791a8ad09c94) diff --git a/localization/zh-TW/command/etc/command.png b/localization/zh-TW/command/etc/command.png new file mode 100644 index 000000000000..0f026464ecc4 Binary files /dev/null and b/localization/zh-TW/command/etc/command.png differ diff --git a/localization/zh-TW/commander/README.md b/localization/zh-TW/commander/README.md new file mode 100644 index 000000000000..459358afd47d --- /dev/null +++ b/localization/zh-TW/commander/README.md @@ -0,0 +1,25 @@ +--- +title: Commander +shortTitle: Commander +category: Concurrency +language: zh +tag: + - Cloud distributed +--- + +## 目的 + +> 用於處理執行分散式事務時可能遇到的所有問題。 + +## 類圖 +![alt text](./etc/commander.urm.png "Commander class diagram") + +## 適用場合 +當我們需要提交兩個資料庫去完成事務,提交不是原子性且可能因此造成問題時,適合用這個設計模式。 + +## 解釋 +處理分散式事務很棘手,但如果我們不仔細處理,可能會帶來不想要的後果。假設我們有一個電子商務網站,它有一個支付微服務和一個運輸微服務。如果當前運輸可用,但支付服務不可用,或者反之,當我們已經收到使用者的訂單後,我們應該如何處理?我們需要有一個機制來處理這些情況。我們必須將訂單指向其中一個服務(在這個例子中是運輸),然後將訂單新增到另一個服務的資料庫中(在這個例子中是支付),因為兩個資料庫不能原子地更新。如果我們當前無法做到這一點,應該有一個佇列,可以將這個請求排隊,並且必須有一個機制,允許佇列中出現失敗。所有這些都需要透過不斷的重試,在保證冪等性(即使請求多次,變化只應用一次)的情況下,由一個指揮類來完成,以達到最終一致性的狀態。 + +## 鳴謝 + +* [Distributed Transactions: The Icebergs of Microservices](https://www.grahamlea.com/2016/08/distributed-transactions-microservices-icebergs/) diff --git a/localization/zh-TW/commander/etc/commander.urm.png b/localization/zh-TW/commander/etc/commander.urm.png new file mode 100644 index 000000000000..6b5ebba75bd6 Binary files /dev/null and b/localization/zh-TW/commander/etc/commander.urm.png differ diff --git a/localization/zh-TW/composite-entity/README.md b/localization/zh-TW/composite-entity/README.md new file mode 100644 index 000000000000..d3eb37290191 --- /dev/null +++ b/localization/zh-TW/composite-entity/README.md @@ -0,0 +1,120 @@ +--- +title: Composite Entity +shortTitle: Composite Entity +category: Structural +language: zh +tag: + - Enterprise Integration Pattern +--- + +## 含義 + +複合實體模式用於對一組相關聯的持久化物件進行建模、描述和管理,用於取代對這組物件描述為單獨粒度的實體。 + +## 解釋 + +現例項子 + +> 對於一個控制檯物件,需要管理許多介面功能。透過使用複合實體模式,將訊息物件、訊號物件等依賴性物件組合在一起,直接使用單個物件對其進行控制。 + +簡單地說 + +> 複合實體模式允許使用一個統一物件來管理一組相互關聯的物件 + +**程式設計示例** + +我們需要一個通用的解決方案來解決上述的控制檯問題。我們引入了以下的通用複合物件。 + +```java +public abstract class DependentObject { + + T data; + + public void setData(T message) { + this.data = message; + } + + public T getData() { + return data; + } +} + +public abstract class CoarseGrainedObject { + + DependentObject[] dependentObjects; + + public void setData(T... data) { + IntStream.range(0, data.length).forEach(i -> dependentObjects[i].setData(data[i])); + } + + public T[] getData() { + return (T[]) Arrays.stream(dependentObjects).map(DependentObject::getData).toArray(); + } +} + +``` + +專用的 `console` 複合實體繼承自這個基類,如下所示。 + +```java +public class MessageDependentObject extends DependentObject { + +} + +public class SignalDependentObject extends DependentObject { + +} + +public class ConsoleCoarseGrainedObject extends CoarseGrainedObject { + + @Override + public String[] getData() { + super.getData(); + return new String[]{ + dependentObjects[0].getData(), dependentObjects[1].getData() + }; + } + + public void init() { + dependentObjects = new DependentObject[]{ + new MessageDependentObject(), new SignalDependentObject()}; + } +} + +public class CompositeEntity { + + private final ConsoleCoarseGrainedObject console = new ConsoleCoarseGrainedObject(); + + public void setData(String message, String signal) { + console.setData(message, signal); + } + + public String[] getData() { + return console.getData(); + } +} +``` + +現在我們使用 `console` 複合實體來進行訊息物件、訊號物件的分配。 + +```java +var console = new CompositeEntity(); +console.init(); +console.setData("No Danger", "Green Light"); +Arrays.stream(console.getData()).forEach(LOGGER::info); +console.setData("Danger", "Red Light"); +Arrays.stream(console.getData()).forEach(LOGGER::info); +``` + +## 類圖 + +![alt text](./etc/composite_entity.urm.png "Composite Entity Pattern") + +## 適用場景 + +複合實體模式適用於以下場景: + +* 你想要透過一個物件來管理多個依賴物件,已調整物件之間的細化程度。同時將依賴物件的生命週期託管到這個粗粒度的複合實體物件。 +## 引用 + +* [Composite Entity Pattern in wikipedia](https://en.wikipedia.org/wiki/Composite_entity_pattern) \ No newline at end of file diff --git a/localization/zh-TW/composite-entity/etc/composite_entity.urm.png b/localization/zh-TW/composite-entity/etc/composite_entity.urm.png new file mode 100644 index 000000000000..d6c29a718837 Binary files /dev/null and b/localization/zh-TW/composite-entity/etc/composite_entity.urm.png differ diff --git a/localization/zh-TW/composite/README.md b/localization/zh-TW/composite/README.md new file mode 100644 index 000000000000..55beeab69027 --- /dev/null +++ b/localization/zh-TW/composite/README.md @@ -0,0 +1,173 @@ +--- +title: Composite +shortTitle: Composite +category: Structural +language: zh +tag: + - Gang of Four +--- + +## 目的 + +將物件組合成樹結構以表示部分整體層次結構。 組合可以使客戶統一對待單個物件和組合物件。 + +## 解釋 + +真實世界例子 + +> 每個句子由單片語成,單詞又由字元組成。這些物件中的每一個都是可列印的,它們可以在它們之前或之後列印一些內容,例如句子始終以句號結尾,單詞始終在其前面有空格。 + +通俗的說 + +> 組合模式使客戶能夠以統一的方式對待各個物件。 + +維基百科說 + +> 在軟體工程中,組合模式是一種分割槽設計模式。組合模式中,一組物件將像一個物件的單獨例項一樣被對待。組合的目的是將物件“組成”樹狀結構,以表示部分整體層次結構。實現組合模式可使客戶統一對待單個物件和組合物件。 + +**程式示例** + +使用上面的句子例子。 這裡我們有基類`LetterComposite`和不同的可列印型別`Letter`,`Word`和`Sentence`。 + +```java +public abstract class LetterComposite { + + private final List children = new ArrayList<>(); + + public void add(LetterComposite letter) { + children.add(letter); + } + + public int count() { + return children.size(); + } + + protected void printThisBefore() { + } + + protected void printThisAfter() { + } + + public void print() { + printThisBefore(); + children.forEach(LetterComposite::print); + printThisAfter(); + } +} + +public class Letter extends LetterComposite { + + private final char character; + + public Letter(char c) { + this.character = c; + } + + @Override + protected void printThisBefore() { + System.out.print(character); + } +} + +public class Word extends LetterComposite { + + public Word(List letters) { + letters.forEach(this::add); + } + + public Word(char... letters) { + for (char letter : letters) { + this.add(new Letter(letter)); + } + } + + @Override + protected void printThisBefore() { + System.out.print(" "); + } +} + +public class Sentence extends LetterComposite { + + public Sentence(List words) { + words.forEach(this::add); + } + + @Override + protected void printThisAfter() { + System.out.print("."); + } +} +``` + +然後我們有一個訊息攜帶者來攜帶訊息。 + +```java +public class Messenger { + + LetterComposite messageFromOrcs() { + + var words = List.of( + new Word('W', 'h', 'e', 'r', 'e'), + new Word('t', 'h', 'e', 'r', 'e'), + new Word('i', 's'), + new Word('a'), + new Word('w', 'h', 'i', 'p'), + new Word('t', 'h', 'e', 'r', 'e'), + new Word('i', 's'), + new Word('a'), + new Word('w', 'a', 'y') + ); + + return new Sentence(words); + + } + + LetterComposite messageFromElves() { + + var words = List.of( + new Word('M', 'u', 'c', 'h'), + new Word('w', 'i', 'n', 'd'), + new Word('p', 'o', 'u', 'r', 's'), + new Word('f', 'r', 'o', 'm'), + new Word('y', 'o', 'u', 'r'), + new Word('m', 'o', 'u', 't', 'h') + ); + + return new Sentence(words); + + } + +} +``` + +然後它可以這樣使用: + +```java +var orcMessage = new Messenger().messageFromOrcs(); +orcMessage.print(); // Where there is a whip there is a way. +var elfMessage = new Messenger().messageFromElves(); +elfMessage.print(); // Much wind pours from your mouth. +``` + +## 類圖 + +![alt text](./etc/composite.urm.png "Composite class diagram") + +## 適用性 + +使用組合模式當 + +* 你想要表示物件的整體層次結構 +* 你希望客戶能夠忽略組合物件和單個物件之間的差異。 客戶將統一對待組合結構中的所有物件。 + +## 真實世界例子 + +* [java.awt.Container](http://docs.oracle.com/javase/8/docs/api/java/awt/Container.html) and [java.awt.Component](http://docs.oracle.com/javase/8/docs/api/java/awt/Component.html) +* [Apache Wicket](https://github.com/apache/wicket) component tree, see [Component](https://github.com/apache/wicket/blob/91e154702ab1ff3481ef6cbb04c6044814b7e130/wicket-core/src/main/java/org/apache/wicket/Component.java) and [MarkupContainer](https://github.com/apache/wicket/blob/b60ec64d0b50a611a9549809c9ab216f0ffa3ae3/wicket-core/src/main/java/org/apache/wicket/MarkupContainer.java) + +## 鳴謝 + +* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59) +* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b) +* [Refactoring to Patterns](https://www.amazon.com/gp/product/0321213351/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321213351&linkCode=as2&tag=javadesignpat-20&linkId=2a76fcb387234bc71b1c61150b3cc3a7) diff --git a/localization/zh-TW/composite/etc/composite.urm.png b/localization/zh-TW/composite/etc/composite.urm.png new file mode 100644 index 000000000000..93c160f6450a Binary files /dev/null and b/localization/zh-TW/composite/etc/composite.urm.png differ diff --git a/localization/zh-TW/converter/README.md b/localization/zh-TW/converter/README.md new file mode 100644 index 000000000000..a765618bb672 --- /dev/null +++ b/localization/zh-TW/converter/README.md @@ -0,0 +1,100 @@ +--- +title: Converter +shortTitle: Converter +category: Creational +language: zh +tag: + - Decoupling +--- + +## 目的 + +轉換器模式的目的是提供相應型別之間雙向轉換的通用方法,允許進行乾淨的實現,而型別之間無需相互瞭解。此外,Converter模式引入了雙向集合對映,從而將樣板程式碼減少到最少。 + +## 解釋 + +真實世界例子 + +> 在真實的應用中經常有這種情況,資料庫層包含需要被轉換成業務邏輯層DTO來使用的實體。對於潛在的大量類進行類似的對映,我們需要一種通用的方法來實現這一點。 + +通俗的說 + +> 轉換器模式讓一個類的例項對映成另一個類的例項變得簡單 + +**程式示例** + +我們需要一個通用的方案來解決對映問題。讓我們來介紹一個通用的轉換器。 + +```java +public class Converter { + + private final Function fromDto; + private final Function fromEntity; + + public Converter(final Function fromDto, final Function fromEntity) { + this.fromDto = fromDto; + this.fromEntity = fromEntity; + } + + public final U convertFromDto(final T dto) { + return fromDto.apply(dto); + } + + public final T convertFromEntity(final U entity) { + return fromEntity.apply(entity); + } + + public final List createFromDtos(final Collection dtos) { + return dtos.stream().map(this::convertFromDto).collect(Collectors.toList()); + } + + public final List createFromEntities(final Collection entities) { + return entities.stream().map(this::convertFromEntity).collect(Collectors.toList()); + } +} +``` + +專屬的轉換器像下面一樣從基類繼承。 + +```java +public class UserConverter extends Converter { + + public UserConverter() { + super(UserConverter::convertToEntity, UserConverter::convertToDto); + } + + private static UserDto convertToDto(User user) { + return new UserDto(user.getFirstName(), user.getLastName(), user.isActive(), user.getUserId()); + } + + private static User convertToEntity(UserDto dto) { + return new User(dto.getFirstName(), dto.getLastName(), dto.isActive(), dto.getEmail()); + } + +} +``` + +現在,在User和UserDto之間的對映變得輕而易舉。 + +```java +var userConverter = new UserConverter(); +var dtoUser = new UserDto("John", "Doe", true, "whatever[at]wherever.com"); +var user = userConverter.convertFromDto(dtoUser); +``` + +## 類圖 + +![alt text](./etc/converter.png "Converter Pattern") + +## 適用性 + +在下面這些情況下使用轉換器模式: + +* 如果你的型別在邏輯上相互對應,並需要在它們之間轉換實體 +* 當你想根據上下文提供不同的型別轉換方式時 +* 每當你引入DTO(資料傳輸物件)時你可能都需要將其轉換為 + DO + +## 鳴謝 + +* [Converter](http://www.xsolve.pl/blog/converter-pattern-in-java-8/) diff --git a/localization/zh-TW/converter/etc/converter.png b/localization/zh-TW/converter/etc/converter.png new file mode 100644 index 000000000000..01435ef5ae29 Binary files /dev/null and b/localization/zh-TW/converter/etc/converter.png differ diff --git a/localization/zh-TW/crtp/README.md b/localization/zh-TW/crtp/README.md new file mode 100644 index 000000000000..0cc27fb2c78c --- /dev/null +++ b/localization/zh-TW/crtp/README.md @@ -0,0 +1,135 @@ +--- +title: Curiously Recurring Template Pattern +shortTitle: Curiously Recurring Template Pattern +language: zh +category: Structural +tag: +- Extensibility +- Instantiation +--- + +## 名稱/分類 + +Curiously Recurring Template Pattern,CRTP,奇異遞迴模板模式 + +## 別名 + +遞迴型別繫結,遞迴泛型 + +## 目的 + +允許派生元件從與派生型別相容的基本元件繼承某些功能。 + +## 解釋 + +真實世界的例子 + +> 對於正在策劃賽事的綜合格鬥推廣活動來說,確保在相同重量級的運動員之間組織比賽至關重要。這樣可以防止體型明顯不同的拳手之間的不匹配,例如重量級拳手與雛量級拳手的對決。 + +用通俗的話來講 + +> 使型別中的某些方法接受特定於其子型別的引數。 + +維基百科介紹 + +> 奇異遞迴模板模式(curiously recurring template pattern,CRTP)是C++模板程式設計時的一種慣用法:其中類X派生自使用X本身作為模板引數的類别範本例項化。 + +**程式示例** + +讓我們來定義通用介面Fighter + +```java +public interface Fighter { + + void fight(T t); + +} +``` + +MmaFighter類用於例項化按重量級別區分的拳手 + +``` Java +public class MmaFighter> implements Fighter { + + private final String name; + private final String surname; + private final String nickName; + private final String speciality; + + public MmaFighter(String name, String surname, String nickName, String speciality) { + this.name = name; + this.surname = surname; + this.nickName = nickName; + this.speciality = speciality; + } + + @Override + public void fight(T opponent) { + LOGGER.info("{} is going to fight against {}", this, opponent); + } + + @Override + public String toString() { + return name + " \"" + nickName + "\" " + surname; + } +``` + +以下是 MmaFighter 的一些子型別 + +```Java +class MmaBantamweightFighter extends MmaFighter { + + public MmaBantamweightFighter(String name, String surname, String nickName, String speciality) { + super(name, surname, nickName, speciality); + } + +} + +public class MmaHeavyweightFighter extends MmaFighter { + + public MmaHeavyweightFighter(String name, String surname, String nickName, String speciality) { + super(name, surname, nickName, speciality); + } + +} +``` + +允許拳手與相同重量級的對手交手,如果對手是不同重量級,則會出現錯誤 + +``` Java +MmaBantamweightFighter fighter1 = new MmaBantamweightFighter("Joe", "Johnson", "The Geek", "Muay Thai"); +MmaBantamweightFighter fighter2 = new MmaBantamweightFighter("Ed", "Edwards", "The Problem Solver", "Judo"); +fighter1.fight(fighter2); // This is fine + +MmaHeavyweightFighter fighter3 = new MmaHeavyweightFighter("Dave", "Davidson", "The Bug Smasher", "Kickboxing"); +MmaHeavyweightFighter fighter4 = new MmaHeavyweightFighter("Jack", "Jackson", "The Pragmatic", "Brazilian Jiu-Jitsu"); +fighter3.fight(fighter4); // This is fine too + +fighter1.fight(fighter3); // This will raise a compilation error +``` + +## 類圖 + +![alt text](etc/crtp.png "CRTP class diagram") + +## 適用性 + +在以下情況下使用CRTP + +* 在物件層次結構中連結方法時存在型別衝突 +* 你想使用一個引數化的類方法,該方法可以接受類的子類作為引數,從而可以應用於繼承自類的物件 +* 你希望某些方法僅適用於相同型別的例項,例如實現相互比較。 + +## 教程 + +* [The NuaH Blog](https://nuah.livejournal.com/328187.html) +* Yogesh Umesh Vaity answer to [What does "Recursive type bound" in Generics mean?](https://stackoverflow.com/questions/7385949/what-does-recursive-type-bound-in-generics-mean) + +## 已知用途 + +* [java.lang.Enum](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Enum.html) + +## 鳴謝 + +* [How do I decrypt "Enum>"?](http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeParameters.html#FAQ106) +* Chapter 5 Generics, Item 30 in [Effective Java](https://www.amazon.com/gp/product/0134685997/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0134685997&linkCode=as2&tag=javadesignpat-20&linkId=4e349f4b3ff8c50123f8147c828e53eb) diff --git a/localization/zh-TW/crtp/etc/crtp.png b/localization/zh-TW/crtp/etc/crtp.png new file mode 100644 index 000000000000..a348c8af6175 Binary files /dev/null and b/localization/zh-TW/crtp/etc/crtp.png differ diff --git a/localization/zh-TW/dao/README.md b/localization/zh-TW/dao/README.md new file mode 100644 index 000000000000..93eef8b2e4cc --- /dev/null +++ b/localization/zh-TW/dao/README.md @@ -0,0 +1,161 @@ +--- +title: Data Access Object +shortTitle: Data Access Object +category: Architectural +language: zh +tag: + - Data access +--- + +## 目的 + +物件為某種型別的資料庫或其他永續性機制提供了抽象介面。 + +## 解釋 + +真實世界例子 + +> 有一組客戶資料需要持久化到資料庫中。 我們需要整個額外的增刪改查操作以便操作客戶資料。 + +通俗的說 + +> DAO是我們透過基本永續性機制提供的介面。 + +維基百科說 + +> 在計算機軟體中,資料訪問物件(DAO)是一種模式,可為某種型別的資料庫或其他永續性機制提供抽象介面。 + +**程式示例** + +透過我們的客戶示例,下面是基本的`客戶`實體。 + +```java +public class Customer { + + private int id; + private String firstName; + private String lastName; + + public Customer(int id, String firstName, String lastName) { + this.id = id; + this.firstName = firstName; + this.lastName = lastName; + } + // getters and setters -> + ... +} +``` + +這是`CustomerDao`介面及其兩個不同的實現。 + +Here's the `CustomerDao` interface and two different implementations for it. `InMemoryCustomerDao` +將簡單的客戶資料對映儲存在記憶體中 而`DBCustomerDao`是真正的RDBMS實現。 + +```java +public interface CustomerDao { + + Stream getAll() throws Exception; + + Optional getById(int id) throws Exception; + + boolean add(Customer customer) throws Exception; + + boolean update(Customer customer) throws Exception; + + boolean delete(Customer customer) throws Exception; +} + +public class InMemoryCustomerDao implements CustomerDao { + + private final Map idToCustomer = new HashMap<>(); + + // implement the interface using the map + ... +} + +public class DbCustomerDao implements CustomerDao { + + private static final Logger LOGGER = LoggerFactory.getLogger(DbCustomerDao.class); + + private final DataSource dataSource; + + public DbCustomerDao(DataSource dataSource) { + this.dataSource = dataSource; + } + + // implement the interface using the data source + ... +``` + +最後,這是我們使用DAO管理客戶資料的方式。 + +```java + final var dataSource = createDataSource(); + createSchema(dataSource); + final var customerDao = new DbCustomerDao(dataSource); + + addCustomers(customerDao); + log.info(ALL_CUSTOMERS); + try (var customerStream = customerDao.getAll()) { + customerStream.forEach((customer) -> log.info(customer.toString())); + } + log.info("customerDao.getCustomerById(2): " + customerDao.getById(2)); + final var customer = new Customer(4, "Dan", "Danson"); + customerDao.add(customer); + log.info(ALL_CUSTOMERS + customerDao.getAll()); + customer.setFirstName("Daniel"); + customer.setLastName("Danielson"); + customerDao.update(customer); + log.info(ALL_CUSTOMERS); + try (var customerStream = customerDao.getAll()) { + customerStream.forEach((cust) -> log.info(cust.toString())); + } + customerDao.delete(customer); + log.info(ALL_CUSTOMERS + customerDao.getAll()); + + deleteSchema(dataSource); +``` + +程式輸出: + +```java +customerDao.getAllCustomers(): +Customer{id=1, firstName='Adam', lastName='Adamson'} +Customer{id=2, firstName='Bob', lastName='Bobson'} +Customer{id=3, firstName='Carl', lastName='Carlson'} +customerDao.getCustomerById(2): Optional[Customer{id=2, firstName='Bob', lastName='Bobson'}] +customerDao.getAllCustomers(): java.util.stream.ReferencePipeline$Head@7cef4e59 +customerDao.getAllCustomers(): +Customer{id=1, firstName='Adam', lastName='Adamson'} +Customer{id=2, firstName='Bob', lastName='Bobson'} +Customer{id=3, firstName='Carl', lastName='Carlson'} +Customer{id=4, firstName='Daniel', lastName='Danielson'} +customerDao.getAllCustomers(): java.util.stream.ReferencePipeline$Head@2db0f6b2 +customerDao.getAllCustomers(): +Customer{id=1, firstName='Adam', lastName='Adamson'} +Customer{id=2, firstName='Bob', lastName='Bobson'} +Customer{id=3, firstName='Carl', lastName='Carlson'} +customerDao.getCustomerById(2): Optional[Customer{id=2, firstName='Bob', lastName='Bobson'}] +customerDao.getAllCustomers(): java.util.stream.ReferencePipeline$Head@12c8a2c0 +customerDao.getAllCustomers(): +Customer{id=1, firstName='Adam', lastName='Adamson'} +Customer{id=2, firstName='Bob', lastName='Bobson'} +Customer{id=3, firstName='Carl', lastName='Carlson'} +Customer{id=4, firstName='Daniel', lastName='Danielson'} +customerDao.getAllCustomers(): java.util.stream.ReferencePipeline$Head@6ec8211c +``` + +## 類圖 + +![alt text](./etc/dao.png "Data Access Object") + +## 適用性 + +在以下情況下,請使用資料訪問物件:: + +* 當你要鞏固如何訪問資料層時。 +* 當你要避免編寫多個資料檢索/持久層時。 + +## 鳴謝 + +* [J2EE Design Patterns](https://www.amazon.com/gp/product/0596004273/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596004273&linkCode=as2&tag=javadesignpat-20&linkId=48d37c67fb3d845b802fa9b619ad8f31) diff --git a/localization/zh-TW/dao/etc/dao.png b/localization/zh-TW/dao/etc/dao.png new file mode 100644 index 000000000000..452e72ba10ac Binary files /dev/null and b/localization/zh-TW/dao/etc/dao.png differ diff --git a/localization/zh-TW/data-bus/README.md b/localization/zh-TW/data-bus/README.md new file mode 100644 index 000000000000..8eac739d7a4b --- /dev/null +++ b/localization/zh-TW/data-bus/README.md @@ -0,0 +1,29 @@ +--- +title: Data Bus +shortTitle: Data Bus +category: Architectural +language: zh +tag: + - Decoupling +--- + +## 含義 + +資料匯流排模式(譯者:實際上,就是 Event-Bus 訊息匯流排模式)允許在一個應用程式的元件之間收發訊息/事件,而不需要這些元件相互感知,它們只需要知道所傳送/接收的訊息/事件的型別即可。 + +## 類圖 +![data bus pattern uml diagram](./etc/data-bus.urm.png "Data Bus pattern") + +## 適用場景 +可以在以下場景使用資料匯流排模式: + +* 你希望由你的元件自己決定要接收哪些資訊/事件 +* 你希望實現多對多的通訊 +* 你希望你的元件不需要感知彼此 + +## 相關模式 +資料匯流排類似於以下設計模式: + +* 中介者模式(Mediator pattern),由資料匯流排成員自己決定是否要接受任何給定的訊息。 +* 觀察者模式(Observer pattern),但進一步支援了多對多的通訊。 +* 釋出/訂閱模式(Publish/Subscribe pattern),但是資料匯流排將釋出者和訂閱者解耦。 \ No newline at end of file diff --git a/localization/zh-TW/data-bus/etc/data-bus.urm.png b/localization/zh-TW/data-bus/etc/data-bus.urm.png new file mode 100644 index 000000000000..8bd2148174e4 Binary files /dev/null and b/localization/zh-TW/data-bus/etc/data-bus.urm.png differ diff --git a/localization/zh-TW/data-mapper/README.md b/localization/zh-TW/data-mapper/README.md new file mode 100644 index 000000000000..dab58542bb14 --- /dev/null +++ b/localization/zh-TW/data-mapper/README.md @@ -0,0 +1,24 @@ +--- +title: Data Mapper +shortTitle: Data Mapper +category: Architectural +language: zh +tag: + - Decoupling +--- + +## 含義 +一個用於在持久化物件和資料庫之間傳輸資料的對映器,同時保持它們之間和對映器本身的獨立性。 + +## 類圖 +![alt text](./etc/data-mapper.png "Data Mapper") + +## 適用場景 +資料對映器適用於以下場景: + +* 當你想把資料物件從資料庫訪問層解耦時時 +* 當你想編寫多個資料查詢/持久化實現時 + +## 引用 + +* [Data Mapper](http://richard.jp.leguen.ca/tutoring/soen343-f2010/tutorials/implementing-data-mapper/) \ No newline at end of file diff --git a/localization/zh-TW/data-mapper/etc/data-mapper.png b/localization/zh-TW/data-mapper/etc/data-mapper.png new file mode 100644 index 000000000000..bcda8054aac8 Binary files /dev/null and b/localization/zh-TW/data-mapper/etc/data-mapper.png differ diff --git a/localization/zh-TW/data-transfer-object/README.md b/localization/zh-TW/data-transfer-object/README.md new file mode 100644 index 000000000000..9c6d6f486837 --- /dev/null +++ b/localization/zh-TW/data-transfer-object/README.md @@ -0,0 +1,110 @@ +--- +title: Data Transfer Object +shortTitle: Data Transfer Object +category: Architectural +language: zh +tag: + - Performance +--- + +## 目的 + +次將具有多個屬性的資料從客戶端傳遞到伺服器,以避免多次呼叫遠端伺服器。 + +## 解釋 + +真實世界例子 + +> 我們需要從遠端資料庫中獲取有關客戶的資訊。 我們不使用一次查詢一個屬性,而是使用DTO一次傳送所有相關屬性。 + +通俗的說 + +> 使用DTO,可以透過單個後端查詢獲取相關資訊。 + +維基百科說 + +> 在程式設計領域,資料傳輸物件(DTO)是在程序之間承載資料的物件。 使用它的動機是,通常依靠遠端介面(例如Web服務)來完成程序之間的通訊,在這種情況下,每個呼叫都是昂貴的操作。 +> +> 因為每個(方法)呼叫的大部分成本與客戶端和伺服器之間的往返時間有關,所以減少呼叫數量的一種方法是使用一個物件(DTO)來聚合將要在多次呼叫間傳輸的資料,但僅由一個呼叫提供。 + +**程式示例** + +讓我們來介紹我們簡單的`CustomerDTO` 類 + +```java +public class CustomerDto { + private final String id; + private final String firstName; + private final String lastName; + + public CustomerDto(String id, String firstName, String lastName) { + this.id = id; + this.firstName = firstName; + this.lastName = lastName; + } + + public String getId() { + return id; + } + + public String getFirstName() { + return firstName; + } + + public String getLastName() { + return lastName; + } +} +``` + +`CustomerResource` 類充當客戶資訊的伺服器。 + +```java +public class CustomerResource { + private final List customers; + + public CustomerResource(List customers) { + this.customers = customers; + } + + public List getAllCustomers() { + return customers; + } + + public void save(CustomerDto customer) { + customers.add(customer); + } + + public void delete(String customerId) { + customers.removeIf(customer -> customer.getId().equals(customerId)); + } +} +``` + +現在拉取客戶資訊變得簡單自從我們有了DTOs。 + +```java + var allCustomers = customerResource.getAllCustomers(); + allCustomers.forEach(customer -> LOGGER.info(customer.getFirstName())); + // Kelly + // Alfonso +``` + +## 類圖 + +![alt text](./etc/data-transfer-object.urm.png "data-transfer-object") + +## 適用性 + +使用資料傳輸物件模式當 + +* 客戶端請求多種資訊。資訊都是相關的 +* 當你想提高獲取資源的效能 +* 你想降低遠端方法呼叫的次數 + +## 鳴謝 + +* [Design Pattern - Transfer Object Pattern](https://www.tutorialspoint.com/design_pattern/transfer_object_pattern.htm) +* [Data Transfer Object](https://msdn.microsoft.com/en-us/library/ff649585.aspx) +* [J2EE Design Patterns](https://www.amazon.com/gp/product/0596004273/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596004273&linkCode=as2&tag=javadesignpat-20&linkId=f27d2644fbe5026ea448791a8ad09c94) +* [Patterns of Enterprise Application Architecture](https://www.amazon.com/gp/product/0321127420/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321127420&linkCode=as2&tag=javadesignpat-20&linkId=014237a67c9d46f384b35e10151956bd) diff --git a/localization/zh-TW/data-transfer-object/etc/data-transfer-object.urm.png b/localization/zh-TW/data-transfer-object/etc/data-transfer-object.urm.png new file mode 100644 index 000000000000..46facff8dee4 Binary files /dev/null and b/localization/zh-TW/data-transfer-object/etc/data-transfer-object.urm.png differ diff --git a/localization/zh-TW/decorator/README.md b/localization/zh-TW/decorator/README.md new file mode 100644 index 000000000000..2cfb5f2f7a13 --- /dev/null +++ b/localization/zh-TW/decorator/README.md @@ -0,0 +1,134 @@ +--- +title: Decorator +shortTitle: Decorator +category: Structural +language: zh +tag: + - Gang Of Four + - Extensibility +--- + +## 或稱 +包裝器 + +## 目的 +動態的為物件附加額外的職責。裝飾器為子類提供了靈活的替代方案,以擴充套件功能。 + +## 解釋 + +真實世界例子 + +> 附近的山丘上住著一個憤怒的巨魔。通常它是徒手的,但有時它有武器。為了武裝巨魔不必建立新的巨魔,而是用合適的武器動態的裝飾它。 + +通俗的說 + +> 裝飾者模式讓你可以在執行時透過把物件包裝進一個裝飾類物件中來動態的改變一個物件的行為。 + +維基百科說 + +> 在物件導向的程式設計中,裝飾器模式是一種設計模式,它允許將行為靜態或動態地新增到單個物件中,而不會影響同一類中其他物件的行為。裝飾器模式通常對於遵守單一責任原則很有用,因為它允許將功能劃分到具有唯一關注領域的類之間。 + +**程式示例** + +以巨魔的為例。首先我有有一個簡單的巨魔,實現了巨魔介面。 + +```java +public interface Troll { + void attack(); + int getAttackPower(); + void fleeBattle(); +} + +@Slf4j +public class SimpleTroll implements Troll { + + @Override + public void attack() { + LOGGER.info("The troll tries to grab you!"); + } + + @Override + public int getAttackPower() { + return 10; + } + + @Override + public void fleeBattle() { + LOGGER.info("The troll shrieks in horror and runs away!"); + } +} +``` + +下面我們想為巨魔新增球棒。我們可以用裝飾者來動態的實現。 + +```java +@Slf4j +public class ClubbedTroll implements Troll { + + private final Troll decorated; + + public ClubbedTroll(Troll decorated) { + this.decorated = decorated; + } + + @Override + public void attack() { + decorated.attack(); + LOGGER.info("The troll swings at you with a club!"); + } + + @Override + public int getAttackPower() { + return decorated.getAttackPower() + 10; + } + + @Override + public void fleeBattle() { + decorated.fleeBattle(); + } +} +``` + +這裡是巨魔的實戰 + +```java +// simple troll +var troll = new SimpleTroll(); +troll.attack(); // The troll tries to grab you! +troll.fleeBattle(); // The troll shrieks in horror and runs away! + +// change the behavior of the simple troll by adding a decorator +var clubbedTroll = new ClubbedTroll(troll); +clubbedTroll.attack(); // The troll tries to grab you! The troll swings at you with a club! +clubbedTroll.fleeBattle(); // The troll shrieks in horror and runs away! +``` + +## 類圖 +![alt text](./etc/decorator.urm.png "Decorator pattern class diagram") + +## 適用性 +使用裝飾者 + +* 動態透明地向單個物件新增職責,即不影響其他物件 +* 對於可以撤銷的責任 +* 當透過子類化進行擴充套件是不切實際的。有時可能會有大量的獨立擴充套件,並且會產生大量的子類來支援每種組合。 否則類定義可能被隱藏或無法用於子類化。 + +## 教程 +* [Decorator Pattern Tutorial](https://www.journaldev.com/1540/decorator-design-pattern-in-java-example) + +## Java世界的例子 + * [java.io.InputStream](http://docs.oracle.com/javase/8/docs/api/java/io/InputStream.html), [java.io.OutputStream](http://docs.oracle.com/javase/8/docs/api/java/io/OutputStream.html), + [java.io.Reader](http://docs.oracle.com/javase/8/docs/api/java/io/Reader.html) and [java.io.Writer](http://docs.oracle.com/javase/8/docs/api/java/io/Writer.html) + * [java.util.Collections#synchronizedXXX()](http://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#synchronizedCollection-java.util.Collection-) + * [java.util.Collections#unmodifiableXXX()](http://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#unmodifiableCollection-java.util.Collection-) + * [java.util.Collections#checkedXXX()](http://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#checkedCollection-java.util.Collection-java.lang.Class-) + + +## 鳴謝 + +* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59) +* [Functional Programming in Java: Harnessing the Power of Java 8 Lambda Expressions](https://www.amazon.com/gp/product/1937785467/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1937785467&linkCode=as2&tag=javadesignpat-20&linkId=7e4e2fb7a141631491534255252fd08b) +* [J2EE Design Patterns](https://www.amazon.com/gp/product/0596004273/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596004273&linkCode=as2&tag=javadesignpat-20&linkId=48d37c67fb3d845b802fa9b619ad8f31) +* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b) +* [Refactoring to Patterns](https://www.amazon.com/gp/product/0321213351/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321213351&linkCode=as2&tag=javadesignpat-20&linkId=2a76fcb387234bc71b1c61150b3cc3a7) +* [J2EE Design Patterns](https://www.amazon.com/gp/product/0596004273/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596004273&linkCode=as2&tag=javadesignpat-20&linkId=f27d2644fbe5026ea448791a8ad09c94) diff --git a/localization/zh-TW/decorator/etc/decorator.urm.png b/localization/zh-TW/decorator/etc/decorator.urm.png new file mode 100644 index 000000000000..141c0563f0c6 Binary files /dev/null and b/localization/zh-TW/decorator/etc/decorator.urm.png differ diff --git a/localization/zh-TW/delegation/README.md b/localization/zh-TW/delegation/README.md new file mode 100644 index 000000000000..9f0670cf753b --- /dev/null +++ b/localization/zh-TW/delegation/README.md @@ -0,0 +1,28 @@ +--- +title: Delegation +shortTitle: Delegation +category: Structural +language: zh +tag: + - Decoupling +--- + +## 或稱 +代理模式 + +## 目的 +它是一種讓物件將某種行為向外部表達,但實際上將實現該行為的責任委託給關聯物件的技術。 + +## 類圖 +![alt text](./etc/delegation.png "Delegate") + +## 適用性 +使用委託模式以實現以下目的 + +* 降低類的耦合性 +* 元件的行為相同,但是意識到這種情況將來可能會改變。 + +## 鳴謝 + +* [Delegate Pattern: Wikipedia ](https://en.wikipedia.org/wiki/Delegation_pattern) +* [Proxy Pattern: Wikipedia ](https://en.wikipedia.org/wiki/Proxy_pattern) diff --git a/localization/zh-TW/delegation/etc/delegation.png b/localization/zh-TW/delegation/etc/delegation.png new file mode 100644 index 000000000000..375ef4d6b00f Binary files /dev/null and b/localization/zh-TW/delegation/etc/delegation.png differ diff --git a/localization/zh-TW/dependency-injection/README.md b/localization/zh-TW/dependency-injection/README.md new file mode 100644 index 000000000000..2f592129e311 --- /dev/null +++ b/localization/zh-TW/dependency-injection/README.md @@ -0,0 +1,100 @@ +--- +title: Dependency Injection +shortTitle: Dependency Injection +category: Creational +language: zh +tag: + - Decoupling +--- + +## 目的 + +依賴注入是一種軟體設計模式,其中一個或多個依賴項(或服務)被注入或透過引用傳遞到一個依賴物件(或客戶端)中,併成為客戶端狀態的一部分。該模式將客戶的依賴關係的建立與其自身的行為分開,這使程式設計可以鬆散耦合,並遵循控制反轉和單一職責原則。 + +## 解釋 + +真實世界例子 + +> 老巫師喜歡不時地裝滿菸斗抽菸。 但是,他不想只依賴一個菸草品牌,而是希望能夠互換使用它們。 + +通俗的說 + +> 依賴注入將客戶端依賴的建立與其自身行為分開。 + +維基百科說 + +> 在軟體工程中,依賴注入是一種物件接收其依賴的其他物件的技術。 這些其他物件稱為依賴項。 + +**程式示例** + +先介紹一下菸草介面和具體的品牌。 + +```java +public abstract class Tobacco { + + private static final Logger LOGGER = LoggerFactory.getLogger(Tobacco.class); + + public void smoke(Wizard wizard) { + LOGGER.info("{} smoking {}", wizard.getClass().getSimpleName(), + this.getClass().getSimpleName()); + } +} + +public class SecondBreakfastTobacco extends Tobacco { +} + +public class RivendellTobacco extends Tobacco { +} + +public class OldTobyTobacco extends Tobacco { +} +``` + +下面是老巫師的類的層次結構。 + +```java +public interface Wizard { + + void smoke(); +} + +public class AdvancedWizard implements Wizard { + + private final Tobacco tobacco; + + public AdvancedWizard(Tobacco tobacco) { + this.tobacco = tobacco; + } + + @Override + public void smoke() { + tobacco.smoke(this); + } +} +``` + +最後我們可以看到給老巫師任意品牌的菸草是多麼的簡單。 + +```java + var advancedWizard = new AdvancedWizard(new SecondBreakfastTobacco()); + advancedWizard.smoke(); +``` + +## 類圖 + +![alt text](./etc/dependency-injection.png "Dependency Injection") + +## 適用性 + +使用依賴注入當: + +- 當你需要從物件中移除掉具體的實現內容時 + +* 使用模擬物件或存根隔離地啟用類的單元測試 + +## 鳴謝 + +* [Dependency Injection Principles, Practices, and Patterns](https://www.amazon.com/gp/product/161729473X/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=161729473X&linkId=57079257a5c7d33755493802f3b884bd) +* [Clean Code: A Handbook of Agile Software Craftsmanship](https://www.amazon.com/gp/product/0132350882/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0132350882&linkCode=as2&tag=javadesignpat-20&linkId=2c390d89cc9e61c01b9e7005c7842871) +* [Java 9 Dependency Injection: Write loosely coupled code with Spring 5 and Guice](https://www.amazon.com/gp/product/1788296257/ref=as_li_tl?ie=UTF8&tag=javadesignpat-20&camp=1789&creative=9325&linkCode=as2&creativeASIN=1788296257&linkId=4e9137a3bf722a8b5b156cce1eec0fc1) +* [Google Guice Tutorial: Open source Java based dependency injection framework](https://www.amazon.com/gp/product/B083P7DZ8M/ref=as_li_tl?ie=UTF8&tag=javadesignpat-20&camp=1789&creative=9325&linkCode=as2&creativeASIN=B083P7DZ8M&linkId=04f0f902c877921e45215b624a124bfe) diff --git a/localization/zh-TW/dependency-injection/etc/dependency-injection.png b/localization/zh-TW/dependency-injection/etc/dependency-injection.png new file mode 100644 index 000000000000..2a92c9eb228b Binary files /dev/null and b/localization/zh-TW/dependency-injection/etc/dependency-injection.png differ diff --git a/localization/zh-TW/dirty-flag/README.md b/localization/zh-TW/dirty-flag/README.md new file mode 100644 index 000000000000..b9d6a965bcb5 --- /dev/null +++ b/localization/zh-TW/dirty-flag/README.md @@ -0,0 +1,28 @@ +--- +title: Dirty Flag +shortTitle: Dirty Flag +category: Behavioral +language: zh +tag: + - Game programming + - Performance +--- + +## 或稱 +* 是否髒 模式 + +## 目的 +避免昂貴資源的重新獲取。資源保留其身份,保留在某些快速訪問的儲存中,並被重新使用以避免再次獲取它們。 + +## 類圖 +![alt text](./etc/dirty-flag.png "Dirty Flag") + +## 適用性 +在以下情況下使用髒標誌模式 + +* 重複獲取,初始化,釋放相同資源所導致不必要的效能開銷 + +## 鳴謝 + +* [Design Patterns: Dirty Flag](https://www.takeupcode.com/podcast/89-design-patterns-dirty-flag/) +* [J2EE Design Patterns](https://www.amazon.com/gp/product/0596004273/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596004273&linkCode=as2&tag=javadesignpat-20&linkId=48d37c67fb3d845b802fa9b619ad8f31) diff --git a/localization/zh-TW/dirty-flag/etc/dirty-flag.png b/localization/zh-TW/dirty-flag/etc/dirty-flag.png new file mode 100644 index 000000000000..98d4f679d17c Binary files /dev/null and b/localization/zh-TW/dirty-flag/etc/dirty-flag.png differ diff --git a/localization/zh-TW/double-checked-locking/README.md b/localization/zh-TW/double-checked-locking/README.md new file mode 100644 index 000000000000..1a20ed6accbe --- /dev/null +++ b/localization/zh-TW/double-checked-locking/README.md @@ -0,0 +1,20 @@ +--- +title: Double Checked Locking +shortTitle: Double Checked Locking +category: Idiom +language: zh +tag: + - Performance +--- + +## 含義 +透過先測試鎖定標準("鎖提示")而不實際獲取鎖的方式來減少獲取鎖的開銷。只有當鎖定標準檢查表明需要鎖定時,才進行實際的鎖定邏輯。 + +## 類圖 +![alt text](./etc/double_checked_locking_1.png "Double Checked Locking") + +## 適用場景 +在以下場景適合使用雙重鎖檢查模式: + +* 在建立物件時有存在併發的訪問。如單例模式中,你想建立同一個類的單個例項,如果存在兩個或更多的執行緒對例項進行判空,僅僅檢查該該例項是否為空可能是不夠的。 +* 在一個方法上存在併發訪問,該方法的行為是根據一些約束條件而改變,而這些約束條件在該方法中也會發生變化。 \ No newline at end of file diff --git a/localization/zh-TW/double-checked-locking/etc/double_checked_locking_1.png b/localization/zh-TW/double-checked-locking/etc/double_checked_locking_1.png new file mode 100644 index 000000000000..cced091b9d67 Binary files /dev/null and b/localization/zh-TW/double-checked-locking/etc/double_checked_locking_1.png differ diff --git a/localization/zh-TW/facade/README.md b/localization/zh-TW/facade/README.md new file mode 100644 index 000000000000..3cfaf0cc9d02 --- /dev/null +++ b/localization/zh-TW/facade/README.md @@ -0,0 +1,191 @@ +--- +title: Facade +shortTitle: Facade +category: Structural +language: zh +tag: + - Gang Of Four + - Decoupling +--- + +## 目的 +為一個子系統中的一系列介面提供一個統一的介面。外觀定義了一個更高階別的介面以便子系統更容易使用。 + +## 解釋 + +真實世界的例子 + +> 一個金礦是怎麼工作的?“嗯,礦工下去然後挖金子!”你說。這是你所相信的因為你在使用一個金礦對外提供的一個簡單介面,在內部它要卻要做很多事情。這個簡單的介面對複雜的子系統來說就是一個外觀。 + +用通俗的話說 + +> 外觀模式為一個複雜的子系統提供一個簡單的介面。 + +維基百科說 + +> 外觀是為很大體量的程式碼(比如類庫)提供簡單介面的一種物件。 + +**程式示例** + +使用上面金礦的例子。這裡我們有矮人的礦工等級制度。 + +```java + +@Slf4j +public abstract class DwarvenMineWorker { + + public void goToSleep() { + LOGGER.info("{} goes to sleep.", name()); + } + + public void wakeUp() { + LOGGER.info("{} wakes up.", name()); + } + + public void goHome() { + LOGGER.info("{} goes home.", name()); + } + + public void goToMine() { + LOGGER.info("{} goes to the mine.", name()); + } + + private void action(Action action) { + switch (action) { + case GO_TO_SLEEP -> goToSleep(); + case WAKE_UP -> wakeUp(); + case GO_HOME -> goHome(); + case GO_TO_MINE -> goToMine(); + case WORK -> work(); + default -> LOGGER.info("Undefined action"); + } + } + + public void action(Action... actions) { + Arrays.stream(actions).forEach(this::action); + } + + public abstract void work(); + + public abstract String name(); + + enum Action { + GO_TO_SLEEP, WAKE_UP, GO_HOME, GO_TO_MINE, WORK + } +} + +@Slf4j +public class DwarvenTunnelDigger extends DwarvenMineWorker { + + @Override + public void work() { + LOGGER.info("{} creates another promising tunnel.", name()); + } + + @Override + public String name() { + return "Dwarven tunnel digger"; + } +} + +@Slf4j +public class DwarvenGoldDigger extends DwarvenMineWorker { + + @Override + public void work() { + LOGGER.info("{} digs for gold.", name()); + } + + @Override + public String name() { + return "Dwarf gold digger"; + } +} + +@Slf4j +public class DwarvenCartOperator extends DwarvenMineWorker { + + @Override + public void work() { + LOGGER.info("{} moves gold chunks out of the mine.", name()); + } + + @Override + public String name() { + return "Dwarf cart operator"; + } +} + +``` + +為了操縱所有這些礦工我們有了這個外觀 + +```java +public class DwarvenGoldmineFacade { + + private final List workers; + + public DwarvenGoldmineFacade() { + workers = List.of( + new DwarvenGoldDigger(), + new DwarvenCartOperator(), + new DwarvenTunnelDigger()); + } + + public void startNewDay() { + makeActions(workers, DwarvenMineWorker.Action.WAKE_UP, DwarvenMineWorker.Action.GO_TO_MINE); + } + + public void digOutGold() { + makeActions(workers, DwarvenMineWorker.Action.WORK); + } + + public void endDay() { + makeActions(workers, DwarvenMineWorker.Action.GO_HOME, DwarvenMineWorker.Action.GO_TO_SLEEP); + } + + private static void makeActions(Collection workers, + DwarvenMineWorker.Action... actions) { + workers.forEach(worker -> worker.action(actions)); + } +} +``` + +現在來使用外觀 + +```java +DwarvenGoldmineFacade facade = new DwarvenGoldmineFacade(); +facade.startNewDay(); +// Dwarf gold digger wakes up. +// Dwarf gold digger goes to the mine. +// Dwarf cart operator wakes up. +// Dwarf cart operator goes to the mine. +// Dwarven tunnel digger wakes up. +// Dwarven tunnel digger goes to the mine. +facade.digOutGold(); +// Dwarf gold digger digs for gold. +// Dwarf cart operator moves gold chunks out of the mine. +// Dwarven tunnel digger creates another promising tunnel. +facade.endDay(); +// Dwarf gold digger goes home. +// Dwarf gold digger goes to sleep. +// Dwarf cart operator goes home. +// Dwarf cart operator goes to sleep. +// Dwarven tunnel digger goes home. +// Dwarven tunnel digger goes to sleep. +``` + +## 類圖 +![alt text](./etc/facade.urm.png "Facade pattern class diagram") + +## 適用性 +使用外觀模式當 + +* 你想為一個複雜的子系統提供一個簡單的介面。隨著子系統的發展,它們通常會變得更加複雜。多數模式在應用時會導致更多和更少的類。這使子系統更可重用,更易於自定義,但是對於不需要自定義它的客戶來說,使用它也變得更加困難。 外觀可以提供子系統的簡單預設檢視,足以滿足大多數客戶端的需求。只有需要更多可定製性的客戶才需要檢視外觀外的東西(原子系統提供的介面)。 +* 客戶端與抽象的實現類之間存在許多依賴關係。 引入外觀以使子系統與客戶端和其他子系統分離,從而提高子系統的獨立性和可移植性。 +* 你想對子系統進行分層。 使用外觀來定義每個子系統級別的入口點。 如果子系統是相關的,則可以透過使子系統僅透過其外觀相互通訊來簡化它們之間的依賴性。 + +## 鳴謝 + +* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59) +* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b) diff --git a/localization/zh-TW/facade/etc/facade.urm.png b/localization/zh-TW/facade/etc/facade.urm.png new file mode 100644 index 000000000000..8e3ec7aca45e Binary files /dev/null and b/localization/zh-TW/facade/etc/facade.urm.png differ diff --git a/localization/zh-TW/factory-kit/README.md b/localization/zh-TW/factory-kit/README.md new file mode 100644 index 000000000000..ee0abc9b98f6 --- /dev/null +++ b/localization/zh-TW/factory-kit/README.md @@ -0,0 +1,26 @@ +--- +title: Factory Kit +shortTitle: Factory Kit +category: Creational +language: zh +tag: + - Extensibility +--- + +## 含義 +使用分離的構建器和工廠介面來定義一個不可變內容的工廠。 + +## 類圖 +![alt text](./etc/factory-kit.png "Factory Kit") + +## 適用場景 +工廠套件模式適用於與以下場景: + +* 一個類無法預知它需要建立的物件的類別 +- 你只是想要一個新的自定義構建器(builder)的例項,而非全域性的構建器 +- 你明確地想要定義物件的型別,而且工廠可以建立這些物件 +- 你想要分離構建器(builder)和建立器(creator)介面 + +## 引用 + +* [Design Pattern Reloaded by Remi Forax: ](https://www.youtube.com/watch?v=-k2X7guaArU) \ No newline at end of file diff --git a/localization/zh-TW/factory-kit/etc/factory-kit.png b/localization/zh-TW/factory-kit/etc/factory-kit.png new file mode 100644 index 000000000000..7093193cb8d0 Binary files /dev/null and b/localization/zh-TW/factory-kit/etc/factory-kit.png differ diff --git a/localization/zh-TW/factory-method/README.md b/localization/zh-TW/factory-method/README.md new file mode 100644 index 000000000000..655da7fe2096 --- /dev/null +++ b/localization/zh-TW/factory-method/README.md @@ -0,0 +1,87 @@ +--- +title: Factory Method +shortTitle: Factory Method +category: Creational +language: zh +tag: + - Extensibility + - Gang Of Four +--- + +## Also known as +# 或稱 + +虛擬構造器 + +## 目的 +為建立一個物件定義一個介面,但是讓子類決定例項化哪個類。工廠方法允許類將例項化延遲到子類。 + +## 解釋 +真實世界例子 + +> 鐵匠生產武器。精靈需要精靈武器,而獸人需要獸人武器。根據客戶來召喚正確型別的鐵匠。 + +通俗的說 + +> 它為類提供了一種把例項化的邏輯委託給子類的方式。 + +維基百科上說 + +> 在基於類的程式設計中,工廠方法模式是一種建立型設計模式用來解決建立物件的問題,而不需要指定將要建立物件的確切類。這是透過呼叫工廠方法建立物件來完成的,而不是透過呼叫構造器。該工廠方法在介面中指定並由子類實現,或者在基類實現並可以選擇由子類重寫。 + + **程式示例** + +以上面的鐵匠為例,首先我們有鐵匠的介面和一些它的實現。 + +```java +public interface Blacksmith { + Weapon manufactureWeapon(WeaponType weaponType); +} + +public class ElfBlacksmith implements Blacksmith { + public Weapon manufactureWeapon(WeaponType weaponType) { + return ELFARSENAL.get(weaponType); + } +} + +public class OrcBlacksmith implements Blacksmith { + public Weapon manufactureWeapon(WeaponType weaponType) { + return ORCARSENAL.get(weaponType); + } +} +``` + +現在隨著客戶的到來,會召喚出正確型別的鐵匠並製造出要求的武器。 + +```java +var blacksmith = new ElfBlacksmith(); +blacksmith.manufactureWeapon(WeaponType.SPEAR); +blacksmith.manufactureWeapon(WeaponType.AXE); +// Elvish weapons are created +``` + +## 類圖 +![alt text](./etc/factory-method.urm.png "Factory Method pattern class diagram") + +## 適用性 +使用工廠方法模式當 + +* 一個類無法預料它所要必須建立的物件的類 +* 一個類想要它的子類來指定它要建立的物件 +* 類將責任委派給幾個幫助子類中的一個,而你想定位瞭解是具體之中的哪一個 + +## Java中的例子 + +* [java.util.Calendar](http://docs.oracle.com/javase/8/docs/api/java/util/Calendar.html#getInstance--) +* [java.util.ResourceBundle](http://docs.oracle.com/javase/8/docs/api/java/util/ResourceBundle.html#getBundle-java.lang.String-) +* [java.text.NumberFormat](http://docs.oracle.com/javase/8/docs/api/java/text/NumberFormat.html#getInstance--) +* [java.nio.charset.Charset](http://docs.oracle.com/javase/8/docs/api/java/nio/charset/Charset.html#forName-java.lang.String-) +* [java.net.URLStreamHandlerFactory](http://docs.oracle.com/javase/8/docs/api/java/net/URLStreamHandlerFactory.html#createURLStreamHandler-java.lang.String-) +* [java.util.EnumSet](https://docs.oracle.com/javase/8/docs/api/java/util/EnumSet.html#of-E-) +* [javax.xml.bind.JAXBContext](https://docs.oracle.com/javase/8/docs/api/javax/xml/bind/JAXBContext.html#createMarshaller--) + +## 鳴謝 + +* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59) +* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b) +* [Refactoring to Patterns](https://www.amazon.com/gp/product/0321213351/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321213351&linkCode=as2&tag=javadesignpat-20&linkId=2a76fcb387234bc71b1c61150b3cc3a7) diff --git a/localization/zh-TW/factory-method/etc/factory-method.urm.png b/localization/zh-TW/factory-method/etc/factory-method.urm.png new file mode 100644 index 000000000000..7c97aff91ee6 Binary files /dev/null and b/localization/zh-TW/factory-method/etc/factory-method.urm.png differ diff --git a/localization/zh-TW/factory/README.md b/localization/zh-TW/factory/README.md new file mode 100644 index 000000000000..ea1d9c875f94 --- /dev/null +++ b/localization/zh-TW/factory/README.md @@ -0,0 +1,140 @@ +--- +title: Factory +shortTitle: Factory +category: Creational +language: zh +tag: + - Gang of Four +--- + +## 也被稱為 + +* 簡單工廠 +* 靜態工廠方法 + +## 含義 + +在工廠類中提供一個封裝的靜態工廠方法,用於隱藏物件初始化細節,使客戶端程式碼可以專注於使用,而不用關心類的初始化過程。 + +## 解釋 + +現例項子 + +> +> 假設我們有一個需要連線到 SQL Server 的 Web 應用,但現在我們需要切換到連線 Oracle。為了不修改現有程式碼的情況下做到這一點,我們需要實現簡單工廠模式。在這種模式下,可以透過呼叫一個靜態方法來建立與給定資料庫的連線。 + +維基百科 + +> 工廠類是一個用於建立其他物件的物件 -- 從形式上看,工廠方法是一個用於返回不同原型或型別的函式或方法。 + +**程式設計示例** + +我們有一個 `Car` 介面,以及實現類 `Ford`, `Ferrari`。 + +```java +public interface Car { + String getDescription(); +} + +public class Ford implements Car { + + static final String DESCRIPTION = "This is Ford."; + + @Override + public String getDescription() { + return DESCRIPTION; + } +} + +public class Ferrari implements Car { + + static final String DESCRIPTION = "This is Ferrari."; + + @Override + public String getDescription() { + return DESCRIPTION; + } +} +``` + +Enumeration above represents types of cars that we support (`Ford` and `Ferrari`). + +以下的列舉用於表示支援的 `Car` 型別(`Ford` 和 `Ferrari`) + +```java +public enum CarType { + + FORD(Ford::new), + FERRARI(Ferrari::new); + + private final Supplier constructor; + + CarType(Supplier constructor) { + this.constructor = constructor; + } + + public Supplier getConstructor() { + return this.constructor; + } +} +``` +接著我們實現了一個靜態方法 `getCar` 用於封裝工廠類 `CarsFactory` 建立 `Car` 具體物件例項的細節。 + +```java +public class CarsFactory { + + public static Car getCar(CarType type) { + return type.getConstructor().get(); + } +} +``` + +現在我們可以在客戶端程式碼中透過工廠類建立不同型別的 `Car` 物件例項。 + +```java +var car1 = CarsFactory.getCar(CarType.FORD); +var car2 = CarsFactory.getCar(CarType.FERRARI); +LOGGER.info(car1.getDescription()); +LOGGER.info(car2.getDescription()); +``` + +程式輸出: + +```java +This is Ford. +This is Ferrari. +``` + +## 類圖 + +![alt text](./etc/factory.urm.png "Factory pattern class diagram") + +## 適用場景 + +在你只關心物件的建立,但不關心如何建立、管理它的時候,請使用簡單工廠模式。 + +**優點** + +* 可以把物件建立程式碼集中在一個地方,避免在程式碼庫存散佈 "new" 關鍵字。 +* 可以讓程式碼更加低耦合。它的一些主要優點包括更好的可測試性、更好的可讀性、元件可替換性、可拓展性、更好的隔離性。 + +**缺點** + +* 會使程式碼變得比原來的更加複雜一些。 + +## 現實案例 + +* [java.util.Calendar#getInstance()](https://docs.oracle.com/javase/8/docs/api/java/util/Calendar.html#getInstance--) +* [java.util.ResourceBundle#getBundle()](https://docs.oracle.com/javase/8/docs/api/java/util/ResourceBundle.html#getBundle-java.lang.String-) +* [java.text.NumberFormat#getInstance()](https://docs.oracle.com/javase/8/docs/api/java/text/NumberFormat.html#getInstance--) +* [java.nio.charset.Charset#forName()](https://docs.oracle.com/javase/8/docs/api/java/nio/charset/Charset.html#forName-java.lang.String-) +* [java.net.URLStreamHandlerFactory#createURLStreamHandler(String)](https://docs.oracle.com/javase/8/docs/api/java/net/URLStreamHandlerFactory.html) (Returns different singleton objects, depending on a protocol) +* [java.util.EnumSet#of()](https://docs.oracle.com/javase/8/docs/api/java/util/EnumSet.html#of(E)) +* [javax.xml.bind.JAXBContext#createMarshaller()](https://docs.oracle.com/javase/8/docs/api/javax/xml/bind/JAXBContext.html#createMarshaller--) and other similar methods. + +## 相關模式 + +* [Factory Method](https://java-design-patterns.com/patterns/factory-method/) +* [Factory Kit](https://java-design-patterns.com/patterns/factory-kit/) +* [Abstract Factory](https://java-design-patterns.com/patterns/abstract-factory/) + diff --git a/localization/zh-TW/factory/etc/factory.urm.png b/localization/zh-TW/factory/etc/factory.urm.png new file mode 100644 index 000000000000..4b3420792e06 Binary files /dev/null and b/localization/zh-TW/factory/etc/factory.urm.png differ diff --git a/localization/zh-TW/interpreter/README.md b/localization/zh-TW/interpreter/README.md new file mode 100644 index 000000000000..fa003dbdd5d8 --- /dev/null +++ b/localization/zh-TW/interpreter/README.md @@ -0,0 +1,34 @@ +--- +title: Interpreter +shortTitle: Interpreter +category: Behavioral +language: zh +tag: + - Gang of Four +--- + +## 目的 +給定一種語言,請定義其語法的表示形式,以及使用該表示形式來解釋該語言中的句子的直譯器。 + +## 類圖 +![alt text](./etc/interpreter_1.png "Interpreter") + +## 適用性 +有一種要解釋的語言時,請使用直譯器模式,並且可以將語言中的語句表示為抽象語法樹。直譯器模式在以下情況下效果最佳 + +* 語法很簡單。 對於複雜的語法,語法的類層次結構變得龐大且難以管理。 在這種情況下,解析器生成器之類的工具是更好的選擇。 他們可以在不構建抽象語法樹的情況下解釋表示式,這可以節省空間並可能節省時間 +* 效率不是關鍵問題。 通常,最有效的直譯器不是透過直接解釋解析樹來實現的,而是先將其轉換為另一種形式。 例如,正規表示式通常會轉換為狀態機。 但是即使這樣,翻譯器也可以透過直譯器模式實現,因此該模式仍然適用。 + +## 真實世界例子 + +* [java.util.Pattern](http://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html) +* [java.text.Normalizer](http://docs.oracle.com/javase/8/docs/api/java/text/Normalizer.html) +* All subclasses of [java.text.Format](http://docs.oracle.com/javase/8/docs/api/java/text/Format.html) +* [javax.el.ELResolver](http://docs.oracle.com/javaee/7/api/javax/el/ELResolver.html) + + +## 鳴謝 + +* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59) +* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b) +* [Refactoring to Patterns](https://www.amazon.com/gp/product/0321213351/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321213351&linkCode=as2&tag=javadesignpat-20&linkId=2a76fcb387234bc71b1c61150b3cc3a7) diff --git a/localization/zh-TW/interpreter/etc/interpreter_1.png b/localization/zh-TW/interpreter/etc/interpreter_1.png new file mode 100644 index 000000000000..f10342a1df90 Binary files /dev/null and b/localization/zh-TW/interpreter/etc/interpreter_1.png differ diff --git a/localization/zh-TW/iterator/README.md b/localization/zh-TW/iterator/README.md new file mode 100644 index 000000000000..0796cbaf9af7 --- /dev/null +++ b/localization/zh-TW/iterator/README.md @@ -0,0 +1,133 @@ +--- +title: Iterator +shortTitle: Iterator +category: Behavioral +language: zh +tag: + - Gang of Four +--- + +## 又被稱為 +遊標 + +## 目的 +提供一種在不暴露其基礎表示的情況下順序訪問聚合物件的元素的方法。 + +## 解釋 + +真實世界例子 + +> 百寶箱包含一組魔法物品。有多種物品,例如戒指,藥水和武器。可以使用藏寶箱提供的迭代器按型別瀏覽商品。 + +通俗地說 + +> 容器可以提供與表示形式無關的迭代器介面,以提供對元素的訪問。 + +維基百科說 + +> 在物件導向的程式設計中,迭代器模式是一種設計模式,其中迭代器用於遍歷容器並訪問容器的元素。 + +**程式示例** + +在我們的示例中包含物品的藏寶箱是主要類。 + +```java +public class TreasureChest { + + private final List items; + + public TreasureChest() { + items = List.of( + new Item(ItemType.POTION, "Potion of courage"), + new Item(ItemType.RING, "Ring of shadows"), + new Item(ItemType.POTION, "Potion of wisdom"), + new Item(ItemType.POTION, "Potion of blood"), + new Item(ItemType.WEAPON, "Sword of silver +1"), + new Item(ItemType.POTION, "Potion of rust"), + new Item(ItemType.POTION, "Potion of healing"), + new Item(ItemType.RING, "Ring of armor"), + new Item(ItemType.WEAPON, "Steel halberd"), + new Item(ItemType.WEAPON, "Dagger of poison")); + } + + public Iterator iterator(ItemType itemType) { + return new TreasureChestItemIterator(this, itemType); + } + + public List getItems() { + return new ArrayList<>(items); + } +} + +public class Item { + + private ItemType type; + private final String name; + + public Item(ItemType type, String name) { + this.setType(type); + this.name = name; + } + + @Override + public String toString() { + return name; + } + + public ItemType getType() { + return type; + } + + public final void setType(ItemType type) { + this.type = type; + } +} + +public enum ItemType { + + ANY, WEAPON, RING, POTION + +} +``` + +迭代器介面極度簡單。 + +```java +public interface Iterator { + + boolean hasNext(); + + T next(); +} +``` + +在以下示例中,我們遍歷在寶箱中找到的戒指型別物品。 + +```java +var itemIterator = TREASURE_CHEST.iterator(ItemType.RING); +while (itemIterator.hasNext()) { + LOGGER.info(itemIterator.next().toString()); +} +// Ring of shadows +// Ring of armor +``` + +## 類圖 +![alt text](./etc/iterator_1.png "Iterator") + +## 適用性 +以下情況使用迭代器模式 + +* 在不暴露其內部表示的情況下訪問聚合物件的內容 +* 為了支援聚合物件的多種遍歷方式 +* 提供一個遍歷不同聚合結構的統一介面 + +## Java世界例子 + +* [java.util.Iterator](http://docs.oracle.com/javase/8/docs/api/java/util/Iterator.html) +* [java.util.Enumeration](http://docs.oracle.com/javase/8/docs/api/java/util/Enumeration.html) + +## 鳴謝 + +* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59) +* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b) diff --git a/localization/zh-TW/iterator/etc/iterator_1.png b/localization/zh-TW/iterator/etc/iterator_1.png new file mode 100644 index 000000000000..d8313bc5881f Binary files /dev/null and b/localization/zh-TW/iterator/etc/iterator_1.png differ diff --git a/localization/zh-TW/monitor/README.md b/localization/zh-TW/monitor/README.md new file mode 100644 index 000000000000..d4ef5e5f19e8 --- /dev/null +++ b/localization/zh-TW/monitor/README.md @@ -0,0 +1,96 @@ +--- +title: Monitor +shortTitle: Monitor +category: Concurrency +language: zh +tag: + - Performance +--- + +## 或稱 + +監控物件模式 + +## 目的 + +主要目的是為多個執行緒或程序提供一種結構化和受控的方式來安全地訪問和操作共享資源,例如變數、資料結構或程式碼的關鍵部分,而不會導致衝突或競爭條件。 + +## 解釋 + +通俗的說 + +> 監視器模式用於強制對資料進行單執行緒訪問。 一次只允許一個執行緒在監視器物件內執行程式碼。 + +維基百科說 + +> 在併發程式設計(也稱為並行程式設計)中,監視器是一種同步構造,它允許執行緒具有互斥性和等待(阻止)特定條件變為假的能力。 監視器還具有向其他執行緒發出訊號通知其條件已滿足的機制。 + +**程式示例** + +考慮有一家銀行透過轉賬方式將錢從一個帳戶轉移到另一個帳戶。 它是`同步`意味著只有一個執行緒可以訪問此方法,因為如果許多執行緒訪問它並在同一時間將資金從一個帳戶轉移到另一個帳戶,則餘額會發生變化! + +``` +class Bank { + + private int[] accounts; + Logger logger; + + public Bank(int accountNum, int baseAmount, Logger logger) { + this.logger = logger; + accounts = new int[accountNum]; + Arrays.fill(accounts, baseAmount); + } + + public synchronized void transfer(int accountA, int accountB, int amount) { + if (accounts[accountA] >= amount) { + accounts[accountB] += amount; + accounts[accountA] -= amount; + logger.info("Transferred from account :" + accountA + " to account :" + accountB + " , amount :" + amount + " . balance :" + getBalance()); + } + } +``` + +getBalance 始終返回總金額,並且每次轉賬後總金額應相同 + +``` + private synchronized int getBalance() { + int balance = 0; + for (int account : accounts) { + balance += account; + } + return balance; + } + } +``` + +## 類圖 +![alt text](./etc/monitor.urm.png "Monitor class diagram") + +## 適用性 + +監視器設計模式應該用於具有需要由多個執行緒或程序同時訪問和操作的共享資源的情況。 此模式在需要同步以防止競爭條件、資料損壞和不一致狀態的情況下特別有用。 以下是你應該考慮使用監視器模式的一些情況: + +1. **共享資料**:當你的應用程式涉及需要由多個執行緒訪問和更新的共享資料結構、變數或資源時。 監視器確保一次只有一個執行緒可以訪問共享資源,從而防止衝突並確保資料一致性。 + +2. **關鍵部分**:當你有程式碼的關鍵部分一次只需要由一個執行緒執行時。 關鍵部分是操作共享資源的程式碼部分,併發訪問可能會導致問題。 監視器有助於確保在任何給定時間只有一個執行緒可以執行關鍵部分。 + +3. **執行緒安全**:當你需要確保執行緒安全而不是僅僅依賴鎖和訊號量等低階同步機制時。 監視器提供了封裝同步和資源管理的更高階別的抽象。 + +4. **等待和發訊號**:當你遇到執行緒需要等待滿足某些條件才能繼續操作時。 監視器通常包含執行緒等待特定條件以及其他執行緒在滿足條件時通知它們的機制。 + +5. **死鎖預防**:當你希望透過提供結構化方式來獲取和釋放共享資源上的鎖來防止死鎖時。 監視器透過確保資源訪問得到良好管理來幫助避免常見的死鎖情況。 + +6. **併發資料結構**:當你實現併發資料結構(例如佇列、堆疊或雜湊表)時,多個執行緒需要操作該結構,同時保持其完整性。 + +7. **資源共享**:當多個執行緒需要共享有限的資源時,例如連線資料庫或訪問網路套接字。 監視器可以幫助以受控方式管理這些資源的分配和釋放。 + +8. **改進可維護性**:當你想要將同步邏輯和共享資源管理封裝在單個物件中時,改進程式碼組織並使併發相關程式碼更容易推理。 + +但是,需要注意的是,監視器模式可能並不最適合所有併發場景。 在某些情況下,其他同步機制(例如鎖、訊號量或併發資料結構)可能更合適。 此外,現代程式語言和框架通常提供更高階別的併發結構,抽象出低階別同步的複雜性。 + +在應用監視器模式之前,建議徹底分析應用程式的併發需求,並選擇最適合你需求的同步方法,同時考慮效能、複雜性和可用語言功能等因素。 + +## 相關模式 + +* Active object +* Double-checked locking diff --git a/localization/zh-TW/monitor/etc/monitor.urm.png b/localization/zh-TW/monitor/etc/monitor.urm.png new file mode 100644 index 000000000000..c00e70e3fd12 Binary files /dev/null and b/localization/zh-TW/monitor/etc/monitor.urm.png differ diff --git a/localization/zh-TW/observer/README.md b/localization/zh-TW/observer/README.md new file mode 100644 index 000000000000..5f2b84365a3f --- /dev/null +++ b/localization/zh-TW/observer/README.md @@ -0,0 +1,155 @@ +--- +title: Observer +shortTitle: Observer +category: Behavioral +language: zh +tag: + - Gang Of Four + - Reactive +--- + +## Also known as +## 又被稱為 + +家屬,釋出訂閱模式 + +## 目的 + +定義一種一對多的物件依賴關係這樣當一個物件改變狀態時,所有依賴它的物件都將自動通知或更新。 + +## 解釋 + +真實世界例子 + +> 在遙遠的土地上生活著霍位元人和獸人的種族。他們都是戶外生活的人所以他們密切關注天氣的變化。可以說他們不斷地關注著天氣。 + +通俗的說 + +> 註冊成為一個觀察者以接收物件狀態的改變。 + +維基百科說 + +> 觀察者模式是這樣的一種軟體設計模式:它有一個被稱為主題的物件,維護著一個所有依賴於它的依賴者清單,也就是觀察者清單,當主題的狀態發生改變時,主題通常會呼叫觀察者的方法來自動通知觀察者們。 + +**程式設計示例** + +讓我們先來介紹天氣觀察者的介面以及我們的種族,獸人和霍位元人。 + +```java +public interface WeatherObserver { + + void update(WeatherType currentWeather); +} + +@Slf4j +public class Orcs implements WeatherObserver { + + @Override + public void update(WeatherType currentWeather) { + LOGGER.info("The orcs are facing " + currentWeather.getDescription() + " weather now"); + } +} + +@Slf4j +public class Hobbits implements WeatherObserver { + + @Override + public void update(WeatherType currentWeather) { + switch (currentWeather) { + LOGGER.info("The hobbits are facing " + currentWeather.getDescription() + " weather now"); + } +} +``` + +然後這裡是不斷變化的天氣。 + +```java +@Slf4j +public class Weather { + + private WeatherType currentWeather; + private final List observers; + + public Weather() { + observers = new ArrayList<>(); + currentWeather = WeatherType.SUNNY; + } + + public void addObserver(WeatherObserver obs) { + observers.add(obs); + } + + public void removeObserver(WeatherObserver obs) { + observers.remove(obs); + } + + /** + * Makes time pass for weather. + */ + public void timePasses() { + var enumValues = WeatherType.values(); + currentWeather = enumValues[(currentWeather.ordinal() + 1) % enumValues.length]; + LOGGER.info("The weather changed to {}.", currentWeather); + notifyObservers(); + } + + private void notifyObservers() { + for (var obs : observers) { + obs.update(currentWeather); + } + } +} +``` + +這是完整的示例。 + +```java + var weather = new Weather(); + weather.addObserver(new Orcs()); + weather.addObserver(new Hobbits()); + + weather.timePasses(); + // The weather changed to rainy. + // The orcs are facing rainy weather now + // The hobbits are facing rainy weather now + weather.timePasses(); + // The weather changed to windy. + // The orcs are facing windy weather now + // The hobbits are facing windy weather now + weather.timePasses(); + // The weather changed to cold. + // The orcs are facing cold weather now + // The hobbits are facing cold weather now + weather.timePasses(); + // The weather changed to sunny. + // The orcs are facing sunny weather now + // The hobbits are facing sunny weather now +``` + +## Class diagram +![alt text](./etc/observer.png "Observer") + +## 應用 +在下面任何一種情況下都可以使用觀察者模式 + +* 當抽象具有兩個方面時,一個方面依賴於另一個方面。將這些方面封裝在單獨的物件中,可以使你分別進行更改和重用 +* 當一個物件的改變的同時需要改變其他物件,同時你又不知道有多少物件需要改變時 +* 當一個物件可以通知其他物件而無需假設這些物件是誰時。換句話說,你不想讓這些物件緊耦合。 + +## 典型用例 + +* 一個物件的改變導致其他物件的改變 + +## Java中的例子 + +* [java.util.Observer](http://docs.oracle.com/javase/8/docs/api/java/util/Observer.html) +* [java.util.EventListener](http://docs.oracle.com/javase/8/docs/api/java/util/EventListener.html) +* [javax.servlet.http.HttpSessionBindingListener](http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpSessionBindingListener.html) +* [RxJava](https://github.com/ReactiveX/RxJava) + +## 鳴謝 + +* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59) +* [Java Generics and Collections](https://www.amazon.com/gp/product/0596527756/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596527756&linkCode=as2&tag=javadesignpat-20&linkId=246e5e2c26fe1c3ada6a70b15afcb195) +* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b) +* [Refactoring to Patterns](https://www.amazon.com/gp/product/0321213351/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321213351&linkCode=as2&tag=javadesignpat-20&linkId=2a76fcb387234bc71b1c61150b3cc3a7) diff --git a/localization/zh-TW/observer/etc/observer.png b/localization/zh-TW/observer/etc/observer.png new file mode 100644 index 000000000000..f2ab0edfeb21 Binary files /dev/null and b/localization/zh-TW/observer/etc/observer.png differ diff --git a/localization/zh-TW/private-class-data/README.md b/localization/zh-TW/private-class-data/README.md new file mode 100644 index 000000000000..2f65dcbce48e --- /dev/null +++ b/localization/zh-TW/private-class-data/README.md @@ -0,0 +1,127 @@ +--- +title: Private Class Data +shortTitle: Private Class Data +category: Idiom +language: zh +tag: + - Data access +--- + +## 目的 + +私有類資料設計模式試圖透過限制屬性的可見性來減少屬性的暴露。 透過將它們封裝在單個Data物件中,可以減少類屬性的數量。 + +## 解釋 + +真實世界例子 + +> 想象一下你在為家人做晚餐燉湯。你想阻止家庭成員在你烹飪時偷偷品嚐菜品,否則後面可能東西不夠吃了。 + +通俗的說 + +> 私有類資料模式透過將資料與使用它的方法分離到維護資料狀態的類中,從而防止了對不可變資料的操縱。 + +維基百科說 + +> 私有類資料是計算機程式設計中的一種設計模式,用於封裝類屬性及其操作。 + +**程式示例** + +使用上面燉湯的例子。 首先我們有 `燉湯`類 ,它的屬性沒有被私有類資料保護,從而使燉菜的成分對類方法易變。 + +```java +public class Stew { + private static final Logger LOGGER = LoggerFactory.getLogger(Stew.class); + private int numPotatoes; + private int numCarrots; + private int numMeat; + private int numPeppers; + public Stew(int numPotatoes, int numCarrots, int numMeat, int numPeppers) { + this.numPotatoes = numPotatoes; + this.numCarrots = numCarrots; + this.numMeat = numMeat; + this.numPeppers = numPeppers; + } + public void mix() { + LOGGER.info("Mixing the stew we find: {} potatoes, {} carrots, {} meat and {} peppers", + numPotatoes, numCarrots, numMeat, numPeppers); + } + public void taste() { + LOGGER.info("Tasting the stew"); + if (numPotatoes > 0) { + numPotatoes--; + } + if (numCarrots > 0) { + numCarrots--; + } + if (numMeat > 0) { + numMeat--; + } + if (numPeppers > 0) { + numPeppers--; + } + } +} +``` + +現在,我們有了` ImmutableStew`類,其中的資料受`StewData`類保護。 現在,其中的方法無法處理`ImmutableStew`類的資料。 + +```java +public class StewData { + private final int numPotatoes; + private final int numCarrots; + private final int numMeat; + private final int numPeppers; + public StewData(int numPotatoes, int numCarrots, int numMeat, int numPeppers) { + this.numPotatoes = numPotatoes; + this.numCarrots = numCarrots; + this.numMeat = numMeat; + this.numPeppers = numPeppers; + } + public int getNumPotatoes() { + return numPotatoes; + } + public int getNumCarrots() { + return numCarrots; + } + public int getNumMeat() { + return numMeat; + } + public int getNumPeppers() { + return numPeppers; + } +} +public class ImmutableStew { + private static final Logger LOGGER = LoggerFactory.getLogger(ImmutableStew.class); + private final StewData data; + public ImmutableStew(int numPotatoes, int numCarrots, int numMeat, int numPeppers) { + data = new StewData(numPotatoes, numCarrots, numMeat, numPeppers); + } + public void mix() { + LOGGER + .info("Mixing the immutable stew we find: {} potatoes, {} carrots, {} meat and {} peppers", + data.getNumPotatoes(), data.getNumCarrots(), data.getNumMeat(), data.getNumPeppers()); + } +} +``` + +讓我們嘗試建立每個類的例項並呼叫其方法: + +```java +var stew = new Stew(1, 2, 3, 4); +stew.mix(); // Mixing the stew we find: 1 potatoes, 2 carrots, 3 meat and 4 peppers +stew.taste(); // Tasting the stew +stew.mix(); // Mixing the stew we find: 0 potatoes, 1 carrots, 2 meat and 3 peppers +var immutableStew = new ImmutableStew(2, 4, 3, 6); +immutableStew.mix(); // Mixing the immutable stew we find: 2 potatoes, 4 carrots, 3 meat and 6 peppers +``` + +## 類圖 + +![alt text](./etc/private-class-data.png "Private Class Data") + +## 適用性 + +在以下情況下使用私有類資料模式 + +* 你要阻止對類資料成員的寫訪問。 diff --git a/localization/zh-TW/private-class-data/etc/private-class-data.png b/localization/zh-TW/private-class-data/etc/private-class-data.png new file mode 100644 index 000000000000..65c343a5faf9 Binary files /dev/null and b/localization/zh-TW/private-class-data/etc/private-class-data.png differ diff --git a/localization/zh-TW/producer-consumer/README.md b/localization/zh-TW/producer-consumer/README.md new file mode 100644 index 000000000000..0636a1e85421 --- /dev/null +++ b/localization/zh-TW/producer-consumer/README.md @@ -0,0 +1,20 @@ +--- +title: Producer Consumer +shortTitle: Producer Consumer +category: Concurrency +language: zh +tag: + - Reactive +--- + +## 目的 +生產者消費者設計模式是一種經典的併發模式,透過將工作與執行工作任務分開來減少生產者與消費者之間的耦合。 + +## 類圖 +![alt text](./etc/producer-consumer.png "Producer Consumer") + +## 適用性 +在以下情況下使用生產者消費者 + +* 透過將工作分成生產和消費兩個工作程序來解耦系統 +* 解決生產工作和消費工作需要不同時間的問題 diff --git a/localization/zh-TW/producer-consumer/etc/producer-consumer.png b/localization/zh-TW/producer-consumer/etc/producer-consumer.png new file mode 100644 index 000000000000..e8bc573b388f Binary files /dev/null and b/localization/zh-TW/producer-consumer/etc/producer-consumer.png differ diff --git a/localization/zh-TW/proxy/README.md b/localization/zh-TW/proxy/README.md new file mode 100644 index 000000000000..8f389b6c9f26 --- /dev/null +++ b/localization/zh-TW/proxy/README.md @@ -0,0 +1,160 @@ +--- +title: Proxy +shortTitle: Proxy +category: Structural +language: zh +tag: + - Gang Of Four + - Decoupling +--- + +## 又被稱為 + +替代(代孕)模式 + +## 目的 + +為另一個物件提供代理或佔位符以控制對其的訪問。 + +## 解釋 + +真實世界例子 + +> 想象有一個塔,當地的巫師去那裡學習他們的法術。象牙塔只能夠透過代理來進入以此來保證只有首先3個巫師才能進入。這裡的代理就代表的塔的功能並新增訪問控制。 + +通俗的說 + +> 使用代理模式,一個類代表另一個類的功能。 + +維基百科說 + +> 在最一般的形式上,代理是一個類,它充當與其他物件的介面。代理是客戶端呼叫的包裝器或代理物件,以訪問後臺的實際服務物件。代理本身可以簡單地轉發到真實物件,也可以提供其他邏輯。在代理中,可以提供額外的功能,例如在對實物件的操作佔用大量資源時進行快取,或者在對實物件的操作被呼叫之前檢查前提條件。 + +**程式示例** + +使用上面的巫師塔為例。首先我們有**巫師塔**介面和**象牙塔**類 。 + +```java +public interface WizardTower { + + void enter(Wizard wizard); +} + +public class IvoryTower implements WizardTower { + + private static final Logger LOGGER = LoggerFactory.getLogger(IvoryTower.class); + + public void enter(Wizard wizard) { + LOGGER.info("{} enters the tower.", wizard); + } + +} +``` + +然後有個簡單的巫師類。 + +```java +public class Wizard { + + private final String name; + + public Wizard(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } +} +``` + +然後我們有巫師塔代理類為巫師塔新增訪問控制。 + +```java +public class WizardTowerProxy implements WizardTower { + + private static final Logger LOGGER = LoggerFactory.getLogger(WizardTowerProxy.class); + + private static final int NUM_WIZARDS_ALLOWED = 3; + + private int numWizards; + + private final WizardTower tower; + + public WizardTowerProxy(WizardTower tower) { + this.tower = tower; + } + + @Override + public void enter(Wizard wizard) { + if (numWizards < NUM_WIZARDS_ALLOWED) { + tower.enter(wizard); + numWizards++; + } else { + LOGGER.info("{} is not allowed to enter!", wizard); + } + } +} +``` + +然後這是進入塔的場景。 + +```java +var proxy = new WizardTowerProxy(new IvoryTower()); +proxy.enter(new Wizard("Red wizard")); +proxy.enter(new Wizard("White wizard")); +proxy.enter(new Wizard("Black wizard")); +proxy.enter(new Wizard("Green wizard")); +proxy.enter(new Wizard("Brown wizard")); +``` + +程式輸出: + +``` +Red wizard enters the tower. +White wizard enters the tower. +Black wizard enters the tower. +Green wizard is not allowed to enter! +Brown wizard is not allowed to enter! +``` + +## 類圖 + +![alt text](./etc/proxy.urm.png "Proxy pattern class diagram") + +## 適用性 + +代理適用於需要比簡單指標更廣泛或更復雜的物件引用的情況。這是代理模式適用的幾種常見情況。 + +* 遠端代理為不同地址空間中的物件提供了本地代表。 +* 虛擬代理根據需要建立昂貴的物件。 +* 保護代理控制對原始物件的訪問。當物件有不同的接入許可權時保護代理很有用。 + +## 典型用例 + +* 物件的訪問控制 +* 懶載入 +* 實現日誌記錄 +* 簡化網路連線 +* 物件的訪問計數 + +## 教程 + +* [Controlling Access With Proxy Pattern](http://java-design-patterns.com/blog/controlling-access-with-proxy-pattern/) + +## 已知使用 + +* [java.lang.reflect.Proxy](http://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Proxy.html) +* [Apache Commons Proxy](https://commons.apache.org/proper/commons-proxy/) +* Mocking frameworks [Mockito](https://site.mockito.org/), +[Powermock](https://powermock.github.io/), [EasyMock](https://easymock.org/) + +## 相關設計模式 + +* [Ambassador](https://java-design-patterns.com/patterns/ambassador/) + +## 鳴謝 + +* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59) +* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b) diff --git a/localization/zh-TW/proxy/etc/proxy.urm.png b/localization/zh-TW/proxy/etc/proxy.urm.png new file mode 100644 index 000000000000..a0c94fc7c717 Binary files /dev/null and b/localization/zh-TW/proxy/etc/proxy.urm.png differ diff --git a/localization/zh-TW/servant/README.md b/localization/zh-TW/servant/README.md new file mode 100644 index 000000000000..195a1389835d --- /dev/null +++ b/localization/zh-TW/servant/README.md @@ -0,0 +1,231 @@ +--- +title: Servant +shortTitle: Servant +category: Behavioral +language: zh +tag: +- Decoupling +--- + +## 含義 +僕人類被用於向一組類提供一些行為,區別於在每個類定義行為-或者當我們無法排除 +公共父類中的這種行為,這些行為在僕人類被定義一次 + +## 解釋 + +現例項子 + +> 國王、王后和其他宮廷皇室成員需要僕人為他們提供飲食、準備飲料等服務 + +簡單地說 + +> 確保一個僕人物件為一組被服務的物件提供一些特定的服務 + +維基百科 + +> 在軟體工程中,僕人模式定義了一個物件,用於向一組類提供某些功能,而無需在每個類中定義該功能。 僕人是一個類,其例項(甚至只是類)提供了處理所需服務的方法,而僕人為其(或與誰)做某事的物件被視為引數。 + +**程式設計示例** + +那些能夠為其他宮廷皇室成員提供服務的僕人類 + +```java +/** + * Servant. + */ +public class Servant { + + public String name; + + /** + * Constructor. + */ + public Servant(String name) { + this.name = name; + } + + public void feed(Royalty r) { + r.getFed(); + } + + public void giveWine(Royalty r) { + r.getDrink(); + } + + public void giveCompliments(Royalty r) { + r.receiveCompliments(); + } + + /** + * Check if we will be hanged. + */ + public boolean checkIfYouWillBeHanged(List tableGuests) { + return tableGuests.stream().allMatch(Royalty::getMood); + } +} +``` + +皇家是一個介面,它被國王和女王類實現,以獲取僕人的服務 + +```java +interface Royalty { + + void getFed(); + + void getDrink(); + + void changeMood(); + + void receiveCompliments(); + + boolean getMood(); +} +``` +國王類正在實現皇家介面 +```java +public class King implements Royalty { + + private boolean isDrunk; + private boolean isHungry = true; + private boolean isHappy; + private boolean complimentReceived; + + @Override + public void getFed() { + isHungry = false; + } + + @Override + public void getDrink() { + isDrunk = true; + } + + public void receiveCompliments() { + complimentReceived = true; + } + + @Override + public void changeMood() { + if (!isHungry && isDrunk) { + isHappy = true; + } + if (complimentReceived) { + isHappy = false; + } + } + + @Override + public boolean getMood() { + return isHappy; + } +} +``` +女王類正在實現皇家介面 +```java +public class Queen implements Royalty { + + private boolean isDrunk = true; + private boolean isHungry; + private boolean isHappy; + private boolean isFlirty = true; + private boolean complimentReceived; + + @Override + public void getFed() { + isHungry = false; + } + + @Override + public void getDrink() { + isDrunk = true; + } + + public void receiveCompliments() { + complimentReceived = true; + } + + @Override + public void changeMood() { + if (complimentReceived && isFlirty && isDrunk && !isHungry) { + isHappy = true; + } + } + + @Override + public boolean getMood() { + return isHappy; + } + + public void setFlirtiness(boolean f) { + this.isFlirty = f; + } + +} +``` + +然後,為了使用: + +```java +public class App { + + private static final Servant jenkins = new Servant("Jenkins"); + private static final Servant travis = new Servant("Travis"); + + /** + * Program entry point. + */ + public static void main(String[] args) { + scenario(jenkins, 1); + scenario(travis, 0); + } + + /** + * Can add a List with enum Actions for variable scenarios. + */ + public static void scenario(Servant servant, int compliment) { + var k = new King(); + var q = new Queen(); + + var guests = List.of(k, q); + + // feed + servant.feed(k); + servant.feed(q); + // serve drinks + servant.giveWine(k); + servant.giveWine(q); + // compliment + servant.giveCompliments(guests.get(compliment)); + + // outcome of the night + guests.forEach(Royalty::changeMood); + + // check your luck + if (servant.checkIfYouWillBeHanged(guests)) { + LOGGER.info("{} will live another day", servant.name); + } else { + LOGGER.info("Poor {}. His days are numbered", servant.name); + } + } +} +``` + +程式輸出 + +``` +Jenkins will live another day +Poor Travis. His days are numbered +``` + + +## 類圖 +![alt text](./etc/servant-pattern.png "Servant") + +## 適用場景 +在什麼時候使用僕人模式 + +* 當我們希望某些物件執行一個公共操作並且不想將該操作定義為每個類中的方法時 + +## 鳴謝 + +* [Let's Modify the Objects-First Approach into Design-Patterns-First](http://edu.pecinovsky.cz/papers/2006_ITiCSE_Design_Patterns_First.pdf) diff --git a/localization/zh-TW/servant/etc/servant-pattern.png b/localization/zh-TW/servant/etc/servant-pattern.png new file mode 100644 index 000000000000..a8237775e332 Binary files /dev/null and b/localization/zh-TW/servant/etc/servant-pattern.png differ diff --git a/localization/zh-TW/sharding/README.md b/localization/zh-TW/sharding/README.md new file mode 100644 index 000000000000..34db73830212 --- /dev/null +++ b/localization/zh-TW/sharding/README.md @@ -0,0 +1,29 @@ +--- +title: Sharding +shortTitle: Sharding +category: Behavioral +language: zh +tag: + - Performance + - Cloud distributed +--- + +## 含義 +分片模式是指將資料儲存劃分為水平分割槽或分片。每個分片都有相同的模式,但持有自己獨特的資料子集。 + +一個分片本身就是一個資料儲存(它可以包含許多不同型別的實體的資料),執行在作為儲存節點的伺服器上。 + +## 類圖 +![alt text](./etc/sharding.urm.png "Sharding pattern class diagram") + +## 適用場景 +這種設計模式提供了一下的好處: + +- 你可以透過增加在額外的儲存節點上,執行的更多分片來實現系統擴容。 +- 系統可以使用現成的廉價硬體,而不是為每個儲存節點使用專門(或者昂貴)的伺服器硬體。 +- 你可以透過平衡各分片之間的工作負載來減少競爭,以提高效能。 +- 在雲環境中,分片可以在物理上靠近訪問該節點資料的使用者。 + +## 引用 + +* [Sharding pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/sharding) \ No newline at end of file diff --git a/localization/zh-TW/sharding/etc/sharding.urm.png b/localization/zh-TW/sharding/etc/sharding.urm.png new file mode 100644 index 000000000000..e7f412af3f01 Binary files /dev/null and b/localization/zh-TW/sharding/etc/sharding.urm.png differ diff --git a/localization/zh-TW/singleton/README.md b/localization/zh-TW/singleton/README.md new file mode 100644 index 000000000000..61f942ffa67b --- /dev/null +++ b/localization/zh-TW/singleton/README.md @@ -0,0 +1,94 @@ +--- +title: Singleton +shortTitle: Singleton +category: Creational +language: zh +tag: + - Gang of Four +--- + +## 目的 + +確保一個類只有一個例項,併為其提供一個全域性訪問點。 + +## 解釋 + +情境示例 + +> 巫師們之在一個象牙塔中學習他們的魔法,並且始終使用同一座附魔的象牙塔。 +> +> 這裡的象牙塔是一個單例物件。 + +通俗來說 + +> 對於一個特定的類,確保只會建立一個物件。 + +維基百科說 + +> 在軟體工程中,單例模式是一種軟體設計模式,它將類的例項化限制為一個物件。當系統中只需要一個物件來協調各種操作時,這種模式非常有用。 + +**程式示例** + +詳見 Joshua Bloch, Effective Java 2nd Edition p.18。 + +> 一個只有一個元素的列舉型別是實現單例模式的最佳方式。 + +```java +public enum EnumIvoryTower { + INSTANCE +} +``` + +使用: + +```java + var enumIvoryTower1 = EnumIvoryTower.INSTANCE; + var enumIvoryTower2 = EnumIvoryTower.INSTANCE; + LOGGER.info("enumIvoryTower1={}", enumIvoryTower1); + LOGGER.info("enumIvoryTower2={}", enumIvoryTower2); +``` + +控制檯輸出: + +``` +enumIvoryTower1=com.iluwatar.singleton.EnumIvoryTower@1221555852 +enumIvoryTower2=com.iluwatar.singleton.EnumIvoryTower@1221555852 +``` + +## 類圖 + +![alt text](./etc/singleton.urm.png "Singleton pattern class diagram") + +## 適用性 + +當滿足以下情況時,使用單例模式: + +* 確保一個類只有一個例項,並且客戶端能夠透過一個眾所周知的訪問點訪問該例項。 +* 唯一的例項能夠被子類擴充套件, 同時客戶端不需要修改程式碼就能使用擴充套件後的例項。 + +一些典型的單例模式用例包括: + +* logging類 +* 管理與資料庫的連結 +* 檔案管理器(File manager) + +## 已知使用 + +* [java.lang.Runtime#getRuntime()](http://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html#getRuntime%28%29) +* [java.awt.Desktop#getDesktop()](http://docs.oracle.com/javase/8/docs/api/java/awt/Desktop.html#getDesktop--) +* [java.lang.System#getSecurityManager()](http://docs.oracle.com/javase/8/docs/api/java/lang/System.html#getSecurityManager--) + + +## 影響 + +* 透過控制例項的建立和生命週期,違反了單一職責原則(SRP)。 +* 鼓勵使用全域性共享例項,組織了物件及其使用的資源被釋放。 +* 程式碼變得耦合,給客戶端的測試帶來難度。 +* 單例模式的設計可能會使得子類化(繼承)單例變得幾乎不可能 + +## 鳴謝 + +* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59) +* [Effective Java](https://www.amazon.com/gp/product/0134685997/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0134685997&linkCode=as2&tag=javadesignpat-20&linkId=4e349f4b3ff8c50123f8147c828e53eb) +* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b) +* [Refactoring to Patterns](https://www.amazon.com/gp/product/0321213351/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321213351&linkCode=as2&tag=javadesignpat-20&linkId=2a76fcb387234bc71b1c61150b3cc3a7) diff --git a/localization/zh-TW/singleton/etc/singleton.urm.png b/localization/zh-TW/singleton/etc/singleton.urm.png new file mode 100644 index 000000000000..46584af40039 Binary files /dev/null and b/localization/zh-TW/singleton/etc/singleton.urm.png differ diff --git a/localization/zh-TW/state/README.md b/localization/zh-TW/state/README.md new file mode 100644 index 000000000000..2b75b61abe69 --- /dev/null +++ b/localization/zh-TW/state/README.md @@ -0,0 +1,154 @@ +--- +title: State +shortTitle: State +category: Behavioral +language: zh +tag: + - Gang of Four +--- + +## 又被稱為 +物件狀態 + +## 目的 +允許物件在內部狀態改變時改變它的行為。物件看起來好像修改了它的類。 + +## 解釋 +真實世界例子 + +> 當在長毛象的自然棲息地觀察長毛象時,似乎它會根據情況來改變自己的行為。它開始可能很平靜但是隨著時間推移當它檢測到威脅時它會對周圍的環境感到憤怒和危險。 + +通俗的說 + +> 狀態模式允許物件改變它的行為。 + +維基百科說 + +> 狀態模式是一種允許物件在內部狀態改變時改變它的行為的行為型設計模式。這種模式接近於有限狀態機的概念。狀態模式可以被理解為策略模式,它能夠透過呼叫在模式介面中定義的方法來切換策略。 + +**程式設計示例** + +這裡是模式介面和它具體的實現。 + +```java +public interface State { + + void onEnterState(); + + void observe(); +} + +public class PeacefulState implements State { + + private static final Logger LOGGER = LoggerFactory.getLogger(PeacefulState.class); + + private final Mammoth mammoth; + + public PeacefulState(Mammoth mammoth) { + this.mammoth = mammoth; + } + + @Override + public void observe() { + LOGGER.info("{} is calm and peaceful.", mammoth); + } + + @Override + public void onEnterState() { + LOGGER.info("{} calms down.", mammoth); + } +} + +public class AngryState implements State { + + private static final Logger LOGGER = LoggerFactory.getLogger(AngryState.class); + + private final Mammoth mammoth; + + public AngryState(Mammoth mammoth) { + this.mammoth = mammoth; + } + + @Override + public void observe() { + LOGGER.info("{} is furious!", mammoth); + } + + @Override + public void onEnterState() { + LOGGER.info("{} gets angry!", mammoth); + } +} +``` + +然後這裡是包含狀態的長毛象。 + +```java +public class Mammoth { + + private State state; + + public Mammoth() { + state = new PeacefulState(this); + } + + public void timePasses() { + if (state.getClass().equals(PeacefulState.class)) { + changeStateTo(new AngryState(this)); + } else { + changeStateTo(new PeacefulState(this)); + } + } + + private void changeStateTo(State newState) { + this.state = newState; + this.state.onEnterState(); + } + + @Override + public String toString() { + return "The mammoth"; + } + + public void observe() { + this.state.observe(); + } +} +``` + +然後這裡是長毛象隨著時間的推移後的整個行為示例。 + +```java + var mammoth = new Mammoth(); + mammoth.observe(); + mammoth.timePasses(); + mammoth.observe(); + mammoth.timePasses(); + mammoth.observe(); + + // The mammoth gets angry! + // The mammoth is furious! + // The mammoth calms down. + // The mammoth is calm and peaceful. +``` + +## 類圖 +![alt text](./etc/state_urm.png "State") + +## 適用性 + +在以下兩種情況下,請使用State模式 + +* 物件的行為取決於它的狀態,並且它必須在執行時根據狀態更改其行為。 +* 根據物件狀態的不同,操作有大量的條件語句。此狀態通常由一個或多個列舉常量表示。通常,幾個操作將包含此相同的條件結構。狀態模式把條件語句的分支分別放入單獨的類中。這樣一來,你就可以將物件的狀態視為獨立的物件,該物件可以獨立於其他物件而變化。 + +## Java中例子 + +* [javax.faces.lifecycle.Lifecycle#execute()](http://docs.oracle.com/javaee/7/api/javax/faces/lifecycle/Lifecycle.html#execute-javax.faces.context.FacesContext-) controlled by [FacesServlet](http://docs.oracle.com/javaee/7/api/javax/faces/webapp/FacesServlet.html), the behavior is dependent on current phase of lifecycle. +* [JDiameter - Diameter State Machine](https://github.com/npathai/jdiameter/blob/master/core/jdiameter/api/src/main/java/org/jdiameter/api/app/State.java) + +## 鳴謝 + +* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59) +* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b) +* [Refactoring to Patterns](https://www.amazon.com/gp/product/0321213351/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321213351&linkCode=as2&tag=javadesignpat-20&linkId=2a76fcb387234bc71b1c61150b3cc3a7) diff --git a/localization/zh-TW/state/etc/state_urm.png b/localization/zh-TW/state/etc/state_urm.png new file mode 100644 index 000000000000..c2cf9f562943 Binary files /dev/null and b/localization/zh-TW/state/etc/state_urm.png differ diff --git a/localization/zh-TW/step-builder/README.md b/localization/zh-TW/step-builder/README.md new file mode 100644 index 000000000000..09d46925c4c4 --- /dev/null +++ b/localization/zh-TW/step-builder/README.md @@ -0,0 +1,25 @@ +--- +title: Step Builder +shortTitle: Step Builder +category: Creational +language: zn +tag: + - Instantiation +--- + +## 又被稱為 +分步構建 + +## 目的 +這是構建者模式的一個擴充套件,完全指導使用者建立物件,沒有混淆的機會。 +使用者體驗會大大提升,因為他只能看到下一個步驟的方法,直到適當的時機才會出現構建物件的“build”方法。 + +## 類圖 +![alt text](./etc/step-builder.png "Step Builder") + +## 應用 +使用分佈構建模式當建立複雜物件的演算法需要獨立於組成物件的部分以及它們的組裝方式,且構造過程必須允許物件有不同的表示形式,並且在此過程中順序很重要時。 + +## 鳴謝 + +* [Marco Castigliego - Step Builder](http://rdafbn.blogspot.co.uk/2012/07/step-builder-pattern_28.html) diff --git a/localization/zh-TW/step-builder/etc/step-builder.png b/localization/zh-TW/step-builder/etc/step-builder.png new file mode 100644 index 000000000000..b7b623657c58 Binary files /dev/null and b/localization/zh-TW/step-builder/etc/step-builder.png differ diff --git a/localization/zh-TW/strategy/README.md b/localization/zh-TW/strategy/README.md new file mode 100644 index 000000000000..19de6403ec04 --- /dev/null +++ b/localization/zh-TW/strategy/README.md @@ -0,0 +1,132 @@ +--- +title: Strategy +shortTitle: Strategy +category: Behavioral +language: zh +tag: + - Gang of Four +--- + +## 又被稱為 +政策(方針)模式 + +## 目的 + +定義一個家族演算法,並封裝好其中每一個,使它們可以互相替換。策略模式使演算法的變化獨立於使用它的客戶。 + +## 解釋 + +現實世界例子 + +> 屠龍是一項危險的職業。有經驗將會使它變得簡單。經驗豐富的屠龍者對不同型別的龍有不同的戰鬥策略。 + +直白點說 + +> 策略模式允許在執行時選擇最匹配的演算法。 + +維基百科上說 + +> 在程式程式設計領域,策略模式(又叫政策模式)是一種啟用在執行時選擇演算法的行為型軟體設計模式。 + +**程式設計例項** + +讓我們先介紹屠龍的策略模式介面和它的實現。 + +```java +@FunctionalInterface +public interface DragonSlayingStrategy { + + void execute(); +} + +@Slf4j +public class MeleeStrategy implements DragonSlayingStrategy { + + @Override + public void execute() { + LOGGER.info("With your Excalibur you sever the dragon's head!"); + } +} + +@Slf4j +public class ProjectileStrategy implements DragonSlayingStrategy { + + @Override + public void execute() { + LOGGER.info("You shoot the dragon with the magical crossbow and it falls dead on the ground!"); + } +} + +@Slf4j +public class SpellStrategy implements DragonSlayingStrategy { + + @Override + public void execute() { + LOGGER.info("You cast the spell of disintegration and the dragon vaporizes in a pile of dust!"); + } +} +``` + +現在有一個強力的屠龍者要基於上面的元件來選擇他的戰鬥策略。 + +```java +public class DragonSlayer { + + private DragonSlayingStrategy strategy; + + public DragonSlayer(DragonSlayingStrategy strategy) { + this.strategy = strategy; + } + + public void changeStrategy(DragonSlayingStrategy strategy) { + this.strategy = strategy; + } + + public void goToBattle() { + strategy.execute(); + } +} +``` + +最後是屠龍者的行動。 + +```java + LOGGER.info("Green dragon spotted ahead!"); + var dragonSlayer = new DragonSlayer(new MeleeStrategy()); + dragonSlayer.goToBattle(); + LOGGER.info("Red dragon emerges."); + dragonSlayer.changeStrategy(new ProjectileStrategy()); + dragonSlayer.goToBattle(); + LOGGER.info("Black dragon lands before you."); + dragonSlayer.changeStrategy(new SpellStrategy()); + dragonSlayer.goToBattle(); + + // Green dragon spotted ahead! + // With your Excalibur you sever the dragon's head! + // Red dragon emerges. + // You shoot the dragon with the magical crossbow and it falls dead on the ground! + // Black dragon lands before you. + // You cast the spell of disintegration and the dragon vaporizes in a pile of dust! +``` + +## 類圖 +![alt text](./etc/strategy_urm.png "Strategy") + +## 應用 +使用策略模式當 + +* 許多相關的類只是行為不同。策略模式提供了一種為一種類配置多種行為的能力。 +* 你需要一種演算法的不同變體。比如,你可能定義反應不用時間空間權衡的演算法。當這些演算法的變體使用類的層次結構來實現時就可以使用策略模式。 +* 一個演算法使用的資料客戶不應該對其知曉。使用策略模式來避免暴露覆雜的,特定於演算法的資料結構。 +* 一個類定義了許多行為,這些行為在其操作中展現為多個條件語句。移動相關的條件分支到它們分別的策略類中來代替這些條件語句。 + +## 教學 + +* [Strategy Pattern Tutorial](https://www.journaldev.com/1754/strategy-design-pattern-in-java-example-tutorial) + +## 鳴謝 + +* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59) +* [Functional Programming in Java: Harnessing the Power of Java 8 Lambda Expressions](https://www.amazon.com/gp/product/1937785467/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1937785467&linkCode=as2&tag=javadesignpat-20&linkId=7e4e2fb7a141631491534255252fd08b) +* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b) +* [Refactoring to Patterns](https://www.amazon.com/gp/product/0321213351/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321213351&linkCode=as2&tag=javadesignpat-20&linkId=2a76fcb387234bc71b1c61150b3cc3a7) diff --git a/localization/zh-TW/strategy/etc/strategy_urm.png b/localization/zh-TW/strategy/etc/strategy_urm.png new file mode 100644 index 000000000000..67d19acae62d Binary files /dev/null and b/localization/zh-TW/strategy/etc/strategy_urm.png differ diff --git a/localization/zh-TW/table-module/README.md b/localization/zh-TW/table-module/README.md new file mode 100644 index 000000000000..2a46ec2b9e9f --- /dev/null +++ b/localization/zh-TW/table-module/README.md @@ -0,0 +1,136 @@ +--- +title: Table Module +shortTitle: Table Module +category: Structural +language: zh +tag: + - Data access +--- + +## 又被稱為 +表模組 + +## Intent +表模組模式將域邏輯按資料庫中的每個表組織為一個類,並且一個類的單個例項包含將對資料執行的各種過程。 + +## Explanation + +現實世界例子 + +> 當處理一個使用者系統時,我們需要在使用者表上進行一些操作。在這種情況下,我們可以使用表模組模式。我們可以建立一個名為 UserTableModule 的類,並初始化該類的一個例項,來處理使用者表中所有行的業務邏輯。 + +直白點說 + +> 一個單獨的例項,處理資料庫表或檢視中所有行的業務邏輯。 + +**程式設計例項** + +在使用者系統的示例中,我們需要處理使用者登入和使用者註冊的域邏輯。我們可以使用表模組模式,並建立UserTableModule類的一個例項來處理使用者表中所有行的業務邏輯。 + +以下是基本的User實體。 + +```java +@Setter +@Getter +@ToString +@EqualsAndHashCode +@AllArgsConstructor +public class User { + private int id; + private String username; + private String password; +} +``` + +下面的是 `UserTableModule` 類. + +```java +public class UserTableModule { + private final DataSource dataSource; + private Connection connection = null; + private ResultSet resultSet = null; + private PreparedStatement preparedStatement = null; + + public UserTableModule(final DataSource userDataSource) { + this.dataSource = userDataSource; + } + + /** + * Login using username and password. + * + * @param username the username of a user + * @param password the password of a user + * @return the execution result of the method + * @throws SQLException if any error + */ + public int login(final String username, final String password) throws SQLException { + // Method implementation. + + } + + /** + * Register a new user. + * + * @param user a user instance + * @return the execution result of the method + * @throws SQLException if any error + */ + public int registerUser(final User user) throws SQLException { + // Method implementation. + } +} +``` + +在App類中,我們使用UserTableModule的一個例項來處理使用者登入和註冊。 + +```java +// Create data source and create the user table. +final var dataSource = createDataSource(); +createSchema(dataSource); +userTableModule = new UserTableModule(dataSource); + +//Initialize two users. +var user1 = new User(1, "123456", "123456"); +var user2 = new User(2, "test", "password"); + +//Login and register using the instance of userTableModule. +userTableModule.registerUser(user1); +userTableModule.login(user1.getUsername(), user1.getPassword()); +userTableModule.login(user2.getUsername(), user2.getPassword()); +userTableModule.registerUser(user2); +userTableModule.login(user2.getUsername(), user2.getPassword()); + +deleteSchema(dataSource); +``` + +程式輸出: + +```java +12:22:13.095 [main] INFO com.iluwatar.tablemodule.UserTableModule - Register successfully! +12:22:13.117 [main] INFO com.iluwatar.tablemodule.UserTableModule - Login successfully! +12:22:13.128 [main] INFO com.iluwatar.tablemodule.UserTableModule - Fail to login! +12:22:13.136 [main] INFO com.iluwatar.tablemodule.UserTableModule - Register successfully! +12:22:13.144 [main] INFO com.iluwatar.tablemodule.UserTableModule - Login successfully! +``` + +## 類圖 + +![](etc/table-module.urm.png "table module") + +## 應用 +使用表模組模式當: + +- 域邏輯簡單且資料呈表格形式。 +- 應用程式僅使用少量共享的常見的面向表格的資料結構。 + +## 教學 + +- [Transaction Script](https://java-design-patterns.com/patterns/transaction-script/) + +- [Domain Model](https://java-design-patterns.com/patterns/domain-model/) + +## 鳴謝 + +* [Table Module Pattern](http://wiki3.cosc.canterbury.ac.nz/index.php/Table_module_pattern) +* [Patterns of Enterprise Application Architecture](https://www.amazon.com/gp/product/0321127420/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=0321127420&linkId=18acc13ba60d66690009505577c45c04) +* [Architecture patterns: domain model and friends](https://inviqa.com/blog/architecture-patterns-domain-model-and-friends) \ No newline at end of file diff --git a/localization/zh-TW/table-module/etc/table-module.urm.png b/localization/zh-TW/table-module/etc/table-module.urm.png new file mode 100644 index 000000000000..9c4bc0b189db Binary files /dev/null and b/localization/zh-TW/table-module/etc/table-module.urm.png differ diff --git a/localization/zh-TW/template-method/README.md b/localization/zh-TW/template-method/README.md new file mode 100644 index 000000000000..7025095dce4c --- /dev/null +++ b/localization/zh-TW/template-method/README.md @@ -0,0 +1,144 @@ +--- +title: Template method +shortTitle: Template method +category: Behavioral +language: zh +tag: + - Gang of Four +--- + +## 目的 +在一個操作中定義演算法的骨架,將某些步驟推遲到子類。模板方法允許子類重新定義演算法的某些步驟,而無需更改演算法的結構。 + +## 解釋 +真實世界例子 + +> 偷東西的一般步驟是相同的。 首先,選擇目標,然後以某種方式使其迷惑,最後,你偷走了該物品。然而這些步驟有很多實現方式。 + +通俗的說 + +> 模板方法模式在父類中列出一般的步驟然後讓具體的子類定義實現細節。 + +維基百科說 + +> 在物件導向的程式設計中,模板方法是Gamma等人確定的行為設計模式之一。在《設計模式》一書中。模板方法是父類中一個方法,通常是一個抽象父類,根據許多高階步驟定義了操作的骨架。這些步驟本身由與模板方法在同一類中的其他幫助程式方法實現。 + +**程式設計示例** + +讓我們首先介紹模板方法類及其具體實現。 + +```java +public abstract class StealingMethod { + + private static final Logger LOGGER = LoggerFactory.getLogger(StealingMethod.class); + + protected abstract String pickTarget(); + + protected abstract void confuseTarget(String target); + + protected abstract void stealTheItem(String target); + + public void steal() { + var target = pickTarget(); + LOGGER.info("The target has been chosen as {}.", target); + confuseTarget(target); + stealTheItem(target); + } +} + +public class SubtleMethod extends StealingMethod { + + private static final Logger LOGGER = LoggerFactory.getLogger(SubtleMethod.class); + + @Override + protected String pickTarget() { + return "shop keeper"; + } + + @Override + protected void confuseTarget(String target) { + LOGGER.info("Approach the {} with tears running and hug him!", target); + } + + @Override + protected void stealTheItem(String target) { + LOGGER.info("While in close contact grab the {}'s wallet.", target); + } +} + +public class HitAndRunMethod extends StealingMethod { + + private static final Logger LOGGER = LoggerFactory.getLogger(HitAndRunMethod.class); + + @Override + protected String pickTarget() { + return "old goblin woman"; + } + + @Override + protected void confuseTarget(String target) { + LOGGER.info("Approach the {} from behind.", target); + } + + @Override + protected void stealTheItem(String target) { + LOGGER.info("Grab the handbag and run away fast!"); + } +} +``` + +這是包含模板方法的半身賊類。 + +```java +public class HalflingThief { + + private StealingMethod method; + + public HalflingThief(StealingMethod method) { + this.method = method; + } + + public void steal() { + method.steal(); + } + + public void changeMethod(StealingMethod method) { + this.method = method; + } +} +``` +最後,我們展示半身人賊如何利用不同的偷竊方法。 + +```java + var thief = new HalflingThief(new HitAndRunMethod()); + thief.steal(); + thief.changeMethod(new SubtleMethod()); + thief.steal(); +``` + +## 類圖 +![alt text](./etc/template_method_urm.png "Template Method") + +## 適用性 + +使用模板方法模式可以 + +* 一次性實現一個演算法中不變的部分並將其留給子類來實現可能變化的行為。 +* 子類之間的共同行為應分解並集中在一個共同類中,以避免程式碼重複。如Opdyke和Johnson所描述的,這是“重構概括”的一個很好的例子。你首先要確定現有程式碼中的差異,然後將差異拆分為新的操作。最後,將不同的程式碼替換為呼叫這些新操作之一的模板方法。 +* 控制子類擴充套件。你可以定義一個模板方法,該方法在特定點呼叫“ 鉤子”操作,從而僅允許在這些點進行擴充套件 + +## 教程 + +* [Template-method Pattern Tutorial](https://www.journaldev.com/1763/template-method-design-pattern-in-java) + +## Java例子 + +* [javax.servlet.GenericServlet.init](https://jakarta.ee/specifications/servlet/4.0/apidocs/javax/servlet/GenericServlet.html#init--): +Method `GenericServlet.init(ServletConfig config)` calls the parameterless method `GenericServlet.init()` which is intended to be overridden in subclasses. +Method `GenericServlet.init(ServletConfig config)` is the template method in this example. + +## 鳴謝 + +* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59) +* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b) +* [Refactoring to Patterns](https://www.amazon.com/gp/product/0321213351/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321213351&linkCode=as2&tag=javadesignpat-20&linkId=2a76fcb387234bc71b1c61150b3cc3a7) diff --git a/localization/zh-TW/template-method/etc/template_method_urm.png b/localization/zh-TW/template-method/etc/template_method_urm.png new file mode 100644 index 000000000000..b7babccff96d Binary files /dev/null and b/localization/zh-TW/template-method/etc/template_method_urm.png differ diff --git a/localization/zh-TW/trampoline/README.md b/localization/zh-TW/trampoline/README.md new file mode 100644 index 000000000000..3bd8011a12d2 --- /dev/null +++ b/localization/zh-TW/trampoline/README.md @@ -0,0 +1,136 @@ +--- +title: Trampoline +shortTitle: Trampoline +category: Behavioral +language: zh +tag: +- Performance +--- + +## 目的 + +蹦床模式是用於在 Java 中遞迴地實現演算法,而不會破壞堆疊,並且可以交錯地執行函式,而無需將它們編碼在一起。 + +## 解釋 + +遞迴是一種常用的技術,用於以分而治之的方式解決演算法問題。例如,計算斐波那契累加和與階乘。在這類問題中,遞迴比迴圈更簡單直接。此外,遞迴可能需要更少的代 +碼並且看起來更簡明。有一種說法是,每個遞迴問題都可以使用迴圈來解決,但代價是編寫更難以理解的程式碼。然而,遞迴型解決方案有一個很大的警告。對於每個 +遞迴呼叫,通常需要儲存一箇中間值,並且可用的棧記憶體有限。棧記憶體不足會導致棧溢位錯誤並停止程式執行。蹦床模式是一種允許在 Java 中定義遞迴演算法而無需破壞 +堆疊的技巧。 + +現實世界例子 + +> 使用蹦床模式進行遞迴斐波那契計算,不存在堆疊溢位問題。 + +通俗地說 + +> 蹦床模式允許遞迴而不會耗盡棧記憶體。 + +維基百科上說 + +> 在 Java 中,蹦床是指使用反射來避免使用內部類,例如在事件偵聽器中。反射呼叫的整理操作時間換成了內部類的整理操作空間。 Java 中的蹦床通常涉及建立 GenericListener 以將事件傳遞到外部類。 + +**程式設計例項** + +這是 Java 中的蹦床實現。 + +當在返回的蹦床上呼叫 `get` 時,只要返回的具體例項是蹦床,內部就會在返回的蹦床上迭代呼叫跳轉,並在返回的例項完成後停止。 + +```java +public interface Trampoline { + + T get(); + + default Trampoline jump() { + return this; + } + + default T result() { + return get(); + } + + default boolean complete() { + return true; + } + + static Trampoline done(final T result) { + return () -> result; + } + + static Trampoline more(final Trampoline> trampoline) { + return new Trampoline() { + @Override + public boolean complete() { + return false; + } + + @Override + public Trampoline jump() { + return trampoline.result(); + } + + @Override + public T get() { + return trampoline(this); + } + + T trampoline(final Trampoline trampoline) { + return Stream.iterate(trampoline, Trampoline::jump) + .filter(Trampoline::complete) + .findFirst() + .map(Trampoline::result) + .orElseThrow(); + } + }; + } +} +``` + +使用蹦床獲取斐波那契值。 + +```java +public static void main(String[] args) { + LOGGER.info("Start calculating war casualties"); + var result = loop(10, 1).result(); + LOGGER.info("The number of orcs perished in the war: {}", result); +} + +public static Trampoline loop(int times, int prod) { + if (times == 0) { + return Trampoline.done(prod); + } else { + return Trampoline.more(() -> loop(times - 1, prod * times)); + } +} +``` + +程式輸出: + +```java +19:22:24.462 [main] INFO com.iluwatar.trampoline.TrampolineApp - Start calculating war casualties +19:22:24.472 [main] INFO com.iluwatar.trampoline.TrampolineApp - The number of orcs perished in the war: 3628800 +``` + +## 類圖 + +![alt text](./etc/trampoline_urm.png "Trampoline pattern class diagram") + +## 適用場景 + +使用蹦床模式時 +* 用於實現尾遞迴函式。該模式允許切換無堆疊操作。 +* 用於在同一執行緒上交錯執行兩個或多個函式。 + +## 現實案例 + +* [cyclops-react](https://github.com/aol/cyclops-react) + +## 鳴謝 + +* [Trampolining: a practical guide for awesome Java Developers](https://medium.com/@johnmcclean/trampolining-a-practical-guide-for-awesome-java-developers-4b657d9c3076) +* [Trampoline in java ](http://mindprod.com/jgloss/trampoline.html) +* [Laziness, trampolines, monoids and other functional amenities: this is not your father's Java](https://www.slideshare.net/mariofusco/lazine) +* [Trampoline implementation](https://github.com/bodar/totallylazy/blob/master/src/com/googlecode/totallylazy/Trampoline.java) +* [What is a trampoline function?](https://stackoverflow.com/questions/189725/what-is-a-trampoline-function) +* [Modern Java in Action: Lambdas, streams, functional and reactive programming](https://www.amazon.com/gp/product/1617293563/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=1617293563&linkId=ad53ae6f9f7c0982e759c3527bd2595c) +* [Java 8 in Action: Lambdas, Streams, and functional-style programming](https://www.amazon.com/gp/product/1617291994/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=1617291994&linkId=e3e5665b0732c59c9d884896ffe54f4f) diff --git a/localization/zh-TW/trampoline/etc/trampoline_urm.png b/localization/zh-TW/trampoline/etc/trampoline_urm.png new file mode 100644 index 000000000000..f2e9c7439a32 Binary files /dev/null and b/localization/zh-TW/trampoline/etc/trampoline_urm.png differ diff --git a/localization/zh-TW/unit-of-work/README.md b/localization/zh-TW/unit-of-work/README.md new file mode 100644 index 000000000000..000cb76266cf --- /dev/null +++ b/localization/zh-TW/unit-of-work/README.md @@ -0,0 +1,204 @@ +--- +title: Unit Of Work +shortTitle: Unit Of Work +category: Architectural +language: zn +tag: + - Data access + - Performance +--- + +## 又被稱為 +工作單元 + +## 目的 + +當一個業務事務完成時,所有的更新都作為一個大的工作單元一次性傳送,以最小化資料庫的往返次數進行持久化。 + +## 解釋 + +現實世界例子 + +> 武器商人擁有一個包含武器資訊的資料庫。 +> 全城的商販們都在不斷地更新這些資訊,這導致資料庫伺服器的負載很高。 +> 為了使負載更易於管理,我們應用了工作單元模式,將許多小的更新批次傳送。 + +用直白的話來說 + +> 工作單元將許多小的資料庫更新合併成一個批次。 +> 以最佳化往返次數。 + +[MartinFowler.com](https://martinfowler.com/eaaCatalog/unitOfWork.html) 中說 + +> 維護一個受業務事務影響的物件列表。 +> 並協調寫出更改和解決併發問題。 + +**程式設計樣例** + +以下是要持久化到資料庫中的 `Weapon` 的實體。 + +```java +@Getter +@RequiredArgsConstructor +public class Weapon { + private final Integer id; + private final String name; +} +``` + +實現的核心是 `ArmsDealer` 實現了工作單元模式。 +它維護了一個需要完成的資料庫操作對映 (`context`) 當呼叫 `commit` 時 +它會一次性批次應用這些操作。 + +```java +public interface IUnitOfWork { + + String INSERT = "INSERT"; + String DELETE = "DELETE"; + String MODIFY = "MODIFY"; + + void registerNew(T entity); + + void registerModified(T entity); + + void registerDeleted(T entity); + + void commit(); +} + +@Slf4j +@RequiredArgsConstructor +public class ArmsDealer implements IUnitOfWork { + + private final Map> context; + private final WeaponDatabase weaponDatabase; + + @Override + public void registerNew(Weapon weapon) { + LOGGER.info("Registering {} for insert in context.", weapon.getName()); + register(weapon, UnitActions.INSERT.getActionValue()); + } + + @Override + public void registerModified(Weapon weapon) { + LOGGER.info("Registering {} for modify in context.", weapon.getName()); + register(weapon, UnitActions.MODIFY.getActionValue()); + + } + + @Override + public void registerDeleted(Weapon weapon) { + LOGGER.info("Registering {} for delete in context.", weapon.getName()); + register(weapon, UnitActions.DELETE.getActionValue()); + } + + private void register(Weapon weapon, String operation) { + var weaponsToOperate = context.get(operation); + if (weaponsToOperate == null) { + weaponsToOperate = new ArrayList<>(); + } + weaponsToOperate.add(weapon); + context.put(operation, weaponsToOperate); + } + + /** + * All UnitOfWork operations are batched and executed together on commit only. + */ + @Override + public void commit() { + if (context == null || context.size() == 0) { + return; + } + LOGGER.info("Commit started"); + if (context.containsKey(UnitActions.INSERT.getActionValue())) { + commitInsert(); + } + + if (context.containsKey(UnitActions.MODIFY.getActionValue())) { + commitModify(); + } + if (context.containsKey(UnitActions.DELETE.getActionValue())) { + commitDelete(); + } + LOGGER.info("Commit finished."); + } + + private void commitInsert() { + var weaponsToBeInserted = context.get(UnitActions.INSERT.getActionValue()); + for (var weapon : weaponsToBeInserted) { + LOGGER.info("Inserting a new weapon {} to sales rack.", weapon.getName()); + weaponDatabase.insert(weapon); + } + } + + private void commitModify() { + var modifiedWeapons = context.get(UnitActions.MODIFY.getActionValue()); + for (var weapon : modifiedWeapons) { + LOGGER.info("Scheduling {} for modification work.", weapon.getName()); + weaponDatabase.modify(weapon); + } + } + + private void commitDelete() { + var deletedWeapons = context.get(UnitActions.DELETE.getActionValue()); + for (var weapon : deletedWeapons) { + LOGGER.info("Scrapping {}.", weapon.getName()); + weaponDatabase.delete(weapon); + } + } +} +``` + +以下描述了整個應用是如何組裝起來的。 + +```java +// create some weapons +var enchantedHammer = new Weapon(1, "enchanted hammer"); +var brokenGreatSword = new Weapon(2, "broken great sword"); +var silverTrident = new Weapon(3, "silver trident"); + +// create repository +var weaponRepository = new ArmsDealer(new HashMap>(), new WeaponDatabase()); + +// perform operations on the weapons +weaponRepository.registerNew(enchantedHammer); +weaponRepository.registerModified(silverTrident); +weaponRepository.registerDeleted(brokenGreatSword); +weaponRepository.commit(); +``` + +以下是控制檯輸出。 + +``` +21:39:21.984 [main] INFO com.iluwatar.unitofwork.ArmsDealer - Registering enchanted hammer for insert in context. +21:39:21.989 [main] INFO com.iluwatar.unitofwork.ArmsDealer - Registering silver trident for modify in context. +21:39:21.989 [main] INFO com.iluwatar.unitofwork.ArmsDealer - Registering broken great sword for delete in context. +21:39:21.989 [main] INFO com.iluwatar.unitofwork.ArmsDealer - Commit started +21:39:21.989 [main] INFO com.iluwatar.unitofwork.ArmsDealer - Inserting a new weapon enchanted hammer to sales rack. +21:39:21.989 [main] INFO com.iluwatar.unitofwork.ArmsDealer - Scheduling silver trident for modification work. +21:39:21.989 [main] INFO com.iluwatar.unitofwork.ArmsDealer - Scrapping broken great sword. +21:39:21.989 [main] INFO com.iluwatar.unitofwork.ArmsDealer - Commit finished. +``` + +## 類圖 + +![alt text](./etc/unit-of-work.urm.png "unit-of-work") + +## 應用 + +在以下情況時使用單元模式 + +* 為了最佳化資料庫事務所需的時間。 +* 作為工作單元將更改傳送到資料庫,確保事務的原子性。 +* 為了減少資料庫呼叫的次數。 + +## 教程 + +* [Repository and Unit of Work Pattern](https://www.programmingwithwolfgang.com/repository-and-unit-of-work-pattern/) +* [Unit of Work - a Design Pattern](https://mono.software/2017/01/13/unit-of-work-a-design-pattern/) + +## 鳴謝 + +* [Design Pattern - Unit Of Work Pattern](https://www.codeproject.com/Articles/581487/Unit-of-Work-Design-Pattern) +* [Unit Of Work](https://martinfowler.com/eaaCatalog/unitOfWork.html) +* [Patterns of Enterprise Application Architecture](https://www.amazon.com/gp/product/0321127420/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321127420&linkCode=as2&tag=javadesignpat-20&linkId=d9f7d37b032ca6e96253562d075fcc4a) diff --git a/localization/zh-TW/unit-of-work/etc/unit-of-work.urm.png b/localization/zh-TW/unit-of-work/etc/unit-of-work.urm.png new file mode 100644 index 000000000000..bb192af54477 Binary files /dev/null and b/localization/zh-TW/unit-of-work/etc/unit-of-work.urm.png differ diff --git a/localization/zh-TW/update-method/README.md b/localization/zh-TW/update-method/README.md new file mode 100644 index 000000000000..0a65ba446422 --- /dev/null +++ b/localization/zh-TW/update-method/README.md @@ -0,0 +1,37 @@ +--- +title: Update Method +shortTitle: Update Method +category: Behavioral +language: zn +tag: + - Game programming +--- + +## 又被稱為 +更新方法 + +## 目的 +更新方法模式透過告訴每個物件一次處理一個行為幀來模擬一組獨立的物件。 + +## 解釋 +遊戲世界維護了一個物件的集合。每個物件都實現了一個更新方法,用來模擬該物件行為的一幀。在每一幀中,遊戲會更新集合中的每一個物件。 + +要了解更多關於遊戲迴圈是如何執行的,以及何時呼叫更新方法,請參考“遊戲迴圈模式”。 + +## 類圖 +![alt text](./etc/update-method.urm.png "Update Method pattern class diagram") + +## 應用 +如果說遊戲迴圈模式是自切麵包以來最好的東西,那麼更新方法模式就是它的奶油。有很多玩家與動態實體互動的遊戲都以某種形式使用這個模式。如果遊戲裡有宇航兵、龍、火星人、幽靈或運動員,那麼它很可能使用了這種模式。 + +並且,如果遊戲更為抽象,且移動的部分不像是生動的角色,而更像是棋盤上的棋子,那麼這種模式往往並不合適。在像國際象棋這樣的遊戲中,你不需要同時模擬所有的棋子,也可能不需要告訴每一個兵卒在每一幀都更新自己。 + +當以下情況發生時,更新方法工作得很好: + +- 你的遊戲中有許多物件或系統需要同時執行。 +- 每個物件的行為大部分都獨立於其他物件。 +- 這些物件需要隨時間進行模擬。 + +## 鳴謝 + +* [Game Programming Patterns - Update Method](http://gameprogrammingpatterns.com/update-method.html) diff --git a/localization/zh-TW/update-method/etc/update-method.urm.png b/localization/zh-TW/update-method/etc/update-method.urm.png new file mode 100644 index 000000000000..ddc47b5fe145 Binary files /dev/null and b/localization/zh-TW/update-method/etc/update-method.urm.png differ diff --git a/localization/zh-TW/value-object/README.md b/localization/zh-TW/value-object/README.md new file mode 100644 index 000000000000..d238c113eac6 --- /dev/null +++ b/localization/zh-TW/value-object/README.md @@ -0,0 +1,97 @@ +--- +title: Value Object +shortTitle: Value Object +category: Creational +language: zn +tag: + - Instantiation +--- + +## 又被稱為 +值物件 + +## 目的 + +提供的物件應遵循值語義而不是引用語義。這意味著兩個值物件的相等性不是基於它們的身份。只要兩個值物件的值相同,即使它們不是同一個物件,它們也被認為是相等的。 + +## 解釋 + +現實世界例子 + +> 在一個角色扮演遊戲中,有一個用於英雄屬性統計的類。 +> 這些統計屬性包括力量、智慧和運氣等特徵。 +> 當所有的屬性都相同時,不同英雄的統計資料應被認為是相等的。 + +用直白的話來說 + +> 當值物件的屬性有相同的值時,它們是相等的。 + +維基百科中說 + +> 在電腦科學中,值物件是一個代表簡單實體的小物件。 +> 其相等性不是基於身份的:即當兩個值物件有相同的值的時候。 +> 它們是相等的,而不必要是同一個物件。 + +**程式設計樣例** + +這裡是作為值物件的 `HeroStat` 類。 請注意使用了 +[Lombok's `@Value`](https://projectlombok.org/features/Value) 註解。 + +```java +@Value(staticConstructor = "valueOf") +class HeroStat { + + int strength; + int intelligence; + int luck; +} +``` + +這個示例建立了三個不同的 `HeroStat`s 並比較了它們的相等性。 + +```java +var statA = HeroStat.valueOf(10, 5, 0); +var statB = HeroStat.valueOf(10, 5, 0); +var statC = HeroStat.valueOf(5, 1, 8); + +LOGGER.info(statA.toString()); +LOGGER.info(statB.toString()); +LOGGER.info(statC.toString()); + +LOGGER.info("Is statA and statB equal : {}", statA.equals(statB)); +LOGGER.info("Is statA and statC equal : {}", statA.equals(statC)); +``` + +以下是控制檯的輸出。 + +``` +20:11:12.199 [main] INFO com.iluwatar.value.object.App - HeroStat(strength=10, intelligence=5, luck=0) +20:11:12.202 [main] INFO com.iluwatar.value.object.App - HeroStat(strength=10, intelligence=5, luck=0) +20:11:12.202 [main] INFO com.iluwatar.value.object.App - HeroStat(strength=5, intelligence=1, luck=8) +20:11:12.202 [main] INFO com.iluwatar.value.object.App - Is statA and statB equal : true +20:11:12.203 [main] INFO com.iluwatar.value.object.App - Is statA and statC equal : false +``` + +## 類圖 + +![alt text](./etc/value-object.png "Value Object") + +## 應用 + +當滿足以下情況時,使用值物件: + +* 物件的相等性需要基於物件的值 + +## 現實世界的案例 + +* [java.util.Optional](https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html) +* [java.time.LocalDate](https://docs.oracle.com/javase/8/docs/api/java/time/LocalDate.html) +* [joda-time, money, beans](http://www.joda.org/) + +## 鳴謝 + +* [Patterns of Enterprise Application Architecture](http://www.martinfowler.com/books/eaa.html) +* [ValueObject](https://martinfowler.com/bliki/ValueObject.html) +* [VALJOs - Value Java Objects : Stephen Colebourne's blog](http://blog.joda.org/2014/03/valjos-value-java-objects.html) +* [Value Object : Wikipedia](https://en.wikipedia.org/wiki/Value_object) +* [J2EE Design Patterns](https://www.amazon.com/gp/product/0596004273/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596004273&linkCode=as2&tag=javadesignpat-20&linkId=f27d2644fbe5026ea448791a8ad09c94) diff --git a/localization/zh-TW/value-object/etc/value-object.png b/localization/zh-TW/value-object/etc/value-object.png new file mode 100644 index 000000000000..69a244c80691 Binary files /dev/null and b/localization/zh-TW/value-object/etc/value-object.png differ diff --git a/localization/zh-TW/version-number/README.md b/localization/zh-TW/version-number/README.md new file mode 100644 index 000000000000..5d40c75c1b81 --- /dev/null +++ b/localization/zh-TW/version-number/README.md @@ -0,0 +1,158 @@ +--- +title: Version Number +shortTitle: Version Number +category: Concurrency +language: zh +tag: + - Data access + - Microservices +--- + +## 名字 / 分類 + +版本號 + +## 或稱 + +實體版本控制,樂觀鎖。 + +## 目的 + +解決多個客戶端嘗試同時更新同一實體時的併發衝突。 + +## 解釋 + +現實世界的例子 + +> 愛麗絲(Alice)和鮑勃(Bob)正在管理書,該書儲存在資料庫中。 我們的英雄們正在同時進行更改,我們需要某種機制來防止他們相互覆蓋。 + +通俗地說 + +> 版本號模式可防止對同一實體進行併發更新。 + +維基百科說 + +> 樂觀併發控制假設多個事務可以頻繁完成而不會互相干擾。 在執行時,事務使用資料資源而不獲取這些資源的鎖。 在提交之前,每個事務都將驗證沒有其他事務修改了已讀取的資料。如果檢查發現有衝突的修改,則提交的事務將回滾並可以重新啟動。 + +**程式示例** + +我們有`Book` 已版本化的實體,它有一個複製建構函式。 + +```java +public class Book { + private long id; + private String title = ""; + private String author = ""; + + private long version = 0; // version number + + public Book(Book book) { + this.id = book.id; + this.title = book.title; + this.author = book.author; + this.version = book.version; + } + + // getters and setters are omitted here +} +``` + +我們還有一個 `BookRepository`, 它實現了併發控制。 + +```java +public class BookRepository { + private final Map collection = new HashMap<>(); + + public void update(Book book) throws BookNotFoundException, VersionMismatchException { + if (!collection.containsKey(book.getId())) { + throw new BookNotFoundException("Not found book with id: " + book.getId()); + } + + var latestBook = collection.get(book.getId()); + if (book.getVersion() != latestBook.getVersion()) { + throw new VersionMismatchException( + "Tried to update stale version " + book.getVersion() + + " while actual version is " + latestBook.getVersion() + ); + } + + // update version, including client representation - modify by reference here + book.setVersion(book.getVersion() + 1); + + // save book copy to repository + collection.put(book.getId(), new Book(book)); + } + + public Book get(long bookId) throws BookNotFoundException { + if (!collection.containsKey(bookId)) { + throw new BookNotFoundException("Not found book with id: " + bookId); + } + + // return copy of the book + return new Book(collection.get(bookId)); + } +} +``` + +這是實踐中的併發控制: + +```java +var bookId = 1; +// Alice and Bob took the book concurrently +final var aliceBook = bookRepository.get(bookId); +final var bobBook = bookRepository.get(bookId); + +aliceBook.setTitle("Kama Sutra"); // Alice has updated book title +bookRepository.update(aliceBook); // and successfully saved book in database +LOGGER.info("Alice updates the book with new version {}", aliceBook.getVersion()); + +// now Bob has the stale version of the book with empty title and version = 0 +// while actual book in database has filled title and version = 1 +bobBook.setAuthor("Vatsyayana Mallanaga"); // Bob updates the author +try { + LOGGER.info("Bob tries to update the book with his version {}", bobBook.getVersion()); + bookRepository.update(bobBook); // Bob tries to save his book to database +} catch (VersionMismatchException e) { + // Bob update fails, and book in repository remained untouchable + LOGGER.info("Exception: {}", e.getMessage()); + // Now Bob should reread actual book from repository, do his changes again and save again +} +``` + +程式輸出: + +```java +Alice updates the book with new version 1 +Bob tries to update the book with his version 0 +Exception: Tried to update stale version 0 while actual version is 1 +``` + +## 類圖 + +![alt text](./etc/version-number.urm.png "Version Number pattern class diagram") + +## 適用性 + +將版本號用於: + +* 解決對資料的併發寫訪問 +* 強的資料一致性 + +## 教程 +* [Version Number Pattern Tutorial](http://www.java2s.com/Tutorial/Java/0355__JPA/VersioningEntity.htm) + +## 已知用途 + * [Hibernate](https://vladmihalcea.com/jpa-entity-version-property-hibernate/) + * [Elasticsearch](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-index_.html#index-versioning) + * [Apache Solr](https://lucene.apache.org/solr/guide/6_6/updating-parts-of-documents.html) + +## 意義 +版本號模式允許實現併發控制,通常透過樂觀離線鎖模式來完成。 + +## 相關模式 +* [Optimistic Offline Lock](https://martinfowler.com/eaaCatalog/optimisticOfflineLock.html) + +## 鳴謝 +* [Optimistic Locking in JPA](https://www.baeldung.com/jpa-optimistic-locking) +* [JPA entity versioning](https://www.byteslounge.com/tutorials/jpa-entity-versioning-version-and-optimistic-locking) +* [J2EE Design Patterns](http://ommolketab.ir/aaf-lib/axkwht7wxrhvgs2aqkxse8hihyu9zv.pdf) diff --git a/localization/zh-TW/version-number/etc/version-number.urm.png b/localization/zh-TW/version-number/etc/version-number.urm.png new file mode 100644 index 000000000000..95a5819b4b6e Binary files /dev/null and b/localization/zh-TW/version-number/etc/version-number.urm.png differ diff --git a/localization/zh-TW/visitor/README.md b/localization/zh-TW/visitor/README.md new file mode 100644 index 000000000000..2e1ad097aae6 --- /dev/null +++ b/localization/zh-TW/visitor/README.md @@ -0,0 +1,223 @@ +--- +title: Visitor +shortTitle: Visitor +category: Behavioral +language: zh +tag: + - Gang of Four +--- + +## 目的 + +表示要在物件結構的元素上執行的操作。訪問者可讓你定義新操作,而無需更改其所操作元素的類。 + +## 解釋 + +真實世界例子 + +> 考慮有一個帶有軍隊單位的樹形結構。指揮官下有兩名中士,每名中士下有三名士兵。基於這個層級結構實現訪問者模式,我們可以輕鬆建立與指揮官,中士,士兵或所有人員互動的新物件 + +通俗的說 + +> 訪問者模式定義可以在資料結構的節點上執行的操作。 + +維基百科說 + +> 在物件導向的程式設計和軟體工程中,訪問者設計模式是一種將演算法與操作物件的結構分離的方法。這種分離的實際結果是能夠在不修改結構的情況下向現有物件結構新增新操作。 + +**程式示例** + +使用上面的軍隊單元的例子,我們首先由單位和單位訪問器型別。 + +```java +public abstract class Unit { + + private final Unit[] children; + + public Unit(Unit... children) { + this.children = children; + } + + public void accept(UnitVisitor visitor) { + Arrays.stream(children).forEach(child -> child.accept(visitor)); + } +} + +public interface UnitVisitor { + + void visitSoldier(Soldier soldier); + + void visitSergeant(Sergeant sergeant); + + void visitCommander(Commander commander); +} +``` + +然後我們有具體的單元。 + +```java +public class Commander extends Unit { + + public Commander(Unit... children) { + super(children); + } + + @Override + public void accept(UnitVisitor visitor) { + visitor.visitCommander(this); + super.accept(visitor); + } + + @Override + public String toString() { + return "commander"; + } +} + +public class Sergeant extends Unit { + + public Sergeant(Unit... children) { + super(children); + } + + @Override + public void accept(UnitVisitor visitor) { + visitor.visitSergeant(this); + super.accept(visitor); + } + + @Override + public String toString() { + return "sergeant"; + } +} + +public class Soldier extends Unit { + + public Soldier(Unit... children) { + super(children); + } + + @Override + public void accept(UnitVisitor visitor) { + visitor.visitSoldier(this); + super.accept(visitor); + } + + @Override + public String toString() { + return "soldier"; + } +} +``` + +然後有一些具體的訪問者。 + +```java +public class CommanderVisitor implements UnitVisitor { + + private static final Logger LOGGER = LoggerFactory.getLogger(CommanderVisitor.class); + + @Override + public void visitSoldier(Soldier soldier) { + // Do nothing + } + + @Override + public void visitSergeant(Sergeant sergeant) { + // Do nothing + } + + @Override + public void visitCommander(Commander commander) { + LOGGER.info("Good to see you {}", commander); + } +} + +public class SergeantVisitor implements UnitVisitor { + + private static final Logger LOGGER = LoggerFactory.getLogger(SergeantVisitor.class); + + @Override + public void visitSoldier(Soldier soldier) { + // Do nothing + } + + @Override + public void visitSergeant(Sergeant sergeant) { + LOGGER.info("Hello {}", sergeant); + } + + @Override + public void visitCommander(Commander commander) { + // Do nothing + } +} + +public class SoldierVisitor implements UnitVisitor { + + private static final Logger LOGGER = LoggerFactory.getLogger(SoldierVisitor.class); + + @Override + public void visitSoldier(Soldier soldier) { + LOGGER.info("Greetings {}", soldier); + } + + @Override + public void visitSergeant(Sergeant sergeant) { + // Do nothing + } + + @Override + public void visitCommander(Commander commander) { + // Do nothing + } +} +``` + +最後,來看看實踐中訪問者模式的力量。 + +```java +commander.accept(new SoldierVisitor()); +commander.accept(new SergeantVisitor()); +commander.accept(new CommanderVisitor()); +``` + +程式輸出: + +``` +Greetings soldier +Greetings soldier +Greetings soldier +Greetings soldier +Greetings soldier +Greetings soldier +Hello sergeant +Hello sergeant +Good to see you commander +``` + +## Class diagram + +![alt text](./etc/visitor_1.png "Visitor") + +## 適用性 + +使用訪問者模式當 + +* 物件結構包含許多具有不同介面的物件類,並且你希望根據這些物件的具體類對這些物件執行操作。 +* 需要對物件結構中的物件執行許多不同且不相關的操作,並且你想避免使用這些操作“汙染”它們的類。 訪問者可以透過在一個類中定義相關操作來將它們保持在一起。當許多應用程式共享物件結構時,請使用訪問者模式將操作僅放在需要它們的那些應用程式中 +* 定義物件結構的類很少變化,但是你經常想在結構上定義新的操作。更改物件結構類需要重新定義所有訪問者的介面,這可能會導致成本高昂。如果物件結構類經常更改,則最好在這些類中定義操作。 + +## 真例項子 + +* [Apache Wicket](https://github.com/apache/wicket) component tree, see [MarkupContainer](https://github.com/apache/wicket/blob/b60ec64d0b50a611a9549809c9ab216f0ffa3ae3/wicket-core/src/main/java/org/apache/wicket/MarkupContainer.java) +* [javax.lang.model.element.AnnotationValue](http://docs.oracle.com/javase/8/docs/api/javax/lang/model/element/AnnotationValue.html) and [AnnotationValueVisitor](http://docs.oracle.com/javase/8/docs/api/javax/lang/model/element/AnnotationValueVisitor.html) +* [javax.lang.model.element.Element](http://docs.oracle.com/javase/8/docs/api/javax/lang/model/element/Element.html) and [Element Visitor](http://docs.oracle.com/javase/8/docs/api/javax/lang/model/element/ElementVisitor.html) +* [java.nio.file.FileVisitor](http://docs.oracle.com/javase/8/docs/api/java/nio/file/FileVisitor.html) + +## 鳴謝 + +* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59) +* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b) +* [Refactoring to Patterns](https://www.amazon.com/gp/product/0321213351/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321213351&linkCode=as2&tag=javadesignpat-20&linkId=2a76fcb387234bc71b1c61150b3cc3a7) diff --git a/localization/zh-TW/visitor/etc/visitor_1.png b/localization/zh-TW/visitor/etc/visitor_1.png new file mode 100644 index 000000000000..de5285d7fdc3 Binary files /dev/null and b/localization/zh-TW/visitor/etc/visitor_1.png differ