* #1771 Move translations to a new directory to have more organization * #1771 spanish translation * #1771 change the language codes to follow ISO 639-1 and change the links * #1771 remove country flags
This commit is contained in:
committed by
GitHub
parent
5e434b783e
commit
784cdee819
39
localization/zh/README.md
Normal file
39
localization/zh/README.md
Normal file
@ -0,0 +1,39 @@
|
||||
<!-- the line below needs to be an empty line C: (its because kramdown isnt
|
||||
that smart and dearly wants an empty line before a heading to be able to
|
||||
display it as such, e.g. website) -->
|
||||
|
||||
# 设计模式Java版
|
||||
|
||||
 [](https://raw.githubusercontent.com/iluwatar/java-design-patterns/master/LICENSE.md) [](https://gitter.im/iluwatar/java-design-patterns?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [](https://sonarcloud.io/dashboard?id=iluwatar_java-design-patterns)
|
||||
|
||||
# 介绍
|
||||
|
||||
设计模式是程序员在设计应用程序或系统时可以用来解决常见问题的最佳形式化实践。
|
||||
|
||||
设计模式可以通过提供经过测试的,经过验证的开发范例来加快开发过程。
|
||||
|
||||
重用设计模式有助于防止引起重大问题的细微问题,并且还可以为熟悉模式的程序员和架构师们提高代码可读性。
|
||||
|
||||
# 入门
|
||||
|
||||
这个站点展示了Java设计模式。该解决方案是由开源社区中经验丰富的程序员和架构师开发的。可以通过他们高级描述或查看源代码来浏览这些设计模式。源代码示例得到了很好的注释,可以视为编程教程,以了解如何实现特定的模式。我们使用最流行的,久经考验的开源Java技术。
|
||||
|
||||
在深入学习该材料之前,您应该熟悉各种软件设计原则。
|
||||
|
||||
所有设计应尽可能简单。您应该从KISS,YAGNI开始,并做可能可行的最简单的事情。仅在实际可扩展性需要它们时才应引入复杂性和模式。
|
||||
|
||||
熟悉这些概念后,您可以通过以下任何一种方法开始深入研究模式
|
||||
|
||||
- 使用难度标签: `入门难度` , `中等难度`和`专家难度` 。
|
||||
- 使用模式分类`创建型` ,`行为型`和其他类别。
|
||||
- 搜索特定的模式。一个也找不到?请[在这里](https://github.com/iluwatar/java-design-patterns/issues)反馈新模式。
|
||||
|
||||
希望您在本站上找到的面向对象解决方案能够对您的体系结构很有用,并在学习它们的时候能够像我们开发它们一样有趣。
|
||||
|
||||
# 如何做出贡献
|
||||
|
||||
如果您愿意为该项目做出贡献,则可以在我们的[开发人员Wiki中](https://github.com/iluwatar/java-design-patterns/wiki)找到相关信息。我们将在[Gitter聊天室为](https://gitter.im/iluwatar/java-design-patterns)您提供帮助并回答您的问题。
|
||||
|
||||
# 许可证
|
||||
|
||||
该项目使用MIT许可证。
|
183
localization/zh/abstract-document/README.md
Normal file
183
localization/zh/abstract-document/README.md
Normal file
@ -0,0 +1,183 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Abstract Document
|
||||
folder: abstract-document
|
||||
permalink: /patterns/abstract-document/zh
|
||||
categories: Structural
|
||||
language: zh
|
||||
tags:
|
||||
- Extensibility
|
||||
---
|
||||
|
||||
## 目的
|
||||
|
||||
使用动态属性,并在保持类型安全的同时实现非类型化语言的灵活性。
|
||||
|
||||
## 解释
|
||||
|
||||
抽象文档模式使您能够处理其他非静态属性。 此模式使用特征的概念来实现类型安全,并将不同类的属性分离为一组接口。
|
||||
|
||||
真实世界例子
|
||||
|
||||
> 考虑由多个部分组成的汽车。 但是,我们不知道特定汽车是否真的拥有所有零件,或者仅仅是零件中的一部分。 我们的汽车是动态而且非常灵活的。
|
||||
|
||||
通俗的说
|
||||
|
||||
> 抽象文档模式允许在对象不知道的情况下将属性附加到对象。
|
||||
|
||||
维基百科说
|
||||
|
||||
> 面向对象的结构设计模式,用于组织松散类型的键值存储中的对象并使用类型化的视图公开数据。 该模式的目的是在强类型语言中实现组件之间的高度灵活性,在这种语言中,可以在不丢失类型安全支持的情况下,将新属性动态地添加到对象树中。 该模式利用特征将类的不同属性分成不同的接口。
|
||||
|
||||
**程序示例**
|
||||
|
||||
让我们首先定义基类`Document`和`AbstractDocument`。 它们基本上使对象拥有属性映射和任意数量的子对象。
|
||||
|
||||
```java
|
||||
public interface Document {
|
||||
|
||||
Void put(String key, Object value);
|
||||
|
||||
Object get(String key);
|
||||
|
||||
<T> Stream<T> children(String key, Function<Map<String, Object>, T> constructor);
|
||||
}
|
||||
|
||||
public abstract class AbstractDocument implements Document {
|
||||
|
||||
private final Map<String, Object> properties;
|
||||
|
||||
protected AbstractDocument(Map<String, Object> properties) {
|
||||
Objects.requireNonNull(properties, "properties map is required");
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void put(String key, Object value) {
|
||||
properties.put(key, value);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object get(String key) {
|
||||
return properties.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Stream<T> children(String key, Function<Map<String, Object>, T> constructor) {
|
||||
return Stream.ofNullable(get(key))
|
||||
.filter(Objects::nonNull)
|
||||
.map(el -> (List<Map<String, Object>>) el)
|
||||
.findAny()
|
||||
.stream()
|
||||
.flatMap(Collection::stream)
|
||||
.map(constructor);
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
接下来,我们定义一个枚举“属性”和一组类型,价格,模型和零件的接口。 这使我们能够为Car类创建静态外观的界面。
|
||||
|
||||
```java
|
||||
public enum Property {
|
||||
|
||||
PARTS, TYPE, PRICE, MODEL
|
||||
}
|
||||
|
||||
public interface HasType extends Document {
|
||||
|
||||
default Optional<String> getType() {
|
||||
return Optional.ofNullable((String) get(Property.TYPE.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
public interface HasPrice extends Document {
|
||||
|
||||
default Optional<Number> getPrice() {
|
||||
return Optional.ofNullable((Number) get(Property.PRICE.toString()));
|
||||
}
|
||||
}
|
||||
public interface HasModel extends Document {
|
||||
|
||||
default Optional<String> getModel() {
|
||||
return Optional.ofNullable((String) get(Property.MODEL.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
public interface HasParts extends Document {
|
||||
|
||||
default Stream<Part> getParts() {
|
||||
return children(Property.PARTS.toString(), Part::new);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
现在我们准备介绍`Car`。
|
||||
|
||||
```java
|
||||
public class Car extends AbstractDocument implements HasModel, HasPrice, HasParts {
|
||||
|
||||
public Car(Map<String, Object> properties) {
|
||||
super(properties);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
最后是完整示例中的`Car`构造和使用方式。
|
||||
|
||||
```java
|
||||
LOGGER.info("Constructing parts and car");
|
||||
|
||||
var wheelProperties = Map.of(
|
||||
Property.TYPE.toString(), "wheel",
|
||||
Property.MODEL.toString(), "15C",
|
||||
Property.PRICE.toString(), 100L);
|
||||
|
||||
var doorProperties = Map.of(
|
||||
Property.TYPE.toString(), "door",
|
||||
Property.MODEL.toString(), "Lambo",
|
||||
Property.PRICE.toString(), 300L);
|
||||
|
||||
var carProperties = Map.of(
|
||||
Property.MODEL.toString(), "300SL",
|
||||
Property.PRICE.toString(), 10000L,
|
||||
Property.PARTS.toString(), List.of(wheelProperties, doorProperties));
|
||||
|
||||
var car = new Car(carProperties);
|
||||
|
||||
LOGGER.info("Here is our car:");
|
||||
LOGGER.info("-> model: {}", car.getModel().orElseThrow());
|
||||
LOGGER.info("-> price: {}", car.getPrice().orElseThrow());
|
||||
LOGGER.info("-> parts: ");
|
||||
car.getParts().forEach(p -> LOGGER.info("\t{}/{}/{}",
|
||||
p.getType().orElse(null),
|
||||
p.getModel().orElse(null),
|
||||
p.getPrice().orElse(null))
|
||||
);
|
||||
|
||||
// Constructing parts and car
|
||||
// Here is our car:
|
||||
// model: 300SL
|
||||
// price: 10000
|
||||
// parts:
|
||||
// wheel/15C/100
|
||||
// door/Lambo/300
|
||||
```
|
||||
|
||||
## 类图
|
||||
|
||||

|
||||
|
||||
## 适用性
|
||||
|
||||
使用抽象文档模式当
|
||||
|
||||
* 需要即时添加新属性
|
||||
* 你想要一种灵活的方式来以树状结构组织域
|
||||
* 你想要更宽松的耦合系统
|
||||
|
||||
## 鸣谢
|
||||
|
||||
* [Wikipedia: Abstract Document Pattern](https://en.wikipedia.org/wiki/Abstract_Document_Pattern)
|
||||
* [Martin Fowler: Dealing with properties](http://martinfowler.com/apsupp/properties.pdf)
|
||||
* [Pattern-Oriented Software Architecture Volume 4: A Pattern Language for Distributed Computing (v. 4)](https://www.amazon.com/gp/product/0470059028/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=0470059028&linkId=e3aacaea7017258acf184f9f3283b492)
|
219
localization/zh/abstract-factory/README.md
Normal file
219
localization/zh/abstract-factory/README.md
Normal file
@ -0,0 +1,219 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Abstract Factory
|
||||
folder: abstract-factory
|
||||
permalink: /patterns/abstract-factory/zh
|
||||
categories: Creational
|
||||
language: zh
|
||||
tags:
|
||||
- Gang of Four
|
||||
---
|
||||
|
||||
## 或称
|
||||
|
||||
工具包
|
||||
|
||||
## 目的
|
||||
|
||||
提供一个用于创建相关对象家族的接口,而无需指定其具体类。
|
||||
|
||||
## 解释
|
||||
|
||||
真实世界例子
|
||||
|
||||
> 要创建一个王国,我们需要具有共同主题的对象。 精灵王国需要精灵王,精灵城堡和精灵军队,而兽人王国需要兽王,精灵城堡和兽人军队。 王国中的对象之间存在依赖性。
|
||||
|
||||
通俗的说
|
||||
|
||||
> 工厂的工厂; 一个将单个但相关/从属的工厂分组在一起而没有指定其具体类别的工厂。
|
||||
|
||||
维基百科上说
|
||||
|
||||
> 抽象工厂模式提供了一种封装一组具有共同主题的单个工厂而无需指定其具体类的方法
|
||||
|
||||
**程序示例**
|
||||
|
||||
翻译上面的王国示例。 首先,我们为王国中的对象提供了一些接口和实现。
|
||||
|
||||
```java
|
||||
public interface Castle {
|
||||
String getDescription();
|
||||
}
|
||||
|
||||
public interface King {
|
||||
String getDescription();
|
||||
}
|
||||
|
||||
public interface Army {
|
||||
String getDescription();
|
||||
}
|
||||
|
||||
// Elven implementations ->
|
||||
public class ElfCastle implements Castle {
|
||||
static final String DESCRIPTION = "This is the Elven castle!";
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return DESCRIPTION;
|
||||
}
|
||||
}
|
||||
public class ElfKing implements King {
|
||||
static final String DESCRIPTION = "This is the Elven king!";
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return DESCRIPTION;
|
||||
}
|
||||
}
|
||||
public class ElfArmy implements Army {
|
||||
static final String DESCRIPTION = "This is the Elven Army!";
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return DESCRIPTION;
|
||||
}
|
||||
}
|
||||
|
||||
// Orcish implementations similarly -> ...
|
||||
|
||||
```
|
||||
|
||||
然后我们有了王国工厂的抽象和实现
|
||||
|
||||
```java
|
||||
public interface KingdomFactory {
|
||||
Castle createCastle();
|
||||
King createKing();
|
||||
Army createArmy();
|
||||
}
|
||||
|
||||
public class ElfKingdomFactory implements KingdomFactory {
|
||||
public Castle createCastle() {
|
||||
return new ElfCastle();
|
||||
}
|
||||
public King createKing() {
|
||||
return new ElfKing();
|
||||
}
|
||||
public Army createArmy() {
|
||||
return new ElfArmy();
|
||||
}
|
||||
}
|
||||
|
||||
public class OrcKingdomFactory implements KingdomFactory {
|
||||
public Castle createCastle() {
|
||||
return new OrcCastle();
|
||||
}
|
||||
public King createKing() {
|
||||
return new OrcKing();
|
||||
}
|
||||
public Army createArmy() {
|
||||
return new OrcArmy();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
现在我们有了抽象工厂,使我们可以制作相关对象的系列,即精灵王国工厂创建了精灵城堡,国王和军队等。
|
||||
|
||||
```java
|
||||
var factory = new ElfKingdomFactory();
|
||||
var castle = factory.createCastle();
|
||||
var king = factory.createKing();
|
||||
var army = factory.createArmy();
|
||||
|
||||
castle.getDescription();
|
||||
king.getDescription();
|
||||
army.getDescription();
|
||||
```
|
||||
|
||||
程序输出:
|
||||
|
||||
```java
|
||||
This is the Elven castle!
|
||||
This is the Elven king!
|
||||
This is the Elven Army!
|
||||
```
|
||||
|
||||
现在,我们可以为不同的王国工厂设计工厂。 在此示例中,我们创建了FactoryMaker,负责返回ElfKingdomFactory或OrcKingdomFactory的实例。 客户可以使用FactoryMaker来创建所需的具体工厂,该工厂随后将生产不同的具体对象(军队,国王,城堡)。 在此示例中,我们还使用了一个枚举来参数化客户要求的王国工厂类型。
|
||||
|
||||
```java
|
||||
public static class FactoryMaker {
|
||||
|
||||
public enum KingdomType {
|
||||
ELF, ORC
|
||||
}
|
||||
|
||||
public static KingdomFactory makeFactory(KingdomType type) {
|
||||
switch (type) {
|
||||
case ELF:
|
||||
return new ElfKingdomFactory();
|
||||
case ORC:
|
||||
return new OrcKingdomFactory();
|
||||
default:
|
||||
throw new IllegalArgumentException("KingdomType not supported.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
var app = new App();
|
||||
|
||||
LOGGER.info("Elf Kingdom");
|
||||
app.createKingdom(FactoryMaker.makeFactory(KingdomType.ELF));
|
||||
LOGGER.info(app.getArmy().getDescription());
|
||||
LOGGER.info(app.getCastle().getDescription());
|
||||
LOGGER.info(app.getKing().getDescription());
|
||||
|
||||
LOGGER.info("Orc Kingdom");
|
||||
app.createKingdom(FactoryMaker.makeFactory(KingdomType.ORC));
|
||||
-- similar use of the orc factory
|
||||
}
|
||||
```
|
||||
|
||||
## 类图
|
||||
|
||||

|
||||
|
||||
|
||||
## 适用性
|
||||
|
||||
在以下情况下使用抽象工厂模式
|
||||
|
||||
* 该系统应独立于其产品的创建,组成和表示方式
|
||||
* 系统应配置有多个产品系列之一
|
||||
* 相关产品对象系列旨在一起使用,你需要强制执行此约束
|
||||
* 你想提供产品的类库,并且只想暴露它们的接口,而不是它们的实现。
|
||||
* 从概念上讲,依赖项的生存期比使用者的生存期短。
|
||||
* 你需要一个运行时值来构建特定的依赖关系
|
||||
* 你想决定在运行时从系列中调用哪种产品。
|
||||
* 你需要提供一个或更多仅在运行时才知道的参数,然后才能解决依赖关系。
|
||||
* 当你需要产品之间的一致性时
|
||||
* 在向程序添加新产品或产品系列时,您不想更改现有代码。
|
||||
|
||||
示例场景
|
||||
|
||||
* 在运行时在FileSystemAcmeService ,DatabaseAcmeService 或NetworkAcmeService中选择并调用一个
|
||||
* 单元测试用例的编写变得更加容易
|
||||
* 适用于不同操作系统的UI工具
|
||||
|
||||
## 后果:
|
||||
|
||||
* Java中的依赖注入会隐藏服务类的依赖关系,这些依赖关系可能导致运行时错误,而这些错误在编译时会被捕获。
|
||||
* 虽然在创建预定义对象时模式很好,但是添加新对象可能会很困难。
|
||||
* 由于引入了许多新的接口和类,因此代码变得比应有的复杂。
|
||||
|
||||
## 教程
|
||||
|
||||
* [Abstract Factory Pattern Tutorial](https://www.journaldev.com/1418/abstract-factory-design-pattern-in-java)
|
||||
|
||||
## 已知使用
|
||||
|
||||
* [javax.xml.parsers.DocumentBuilderFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/parsers/DocumentBuilderFactory.html)
|
||||
* [javax.xml.transform.TransformerFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/transform/TransformerFactory.html#newInstance--)
|
||||
* [javax.xml.xpath.XPathFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/xpath/XPathFactory.html#newInstance--)
|
||||
|
||||
## 相关模式
|
||||
|
||||
* [Factory Method](https://java-design-patterns.com/patterns/factory-method/)
|
||||
* [Factory Kit](https://java-design-patterns.com/patterns/factory-kit/)
|
||||
|
||||
## 鸣谢
|
||||
|
||||
* [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)
|
125
localization/zh/active-object/README.md
Normal file
125
localization/zh/active-object/README.md
Normal file
@ -0,0 +1,125 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Active Object
|
||||
folder: active-object
|
||||
permalink: /patterns/active-object/zh
|
||||
categories: Concurrency
|
||||
language: zh
|
||||
tags:
|
||||
- Performance
|
||||
---
|
||||
|
||||
|
||||
## 目的
|
||||
活动对象设计模式使每个驻留在其控制线程中的对象的方法执行与方法调用脱钩。 目的是通过使用异步方法调用和用于处理请求的调度程序来引入并发。
|
||||
|
||||
## 解释
|
||||
|
||||
实现活动对象模式的类将包含自同步机制,而无需使用“同步”方法。
|
||||
|
||||
真实世界例子
|
||||
|
||||
>兽人以其野性和顽强的灵魂而著称。 似乎他们有基于先前行为的控制线程。
|
||||
|
||||
要实现具有自己的控制机制线程并仅公开其API而不公开自己的执行,我们可以使用活动对象模式。
|
||||
|
||||
**程序示例**
|
||||
|
||||
```java
|
||||
public abstract class ActiveCreature{
|
||||
private final Logger logger = LoggerFactory.getLogger(ActiveCreature.class.getName());
|
||||
|
||||
private BlockingQueue<Runnable> requests;
|
||||
|
||||
private String name;
|
||||
|
||||
private Thread thread;
|
||||
|
||||
public ActiveCreature(String name) {
|
||||
this.name = name;
|
||||
this.requests = new LinkedBlockingQueue<Runnable>();
|
||||
thread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
try {
|
||||
requests.take().run();
|
||||
} catch (InterruptedException e) {
|
||||
logger.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
thread.start();
|
||||
}
|
||||
|
||||
public void eat() throws InterruptedException {
|
||||
requests.put(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
logger.info("{} is eating!",name());
|
||||
logger.info("{} has finished eating!",name());
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public void roam() throws InterruptedException {
|
||||
requests.put(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
logger.info("{} has started to roam and the wastelands.",name());
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return this.name;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
我们可以看到,任何将扩展ActiveCreature的类都将具有自己的控制线程来执行和调用方法。
|
||||
|
||||
例如,兽人类:
|
||||
|
||||
```java
|
||||
public class Orc extends ActiveCreature {
|
||||
|
||||
public Orc(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
现在,我们可以创建多个生物,例如兽人,告诉他们吃东西和散步,然后他们将在自己的控制线程上执行它:
|
||||
|
||||
```java
|
||||
public static void main(String[] args) {
|
||||
var app = new App();
|
||||
app.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
ActiveCreature creature;
|
||||
try {
|
||||
for (int i = 0;i < creatures;i++) {
|
||||
creature = new Orc(Orc.class.getSimpleName().toString() + i);
|
||||
creature.eat();
|
||||
creature.roam();
|
||||
}
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
logger.error(e.getMessage());
|
||||
}
|
||||
Runtime.getRuntime().exit(1);
|
||||
}
|
||||
```
|
||||
|
||||
## 类图
|
||||
|
||||

|
158
localization/zh/acyclic-visitor/README.md
Normal file
158
localization/zh/acyclic-visitor/README.md
Normal file
@ -0,0 +1,158 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Acyclic Visitor
|
||||
folder: acyclic-visitor
|
||||
permalink: /patterns/acyclic-visitor/zh
|
||||
categories: Behavioral
|
||||
language: zh
|
||||
tags:
|
||||
- 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.
|
||||
```
|
||||
|
||||
## 类图
|
||||
|
||||

|
||||
|
||||
## 适用性
|
||||
|
||||
以下情况可以使用此模式:
|
||||
|
||||
* 需要在现有层次结构中添加新功能而无需更改或影响该层次结构时。
|
||||
* 当某些功能在层次结构上运行,但不属于层次结构本身时。 例如 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)
|
138
localization/zh/adapter/README.md
Normal file
138
localization/zh/adapter/README.md
Normal file
@ -0,0 +1,138 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Adapter
|
||||
folder: adapter
|
||||
permalink: /patterns/adapter/zh
|
||||
categories: Structural
|
||||
language: zh
|
||||
tags:
|
||||
- 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();
|
||||
```
|
||||
|
||||
## 类图
|
||||

|
||||
|
||||
|
||||
## 应用
|
||||
使用适配器模式当
|
||||
|
||||
* 你想使用一个已有类,但是它的接口不能和你需要的所匹配
|
||||
* 你需要创建一个可重用类,该类与不相关或不可预见的类进行协作,即不一定具有兼容接口的类
|
||||
* 你需要使用一些现有的子类,但是子类化他们每一个的子类来进行接口的适配是不现实的。一个对象适配器可以适配他们父类的接口。
|
||||
* 大多数使用第三方类库的应用使用适配器作为一个在应用和第三方类库间的中间层来使应用和类库解耦。如果必须使用另一个库,则只需使用一个新库的适配器而无需改变应用程序的代码。
|
||||
|
||||
## 后果:
|
||||
类和对象适配器有不同的权衡取舍。一个类适配器
|
||||
|
||||
* 适配被适配者到目标接口,需要保证只有一个具体的被适配者类。作为结果,当我们想适配一个类和它所有的子类时,类适配器将不会起作用。
|
||||
* 可以让适配器重写一些被适配者的行为,因为适配器是被适配者的子类。
|
||||
* 只引入了一个对象,并且不需要其他指针间接访问被适配者。
|
||||
|
||||
对象适配器
|
||||
|
||||
* 一个适配器可以和许多被适配者工作,也就是被适配者自己和所有它的子类。适配器同时可以为所有被适配者添加功能。
|
||||
* 覆盖被适配者的行为变得更难。需要子类化被适配者然后让适配器引用这个子类不是被适配者。
|
||||
|
||||
|
||||
## 现实世界的案例
|
||||
|
||||
* [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)
|
||||
|
||||
```
|
||||
|
||||
```
|
108
localization/zh/aggregator-microservices/README.md
Normal file
108
localization/zh/aggregator-microservices/README.md
Normal file
@ -0,0 +1,108 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Aggregator Microservices
|
||||
folder: aggregator-microservices
|
||||
permalink: /patterns/aggregator-microservices/zh
|
||||
categories: Architectural
|
||||
language: zh
|
||||
tags:
|
||||
- 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}
|
||||
```
|
||||
|
||||
## 类图
|
||||
|
||||

|
||||
|
||||
## 适用性
|
||||
|
||||
当需要各种微服务的统一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)
|
197
localization/zh/ambassador/README.md
Normal file
197
localization/zh/ambassador/README.md
Normal file
@ -0,0 +1,197 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Ambassador
|
||||
folder: ambassador
|
||||
permalink: /patterns/ambassador/zh
|
||||
categories: Structural
|
||||
language: zh
|
||||
tags:
|
||||
- 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
|
||||
```
|
||||
|
||||
## 类图
|
||||
|
||||

|
||||
|
||||
## 适用性
|
||||
|
||||
大使适用于无法修改或极难修改的旧式远程服务。 可以在客户端上实现连接性的功能,而无需更改远程服务。
|
||||
|
||||
* 大使提供了用于远程服务的本地接口。
|
||||
* 大使在客户端上提供日志记录,断路,重试和安全性。
|
||||
|
||||
## 典型用例
|
||||
|
||||
* 控制对另一个对象的访问
|
||||
* 实现日志
|
||||
* 卸载远程服务任务
|
||||
* 简化网络连接
|
||||
|
||||
## 已知使用
|
||||
|
||||
* [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)
|
139
localization/zh/api-gateway/README.md
Normal file
139
localization/zh/api-gateway/README.md
Normal file
@ -0,0 +1,139 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: API Gateway
|
||||
folder: api-gateway
|
||||
permalink: /patterns/api-gateway/zh
|
||||
categories: Architectural
|
||||
language: zh
|
||||
tags:
|
||||
- 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;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 类图
|
||||

|
||||
|
||||
## 适用性
|
||||
|
||||
在以下情况下使用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)
|
144
localization/zh/arrange-act-assert/README.md
Normal file
144
localization/zh/arrange-act-assert/README.md
Normal file
@ -0,0 +1,144 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Arrange/Act/Assert
|
||||
folder: arrange-act-assert
|
||||
permalink: /patterns/arrange-act-assert/zh
|
||||
categories: Idiom
|
||||
language: zh
|
||||
tags:
|
||||
- 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;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Then we write our unit tests according to Arrange/Act/Assert pattern. Notice the clearly
|
||||
separated steps for each unit test.
|
||||
|
||||
然后我们根据Arrange / Act / Assert模式编写单元测试。 注意每个单元测试的步骤是分开的清晰的。
|
||||
|
||||
```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 模式当
|
||||
|
||||
* You need to structure your unit tests so that they're easier to read, maintain, and enhance.
|
||||
* 你需要结构化你的单元测试代码这样它们可以更好的阅读,维护和增强。
|
||||
|
||||
## 鸣谢
|
||||
|
||||
* [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)
|
161
localization/zh/async-method-invocation/README.md
Normal file
161
localization/zh/async-method-invocation/README.md
Normal file
@ -0,0 +1,161 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Async Method Invocation
|
||||
folder: async-method-invocation
|
||||
permalink: /patterns/async-method-invocation/
|
||||
categories: Concurrency
|
||||
tags:
|
||||
- Reactive
|
||||
---
|
||||
|
||||
## 含义
|
||||
|
||||
异步方法是一种调用线程在等待任务结果时候不会被阻塞的模式。该模式提供了对多个任务的并行处理,并通过回调或等待,在所有任务完成后在提供结果读取。
|
||||
|
||||
## 解释
|
||||
|
||||
真实世界案例
|
||||
|
||||
> 发射太空火箭是一项令人兴奋的事业。在任务指挥部下达发射命令后, 经过一些未确定的时间,火箭要么成功发射,要么重演挑战者悲剧。
|
||||
|
||||
简而言之
|
||||
|
||||
> 异步方法调用开始任务处理并,在任务结果准备好之前立即返回。任务处理的结果会在稍后再返回给调用者。
|
||||
|
||||
维基百科的解释
|
||||
|
||||
> 在多线程计算机编程中,异步方法调用(AMI),也被称为异步方法调用或异步模式。这是一种设计模式,在这种模式下,调用点在等待被调用代码完成时不会被阻塞。相反,当返回点到达时,调用线程会得到通知。轮询结果是一种不受欢迎的选择。
|
||||
|
||||
**编程示例**
|
||||
|
||||
在这个例子中,我们正在发射太空火箭和部署月球车。
|
||||
|
||||
该应用演示了异步方法调用模式。该模式的关键部分是 `AsyncResult`,它是一个异步计算值的中间容器,`AsyncCallback` 可以在任务完成时提供执行行动作,`AsyncExecutor` 负责管理异步任务的执行。
|
||||
|
||||
```java
|
||||
public interface AsyncResult<T> {
|
||||
boolean isCompleted();
|
||||
T getValue() throws ExecutionException;
|
||||
void await() throws InterruptedException;
|
||||
}
|
||||
```
|
||||
|
||||
```java
|
||||
public interface AsyncCallback<T> {
|
||||
void onComplete(T value, Optional<Exception> ex);
|
||||
}
|
||||
```
|
||||
|
||||
```java
|
||||
public interface AsyncExecutor {
|
||||
<T> AsyncResult<T> startProcess(Callable<T> task);
|
||||
<T> AsyncResult<T> startProcess(Callable<T> task, AsyncCallback<T> callback);
|
||||
<T> T endProcess(AsyncResult<T> asyncResult) throws ExecutionException, InterruptedException;
|
||||
}
|
||||
```
|
||||
|
||||
`ThreadAsyncExecutor` 是 `AsyncExecutor` 的一个实现。接下来将着重说明它的一些关键部分。
|
||||
|
||||
```java
|
||||
public class ThreadAsyncExecutor implements AsyncExecutor {
|
||||
|
||||
@Override
|
||||
public <T> AsyncResult<T> startProcess(Callable<T> task) {
|
||||
return startProcess(task, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> AsyncResult<T> startProcess(Callable<T> task, AsyncCallback<T> 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> T endProcess(AsyncResult<T> asyncResult)
|
||||
throws ExecutionException, InterruptedException {
|
||||
if (!asyncResult.isCompleted()) {
|
||||
asyncResult.await();
|
||||
}
|
||||
return asyncResult.getValue();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
然后我们准备发射一些火箭,看看所有东西是如何一起运作的。
|
||||
|
||||
```java
|
||||
public static void main(String[] args) throws Exception {
|
||||
// construct a new executor that will run async tasks
|
||||
var executor = new ThreadAsyncExecutor();
|
||||
|
||||
// start few async tasks with varying processing times, two last with callback handlers
|
||||
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"));
|
||||
|
||||
// emulate processing in the current thread while async tasks are running in their own threads
|
||||
Thread.sleep(350); // Oh boy, we are working hard here
|
||||
log("Mission command is sipping coffee");
|
||||
|
||||
// wait for completion of the tasks
|
||||
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 <test> 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 <callback> launched successfully
|
||||
21:47:08.519 [executor-5] INFO com.iluwatar.async.method.invocation.App - Deploying lunar rover <callback>
|
||||
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 <test> launch complete
|
||||
21:47:08.618 [main] INFO com.iluwatar.async.method.invocation.App - Space rocket <50> launch complete
|
||||
```
|
||||
|
||||
## 类图
|
||||
|
||||

|
||||
|
||||
## 适用场景
|
||||
|
||||
在以下场景可以使用异步调用模式
|
||||
|
||||
* 你有多有可以并行执行的独立任务
|
||||
* 你需要提高一组串行任务的性能
|
||||
* 你的处理能力有限、或者有长期运行的任务,调用者不应该等待任务所有任务运行结束
|
||||
|
||||
## 现实示例
|
||||
|
||||
* [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)
|
||||
|
129
localization/zh/balking/README.md
Normal file
129
localization/zh/balking/README.md
Normal file
@ -0,0 +1,129 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Balking
|
||||
folder: balking
|
||||
permalink: /patterns/balking/
|
||||
categories: Concurrency
|
||||
tags:
|
||||
- Decoupling
|
||||
---
|
||||
|
||||
## 含义
|
||||
|
||||
阻止模式用于防止一个对象在不完整或不适当的状态下执行某段代码。
|
||||
|
||||
## 解释
|
||||
|
||||
真实世界的案例
|
||||
|
||||
> 洗衣机里有一个用于启动衣物洗涤的启动按钮。当洗衣机没有启动时,该按钮可以正常按下生效,但如果洗衣机已经在洗衣服了,再按下按钮就不生效了。
|
||||
|
||||
简而言之
|
||||
|
||||
> 使用阻止模式,只有当对象处于特定状态时,才会执行某段代码。
|
||||
|
||||
维基百科的解释
|
||||
|
||||
> 阻止模式是一种软件设计模式,它只在对象处于特定状态时对其执行动作。例如,如果一个对象读取 ZIP 文件,当 ZIP 文件没有打开时,如果一个方法在该对象上调用一个获取方法,该对象就会对阻止这个请求。
|
||||
|
||||
**编程示例**
|
||||
|
||||
在这个例子的实现中,`WashingMachine` 对象存在 2 种状态: `ENABLED` 和 `WASHING`。如果该对象处于 `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.
|
||||
```
|
||||
|
||||
## 类图
|
||||
|
||||

|
||||
|
||||
## 适用场景
|
||||
|
||||
在以下情况下可以使用阻止模式:
|
||||
|
||||
* 你想要在某个对象上调用一个动作,只有当该对象处于特定状态时才允许该调用。
|
||||
* 对象一般只处于容易暂时阻止的状态,只不过该时间是未知的。
|
||||
|
||||
## 教学
|
||||
|
||||
* [Guarded Suspension Pattern](https://java-design-patterns.com/patterns/guarded-suspension/)
|
||||
* [Double Checked Locking Pattern](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)
|
206
localization/zh/bridge/README.md
Normal file
206
localization/zh/bridge/README.md
Normal file
@ -0,0 +1,206 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Bridge
|
||||
folder: bridge
|
||||
permalink: /patterns/bridge/zh
|
||||
categories: Structural
|
||||
language: zh
|
||||
tags:
|
||||
- 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.
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 类图
|
||||
|
||||

|
||||
|
||||
## 适用性
|
||||
|
||||
使用桥接模式当
|
||||
|
||||
* 你想永久性的避免抽象和他的实现之间的绑定。有可能是这种情况,当实现需要被选择或者在运行时切换。
|
||||
* 抽象和他们的实现应该能通过写子类来扩展。这种情况下,桥接模式让你可以组合不同的抽象和实现并独立的扩展他们。
|
||||
* 对抽象的实现的改动应当不会对客户产生影响;也就是说,他们的代码不必重新编译。
|
||||
* 你有种类繁多的类。这样的类层次结构表明需要将一个对象分为两部分。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)
|
140
localization/zh/builder/README.md
Normal file
140
localization/zh/builder/README.md
Normal file
@ -0,0 +1,140 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Builder
|
||||
folder: builder
|
||||
permalink: /patterns/builder/zh
|
||||
categories: Creational
|
||||
language: zh
|
||||
tags:
|
||||
- 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();
|
||||
```
|
||||
|
||||
## 类图
|
||||
|
||||

|
||||
|
||||
## 适用性
|
||||
|
||||
使用建造者模式当
|
||||
|
||||
* 创建复杂对象的算法应独立于组成对象的零件及其组装方式
|
||||
* 构造过程必须允许所构造的对象具有不同的表示形式
|
||||
|
||||
## 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)
|
160
localization/zh/business-delegate/README.md
Normal file
160
localization/zh/business-delegate/README.md
Normal file
@ -0,0 +1,160 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Business Delegate
|
||||
folder: business-delegate
|
||||
permalink: /patterns/business-delegate/
|
||||
categories: Structural
|
||||
tags:
|
||||
- Decoupling
|
||||
---
|
||||
|
||||
## 含义
|
||||
|
||||
业务委托模式(译者:国内也有翻译成业务代表模式)在表现层和业务层之间增加了一个抽象层。通过使用该模式,我们获得了各层之间的松散耦合,并封装了关于如何定位、连接和与构成应用程序的业务对象进行交互的知识。
|
||||
|
||||
## 解释
|
||||
|
||||
真实世界的案例
|
||||
|
||||
> 一个手机应用程序承诺将现有的任何电影传输到你的手机上。它捕获了用户的搜索关键字内容,并将其传递给业务委托层。业务委托层选择最合适的视频流服务,并从该服务进行视频播放。
|
||||
|
||||
简而言之
|
||||
|
||||
> 业务委托模式在表现层和业务层之间增加了一个抽象层。
|
||||
|
||||
维基百科的解释
|
||||
|
||||
> Business delegate is a Java EE design pattern. This pattern is directing to reduce the coupling
|
||||
> in between business services and the connected presentation tier, and to hide the implementation
|
||||
> details of services (including lookup and accessibility of EJB architecture). Business delegates
|
||||
> acts as an adaptor to invoke business objects from the presentation tier.
|
||||
>
|
||||
> 业务委托模式是一种 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
|
||||
```
|
||||
|
||||
## 类图
|
||||
|
||||

|
||||
|
||||
## 相关模式
|
||||
|
||||
* [Service locator pattern](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)
|
28
localization/zh/caching/README.md
Normal file
28
localization/zh/caching/README.md
Normal file
@ -0,0 +1,28 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Caching
|
||||
folder: caching
|
||||
permalink: /patterns/caching/zh
|
||||
categories: Behavioral
|
||||
language: zh
|
||||
tags:
|
||||
- Performance
|
||||
- Cloud distributed
|
||||
---
|
||||
|
||||
## 目的
|
||||
为了避免昂贵的资源重新获取,方法是在资源使用后不立即释放资源。资源保留其身份,保留在某些快速访问的存储中,并被重新使用,以避免再次获取它们。
|
||||
|
||||
## 类图
|
||||

|
||||
|
||||
## 适用性
|
||||
在以下情况下使用缓存模式
|
||||
|
||||
* 重复获取,初始化和释放同一资源会导致不必要的性能开销。
|
||||
|
||||
## 鸣谢
|
||||
|
||||
* [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)
|
80
localization/zh/callback/README.md
Normal file
80
localization/zh/callback/README.md
Normal file
@ -0,0 +1,80 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Callback
|
||||
folder: callback
|
||||
permalink: /patterns/callback/zh
|
||||
categories: Idiom
|
||||
language: zh
|
||||
tags:
|
||||
- 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."));
|
||||
```
|
||||
## 类图
|
||||

|
||||
|
||||
## 适用性
|
||||
使用回调模式当
|
||||
* 当一些同步或异步架构动作必须在一些定义好的活动执行后执行时。
|
||||
|
||||
## Java例子
|
||||
|
||||
* [CyclicBarrier](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CyclicBarrier.html#CyclicBarrier%28int,%20java.lang.Runnable%29) 构造函数可以接受回调,该回调将在每次障碍被触发时触发。
|
160
localization/zh/chain/README.md
Normal file
160
localization/zh/chain/README.md
Normal file
@ -0,0 +1,160 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Chain of responsibility
|
||||
folder: chain
|
||||
permalink: /patterns/chain/zh
|
||||
categories: Behavioral
|
||||
language: zh
|
||||
tags:
|
||||
- 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"
|
||||
```
|
||||
|
||||
## 类图
|
||||

|
||||
|
||||
## 适用性
|
||||
使用责任链模式当
|
||||
|
||||
* 多于一个对象可能要处理请求,并且处理器并不知道一个优先级。处理器应自动确定。
|
||||
* 你想对多个对象之一发出请求而无需明确指定接收者
|
||||
* 处理请求的对象集合应该被动态指定时
|
||||
|
||||
## 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)
|
312
localization/zh/circuit-breaker/README.md
Normal file
312
localization/zh/circuit-breaker/README.md
Normal file
@ -0,0 +1,312 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Circuit Breaker
|
||||
folder: circuit-breaker
|
||||
permalink: /patterns/circuit-breaker/
|
||||
categories: Behavioral
|
||||
tags:
|
||||
- Performance
|
||||
- Decoupling
|
||||
- Cloud distributed
|
||||
---
|
||||
|
||||
## 含义
|
||||
|
||||
以这样的方式(译者:指断路器方式)处理昂贵的远程服务调用,可以防止单个服务/组件的故障导致整个应用程序崩溃,同时我们可以尽快地进行服务重连。
|
||||
|
||||
## 解释
|
||||
|
||||
现实世界案例
|
||||
|
||||
> 设想一下,一个网络应用程序既有本地文件/图像,又有用于获取数据的远程服务。这些远程服务可能在某些时候是健康的、有反应的,也可能在某些时候由于各种原因而变得缓慢和无反应。因此,如果其中一个远程服务速度慢或不能成功响应,我们的应用程序将尝试使用多个线程/进程从远程服务中获取响应,很快所有的线程/进程都会挂起(也称为线程饥饿 thread starvation),从而导致我们整个 Web 应用程序崩溃。我们应该能够检测到这种情况,并向用户显示一个适当的信息,以便用户可以探索应用程序的其他部分,而不受远程服务故障的影响。同时,其他正常工作的服务应该保持运作,不受这次故障的影响。
|
||||
|
||||
简而言之
|
||||
|
||||
> 断路器允许优雅地处理失败的远程服务。当我们的应用程序的所有部分都高度解耦时,这种方式的效果会很好,一个组件的失败并不会导致其他部分停止工作。
|
||||
|
||||
维基百科的解释
|
||||
|
||||
> 断路器是现代软件开发中使用的一种设计模式。它用于检测故障,并封装了防止故障不断复发的逻辑,在维护期间,临时地处理外部系统故障或意外的系统问题。
|
||||
|
||||
## Programmatic Example
|
||||
|
||||
那么,这一切是如何实现的呢?考虑到上面的例子,我们将在一个简单的例子中模拟这个功能。一个监控服务(译者:下图的 Monitoring Service)模拟了网络应用,进行本地和远程调用。
|
||||
|
||||
该服务架构如下:
|
||||
|
||||

|
||||
|
||||
终端用户(译者:上图的 End User)应用的代码如下:
|
||||
|
||||
```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);
|
||||
|
||||
//Create an object of monitoring service which makes both local and remote calls
|
||||
var monitoringService = new MonitoringService(delayedServiceCircuitBreaker,
|
||||
quickServiceCircuitBreaker);
|
||||
|
||||
//Fetch response from local resource
|
||||
LOGGER.info(monitoringService.localResourceResponse());
|
||||
|
||||
//Fetch response from delayed service 2 times, to meet the failure threshold
|
||||
LOGGER.info(monitoringService.delayedServiceResponse());
|
||||
LOGGER.info(monitoringService.delayedServiceResponse());
|
||||
|
||||
//Fetch current state of delayed service circuit breaker after crossing failure threshold limit
|
||||
//which is OPEN now
|
||||
LOGGER.info(delayedServiceCircuitBreaker.getState());
|
||||
|
||||
//Meanwhile, the delayed service is down, fetch response from the healthy quick service
|
||||
LOGGER.info(monitoringService.quickServiceResponse());
|
||||
LOGGER.info(quickServiceCircuitBreaker.getState());
|
||||
|
||||
//Wait for the delayed service to become responsive
|
||||
try {
|
||||
LOGGER.info("Waiting for delayed service to become responsive");
|
||||
Thread.sleep(5000);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
//Check the state of delayed circuit breaker, should be HALF_OPEN
|
||||
LOGGER.info(delayedServiceCircuitBreaker.getState());
|
||||
|
||||
//Fetch response from delayed service, which should be healthy by now
|
||||
LOGGER.info(monitoringService.delayedServiceResponse());
|
||||
//As successful response is fetched, it should be CLOSED again.
|
||||
LOGGER.info(delayedServiceCircuitBreaker.getState());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
监控服务代码(译者:上图的 monitoring service):
|
||||
|
||||
```java
|
||||
public class MonitoringService {
|
||||
|
||||
private final CircuitBreaker delayedService;
|
||||
|
||||
private final CircuitBreaker quickService;
|
||||
|
||||
public MonitoringService(CircuitBreaker delayedService, CircuitBreaker quickService) {
|
||||
this.delayedService = delayedService;
|
||||
this.quickService = quickService;
|
||||
}
|
||||
|
||||
//Assumption: Local service won't fail, no need to wrap it in a circuit breaker logic
|
||||
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;
|
||||
// We start in a closed state hoping that everything is fine
|
||||
this.state = State.CLOSED;
|
||||
this.failureThreshold = failureThreshold;
|
||||
// Timeout for the API request.
|
||||
// Used to break the calls made to remote resource if it exceeds the limit
|
||||
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;
|
||||
}
|
||||
|
||||
// Reset everything to defaults
|
||||
@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;
|
||||
}
|
||||
|
||||
// Evaluate the current state based on failureThreshold, failureCount and lastFailureTime.
|
||||
protected void evaluateState() {
|
||||
if (failureCount >= failureThreshold) { //Then something is wrong with remote service
|
||||
if ((System.nanoTime() - lastFailureTime) > retryTimePeriod) {
|
||||
//We have waited long enough and should try checking if service is up
|
||||
state = State.HALF_OPEN;
|
||||
} else {
|
||||
//Service would still probably be down
|
||||
state = State.OPEN;
|
||||
}
|
||||
} else {
|
||||
//Everything is working fine
|
||||
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();
|
||||
break;
|
||||
case HALF_OPEN:
|
||||
this.failureCount = failureThreshold;
|
||||
this.lastFailureTime = System.nanoTime() - retryTimePeriod;
|
||||
break;
|
||||
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 cached response if the circuit is in OPEN state
|
||||
return this.lastFailureResponse;
|
||||
} else {
|
||||
// Make the API request if the circuit is not OPEN
|
||||
try {
|
||||
//In a real application, this would be run in a thread and the timeout
|
||||
//parameter of the circuit breaker would be utilized to know if service
|
||||
//is working. Here, we simulate that based on server response itself
|
||||
var response = service.call();
|
||||
// Yay!! the API responded fine. Let's reset everything.
|
||||
recordSuccess();
|
||||
return response;
|
||||
} catch (RemoteServiceException ex) {
|
||||
recordFailure(ex.getMessage());
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
上述模式是如何防止失败的呢?让我们通过它所实现的这个有限状态机来了解。
|
||||
|
||||

|
||||
|
||||
- 我们用 `timeout`(超时)、 `failureThreshold` (失败阈值)、`retryTimePeriod`(重试时间周期) 参数初始化断路器对象 ,用于确定 API 的适应性。
|
||||
- 最初,断路器处于 `closed` 关闭状态,没有发生对 API 的远程调用。
|
||||
- 每次调用成功,我们就把状态重置为开始时的样子。
|
||||
- 如果失败的次数超过了一定的阈值(`failureThreshold`),断路器就会进入 `open` 开启状态,它的作用就像一个开启的电路,阻止远程服务的调用,从而节省资源。
|
||||
- 一旦我们超过重试时间周期(`retryTimePeriod`),断路器就会转到 `half-open` 半启用状态,并再次调用远程服务,检查服务是否正常,以便我们可以提供最新的响应内容。如果远程服务调用失败会使断路器回到 `open` 状态,并在重试超时后进行另一次尝试;如果远程服务调用成功则使断路器进入 `closed` 状态,这样一切又开始正常工作。
|
||||
|
||||
## 类图
|
||||
|
||||

|
||||
|
||||
## 适用场景
|
||||
|
||||
在以下场景下,可以使用断路器模式:
|
||||
|
||||
- 构建一个高可用的应用程序,某些些服务的失败不会导致整个应用程序的崩溃。
|
||||
- 构建一个持续运行(长期在线)的应用程序,以便其组件可以在不完全关闭的情况下进行升级。
|
||||
|
||||
## 相关模式
|
||||
|
||||
- [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)
|
29
localization/zh/collection-pipeline/README.md
Normal file
29
localization/zh/collection-pipeline/README.md
Normal file
@ -0,0 +1,29 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Collection Pipeline
|
||||
folder: collection-pipeline
|
||||
permalink: /patterns/collection-pipeline/
|
||||
categories: Functional
|
||||
tags:
|
||||
- Reactive
|
||||
---
|
||||
|
||||
## 释义
|
||||
**集合管道(Collection Pipeline)**包含**函数组合(Function Composition)**和**集合管道(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)
|
254
localization/zh/command/README.md
Normal file
254
localization/zh/command/README.md
Normal file
@ -0,0 +1,254 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Command
|
||||
folder: command
|
||||
permalink: /patterns/command/zh
|
||||
categories: Behavioral
|
||||
language: zh
|
||||
tags:
|
||||
- Gang of Four
|
||||
---
|
||||
|
||||
## 或称
|
||||
行动, 事务模式
|
||||
|
||||
## 目的
|
||||
将请求封装为对象,从而使你可以将具有不同请求的客户端参数化,队列或记录请求,并且支持可撤销操作。
|
||||
|
||||
## 解释
|
||||
真实世界例子
|
||||
|
||||
> 有一个巫师在地精上施放咒语。咒语在地精上一一执行。第一个咒语使地精缩小,第二个使他不可见。然后巫师将咒语一个个的反转。这里的每一个咒语都是一个可撤销的命令对象。
|
||||
|
||||
用通俗的话说
|
||||
|
||||
> 用命令对象的方式存储请求以在将来时可以执行它或撤销它。
|
||||
|
||||
维基百科说
|
||||
|
||||
> 在面向对象编程中,命令模式是一种行为型设计模式,它把在稍后执行的一个动作或触发的一个事件所需要的所有信息封装到一个对象中。
|
||||
|
||||
**编程示例**
|
||||
|
||||
这是巫师和地精的示例代码。让我们从巫师类开始。
|
||||
|
||||
```java
|
||||
public class Wizard {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(Wizard.class);
|
||||
|
||||
private final Deque<Command> undoStack = new LinkedList<>();
|
||||
private final Deque<Command> 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]
|
||||
```
|
||||
|
||||
## 类图
|
||||

|
||||
|
||||
## 适用性
|
||||
使用命令模式当你想
|
||||
|
||||
* 通过操作将对象参数化。您可以使用回调函数(即,已在某处注册以便稍后调用的函数)以过程语言表示这种参数化。命令是回调的一种面向对象替代方案。
|
||||
* 在不同的时间指定,排队和执行请求。一个命令对象的生存期可以独立于原始请求。如果请求的接收方可以以地址空间无关的方式来表示,那么你可以将请求的命令对象传输到其他进程并在那里执行请求。
|
||||
* 支持撤销。命令的执行操作可以在命令本身中存储状态以反转其效果。命令接口必须有添加的反执行操作,该操作可以逆转上一次执行调用的效果。执行的命令存储在历史列表中。无限撤消和重做通过分别向后和向前遍历此列表来实现,分别调用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)
|
121
localization/zh/composite-entity/README.md
Normal file
121
localization/zh/composite-entity/README.md
Normal file
@ -0,0 +1,121 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Composite Entity
|
||||
folder: composite-entity
|
||||
permalink: /patterns/composite-entity/
|
||||
categories: Structural
|
||||
tags:
|
||||
- Enterprise Integration Pattern
|
||||
---
|
||||
|
||||
## 含义
|
||||
|
||||
复合实体模式用于对一组相关联的持久化对象进行建模、描述和管理,用于取代对这组对象描述为单独粒度的实体。
|
||||
|
||||
## 解释
|
||||
|
||||
现实例子
|
||||
|
||||
> 对于一个控制台对象,需要管理许多接口功能。通过使用复合实体模式,将消息对象、信号对象等依赖性对象组合在一起,直接使用单个对象对其进行控制。
|
||||
|
||||
简单地说
|
||||
|
||||
> 复合实体模式允许使用一个统一对象来管理一组相互关联的对象
|
||||
|
||||
**编程示例**
|
||||
|
||||
我们需要一个通用的解决方案来解决上述的控制台问题。我们引入了以下的通用复合对象。
|
||||
|
||||
```java
|
||||
public abstract class DependentObject<T> {
|
||||
|
||||
T data;
|
||||
|
||||
public void setData(T message) {
|
||||
this.data = message;
|
||||
}
|
||||
|
||||
public T getData() {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class CoarseGrainedObject<T> {
|
||||
|
||||
DependentObject<T>[] 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<String> {
|
||||
|
||||
}
|
||||
|
||||
public class SignalDependentObject extends DependentObject<String> {
|
||||
|
||||
}
|
||||
|
||||
public class ConsoleCoarseGrainedObject extends CoarseGrainedObject<String> {
|
||||
|
||||
@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);
|
||||
```
|
||||
|
||||
## 类图
|
||||
|
||||

|
||||
|
||||
## 适用场景
|
||||
|
||||
复合实体模式适用于以下场景:
|
||||
|
||||
* 你想要通过一个对象来管理多个依赖对象,已调整对象之间的细化程度。同时将依赖对象的生命周期托管到这个粗粒度的复合实体对象。
|
||||
## 引用
|
||||
|
||||
* [Composite Entity Pattern in wikipedia](https://en.wikipedia.org/wiki/Composite_entity_pattern)
|
175
localization/zh/composite/README.md
Normal file
175
localization/zh/composite/README.md
Normal file
@ -0,0 +1,175 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Composite
|
||||
folder: composite
|
||||
permalink: /patterns/composite/zh
|
||||
categories: Structural
|
||||
language: zh
|
||||
tags:
|
||||
- Gang of Four
|
||||
---
|
||||
|
||||
## 目的
|
||||
|
||||
将对象组合成树结构以表示部分整体层次结构。 组合可以使客户统一对待单个对象和组合对象。
|
||||
|
||||
## 解释
|
||||
|
||||
真实世界例子
|
||||
|
||||
> 每个句子由单词组成,单词又由字符组成。这些对象中的每一个都是可打印的,它们可以在它们之前或之后打印一些内容,例如句子始终以句号结尾,单词始终在其前面有空格。
|
||||
|
||||
通俗的说
|
||||
|
||||
> 组合模式使客户能够以统一的方式对待各个对象。
|
||||
|
||||
维基百科说
|
||||
|
||||
> 在软件工程中,组合模式是一种分区设计模式。组合模式中,一组对象将像一个对象的单独实例一样被对待。组合的目的是将对象“组成”树状结构,以表示部分整体层次结构。实现组合模式可使客户统一对待单个对象和组合对象。
|
||||
|
||||
**程序示例**
|
||||
|
||||
使用上面的句子例子。 这里我们有基类`LetterComposite`和不同的可打印类型`Letter`,`Word`和`Sentence`。
|
||||
|
||||
```java
|
||||
public abstract class LetterComposite {
|
||||
|
||||
private final List<LetterComposite> 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<Letter> 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<Word> 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.
|
||||
```
|
||||
|
||||
## 类图
|
||||
|
||||

|
||||
|
||||
## 适用性
|
||||
|
||||
使用组合模式当
|
||||
|
||||
* 你想要表示对象的整体层次结构
|
||||
* 你希望客户能够忽略组合对象和单个对象之间的差异。 客户将统一对待组合结构中的所有对象。
|
||||
|
||||
## 真实世界例子
|
||||
|
||||
* [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)
|
102
localization/zh/converter/README.md
Normal file
102
localization/zh/converter/README.md
Normal file
@ -0,0 +1,102 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Converter
|
||||
folder: converter
|
||||
permalink: /patterns/converter/zh
|
||||
categories: Creational
|
||||
language: zh
|
||||
tags:
|
||||
- Decoupling
|
||||
---
|
||||
|
||||
## 目的
|
||||
|
||||
转换器模式的目的是提供相应类型之间双向转换的通用方法,允许进行干净的实现,而类型之间无需相互了解。此外,Converter模式引入了双向集合映射,从而将样板代码减少到最少。
|
||||
|
||||
## 解释
|
||||
|
||||
真实世界例子
|
||||
|
||||
> 在真实的应用中经常有这种情况,数据库层包含需要被转换成业务逻辑层DTO来使用的实体。对于潜在的大量类进行类似的映射,我们需要一种通用的方法来实现这一点。
|
||||
|
||||
通俗的说
|
||||
|
||||
> 转换器模式让一个类的实例映射成另一个类的实例变得简单
|
||||
|
||||
**程序示例**
|
||||
|
||||
我们需要一个通用的方案来解决映射问题。让我们来介绍一个通用的转换器。
|
||||
|
||||
```java
|
||||
public class Converter<T, U> {
|
||||
|
||||
private final Function<T, U> fromDto;
|
||||
private final Function<U, T> fromEntity;
|
||||
|
||||
public Converter(final Function<T, U> fromDto, final Function<U, T> 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<U> createFromDtos(final Collection<T> dtos) {
|
||||
return dtos.stream().map(this::convertFromDto).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public final List<T> createFromEntities(final Collection<U> entities) {
|
||||
return entities.stream().map(this::convertFromEntity).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
专属的转换器像下面一样从基类继承。
|
||||
|
||||
```java
|
||||
public class UserConverter extends Converter<UserDto, User> {
|
||||
|
||||
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);
|
||||
```
|
||||
|
||||
## 类图
|
||||
|
||||

|
||||
|
||||
## 适用性
|
||||
|
||||
在下面这些情况下使用转换器模式:
|
||||
|
||||
* 如果你的类型在逻辑上相互对应,并需要在它们之间转换实体
|
||||
* 当你想根据上下文提供不同的类型转换方式时
|
||||
* 每当你引入DTO(数据传输对象)时你可能都需要将其转换为
|
||||
DO
|
||||
|
||||
## 鸣谢
|
||||
|
||||
* [Converter](http://www.xsolve.pl/blog/converter-pattern-in-java-8/)
|
163
localization/zh/dao/README.md
Normal file
163
localization/zh/dao/README.md
Normal file
@ -0,0 +1,163 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Data Access Object
|
||||
folder: dao
|
||||
permalink: /patterns/dao/zh
|
||||
categories: Architectural
|
||||
language: zh
|
||||
tags:
|
||||
- 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<Customer> getAll() throws Exception;
|
||||
|
||||
Optional<Customer> 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<Integer, Customer> 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
|
||||
```
|
||||
|
||||
## 类图
|
||||
|
||||

|
||||
|
||||
## 适用性
|
||||
|
||||
在以下情况下,请使用数据访问对象::
|
||||
|
||||
* 当您要巩固如何访问数据层时。
|
||||
* 当您要避免编写多个数据检索/持久层时。
|
||||
|
||||
## 鸣谢
|
||||
|
||||
* [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)
|
31
localization/zh/data-bus/README.md
Normal file
31
localization/zh/data-bus/README.md
Normal file
@ -0,0 +1,31 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Data Bus
|
||||
folder: data-bus
|
||||
permalink: /patterns/data-bus/
|
||||
|
||||
categories: Architectural
|
||||
tags:
|
||||
- Decoupling
|
||||
---
|
||||
|
||||
## 含义
|
||||
|
||||
数据总线模式(译者:实际上,就是 Event-Bus 消息总线模式)允许在一个应用程序的组件之间收发消息/事件,而不需要这些组件相互感知,它们只需要知道所发送/接收的消息/事件的类型即可。
|
||||
|
||||
## 类图
|
||||

|
||||
|
||||
## 适用场景
|
||||
可以在以下场景使用数据总线模式:
|
||||
|
||||
* 你希望由你的组件自己决定要接收哪些信息/事件
|
||||
* 你希望实现多对多的通信
|
||||
* 你希望你的组件不需要感知彼此
|
||||
|
||||
## 相关模式
|
||||
数据总线类似于以下设计模式:
|
||||
|
||||
* 中介者模式(Mediator pattern),由数据总线成员自己决定是否要接受任何给定的消息。
|
||||
* 观察者模式(Observer pattern),但进一步支持了多对多的通信。
|
||||
* 发布/订阅模式(Publish/Subscribe pattern),但是数据总线将发布者和订阅者解耦。
|
25
localization/zh/data-mapper/README.md
Normal file
25
localization/zh/data-mapper/README.md
Normal file
@ -0,0 +1,25 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Data Mapper
|
||||
folder: data-mapper
|
||||
permalink: /patterns/data-mapper/
|
||||
categories: Architectural
|
||||
tags:
|
||||
- Decoupling
|
||||
---
|
||||
|
||||
## 含义
|
||||
一个用于在持久化对象和数据库之间传输数据的映射器,同时保持它们之间和映射器本身的独立性。
|
||||
|
||||
## 类图
|
||||

|
||||
|
||||
## 适用场景
|
||||
数据映射器适用于以下场景:
|
||||
|
||||
* 当你想把数据对象从数据库访问层解耦时时
|
||||
* 当你想编写多个数据查询/持久化实现时
|
||||
|
||||
## 引用
|
||||
|
||||
* [Data Mapper](http://richard.jp.leguen.ca/tutoring/soen343-f2010/tutorials/implementing-data-mapper/)
|
112
localization/zh/data-transfer-object/README.md
Normal file
112
localization/zh/data-transfer-object/README.md
Normal file
@ -0,0 +1,112 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Data Transfer Object
|
||||
folder: data-transfer-object
|
||||
permalink: /patterns/data-transfer-object/zh
|
||||
categories: Architectural
|
||||
language: zh
|
||||
tags:
|
||||
- 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<CustomerDto> customers;
|
||||
|
||||
public CustomerResource(List<CustomerDto> customers) {
|
||||
this.customers = customers;
|
||||
}
|
||||
|
||||
public List<CustomerDto> 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
|
||||
```
|
||||
|
||||
## 类图
|
||||
|
||||

|
||||
|
||||
## 适用性
|
||||
|
||||
使用数据传输对象模式当
|
||||
|
||||
* 客户端请求多种信息。信息都是相关的
|
||||
* 当你想提高获取资源的性能
|
||||
* 你想降低远程方法调用的次数
|
||||
|
||||
## 鸣谢
|
||||
|
||||
* [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)
|
136
localization/zh/decorator/README.md
Normal file
136
localization/zh/decorator/README.md
Normal file
@ -0,0 +1,136 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Decorator
|
||||
folder: decorator
|
||||
permalink: /patterns/decorator/zh
|
||||
categories: Structural
|
||||
language: zh
|
||||
tags:
|
||||
- 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!
|
||||
```
|
||||
|
||||
## 类图
|
||||

|
||||
|
||||
## 适用性
|
||||
使用装饰者
|
||||
|
||||
* 动态透明地向单个对象添加职责,即不影响其他对象
|
||||
* 对于可以撤销的责任
|
||||
* 当通过子类化进行扩展是不切实际的。有时可能会有大量的独立扩展,并且会产生大量的子类来支持每种组合。 否则类定义可能被隐藏或无法用于子类化。
|
||||
|
||||
## 教程
|
||||
* [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)
|
30
localization/zh/delegation/README.md
Normal file
30
localization/zh/delegation/README.md
Normal file
@ -0,0 +1,30 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Delegation
|
||||
folder: delegation
|
||||
permalink: /patterns/delegation/zh
|
||||
categories: Structural
|
||||
language: zh
|
||||
tags:
|
||||
- Decoupling
|
||||
---
|
||||
|
||||
## 或称
|
||||
代理模式
|
||||
|
||||
## 目的
|
||||
它是一种让对象将某种行为向外部表达,但实际上将实现该行为的责任委托给关联对象的技术。
|
||||
|
||||
## 类图
|
||||

|
||||
|
||||
## 适用性
|
||||
使用委托模式以实现以下目的
|
||||
|
||||
* 降低类的耦合性
|
||||
* 组件的行为相同,但是意识到这种情况将来可能会改变。
|
||||
|
||||
## 鸣谢
|
||||
|
||||
* [Delegate Pattern: Wikipedia ](https://en.wikipedia.org/wiki/Delegation_pattern)
|
||||
* [Proxy Pattern: Wikipedia ](https://en.wikipedia.org/wiki/Proxy_pattern)
|
102
localization/zh/dependency-injection/README.md
Normal file
102
localization/zh/dependency-injection/README.md
Normal file
@ -0,0 +1,102 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Dependency Injection
|
||||
folder: dependency-injection
|
||||
permalink: /patterns/dependency-injection/zh
|
||||
categories: Creational
|
||||
language: zh
|
||||
tags:
|
||||
- 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();
|
||||
```
|
||||
|
||||
## 类图
|
||||
|
||||

|
||||
|
||||
## 适用性
|
||||
|
||||
使用依赖注入当:
|
||||
|
||||
- 当你需要从对象中移除掉具体的实现内容时
|
||||
|
||||
* 使用模拟对象或存根隔离地启用类的单元测试
|
||||
|
||||
## 鸣谢
|
||||
|
||||
* [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)
|
30
localization/zh/dirty-flag/README.md
Normal file
30
localization/zh/dirty-flag/README.md
Normal file
@ -0,0 +1,30 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Dirty Flag
|
||||
folder: dirty-flag
|
||||
permalink: /patterns/dirty-flag/zh
|
||||
categories: Behavioral
|
||||
language: zh
|
||||
tags:
|
||||
- Game programming
|
||||
- Performance
|
||||
---
|
||||
|
||||
## 或称
|
||||
* 是否脏 模式
|
||||
|
||||
## 目的
|
||||
避免昂贵资源的重新获取。资源保留其身份,保留在某些快速访问的存储中,并被重新使用以避免再次获取它们。
|
||||
|
||||
## 类图
|
||||

|
||||
|
||||
## 适用性
|
||||
在以下情况下使用脏标志模式
|
||||
|
||||
* 重复获取,初始化,释放相同资源所导致不必要的性能开销
|
||||
|
||||
## 鸣谢
|
||||
|
||||
* [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)
|
21
localization/zh/double-checked-locking/README.md
Normal file
21
localization/zh/double-checked-locking/README.md
Normal file
@ -0,0 +1,21 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Double Checked Locking
|
||||
folder: double-checked-locking
|
||||
permalink: /patterns/double-checked-locking/
|
||||
categories: Idiom
|
||||
tags:
|
||||
- Performance
|
||||
---
|
||||
|
||||
## 含义
|
||||
通过先测试锁定标准("锁提示")而不实际获取锁的方式来减少获取锁的开销。只有当锁定标准检查表明需要锁定时,才进行实际的锁定逻辑。
|
||||
|
||||
## 类图
|
||||

|
||||
|
||||
## 适用场景
|
||||
在以下场景适合使用双重锁检查模式:
|
||||
|
||||
* 在创建对象时有存在并发的访问。如单例模式中,你想创建同一个类的单个实例,如果存在两个或更多的线程对实例进行判空,仅仅检查该该实例是否为空可能是不够的。
|
||||
* 在一个方法上存在并发访问,该方法的行为是根据一些约束条件而改变,而这些约束条件在该方法中也会发生变化。
|
204
localization/zh/facade/README.md
Normal file
204
localization/zh/facade/README.md
Normal file
@ -0,0 +1,204 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Facade
|
||||
folder: facade
|
||||
permalink: /patterns/facade/zh
|
||||
categories: Structural
|
||||
language: zh
|
||||
tags:
|
||||
- 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();
|
||||
break;
|
||||
case WAKE_UP:
|
||||
wakeUp();
|
||||
break;
|
||||
case GO_HOME:
|
||||
goHome();
|
||||
break;
|
||||
case GO_TO_MINE:
|
||||
goToMine();
|
||||
break;
|
||||
case WORK:
|
||||
work();
|
||||
break;
|
||||
default:
|
||||
LOGGER.info("Undefined action");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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<DwarvenMineWorker> 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<DwarvenMineWorker> 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.
|
||||
```
|
||||
|
||||
## 类图
|
||||

|
||||
|
||||
## 适用性
|
||||
使用外观模式当
|
||||
|
||||
* 你想为一个复杂的子系统提供一个简单的接口。随着子系统的发展,它们通常会变得更加复杂。多数模式在应用时会导致更多和更少的类。这使子系统更可重用,更易于自定义,但是对于不需要自定义它的客户来说,使用它也变得更加困难。 外观可以提供子系统的简单默认视图,足以满足大多数客户端的需求。只有需要更多可定制性的客户才需要查看外观外的东西(原子系统提供的接口)。
|
||||
* 客户端与抽象的实现类之间存在许多依赖关系。 引入外观以使子系统与客户端和其他子系统分离,从而提高子系统的独立性和可移植性。
|
||||
* 您想对子系统进行分层。 使用外观来定义每个子系统级别的入口点。 如果子系统是相关的,则可以通过使子系统仅通过其外观相互通信来简化它们之间的依赖性。
|
||||
|
||||
## 鸣谢
|
||||
|
||||
* [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)
|
27
localization/zh/factory-kit/README.md
Normal file
27
localization/zh/factory-kit/README.md
Normal file
@ -0,0 +1,27 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Factory Kit
|
||||
folder: factory-kit
|
||||
permalink: /patterns/factory-kit/
|
||||
categories: Creational
|
||||
tags:
|
||||
- Extensibility
|
||||
---
|
||||
|
||||
## 含义
|
||||
使用分离的构建器和工厂接口来定义一个不可变内容的工厂。
|
||||
|
||||
## 类图
|
||||

|
||||
|
||||
## 适用场景
|
||||
工厂套件模式适用于与以下场景:
|
||||
|
||||
* 一个类无法预知它需要创建的对象的类别
|
||||
- 你只是想要一个新的自定义构建器(builder)的实例,而非全局的构建器
|
||||
- 你明确地想要定义对象的类型,而且工厂可以创建这些对象
|
||||
- 你想要分离构建器(builder)和创建器(creator)接口
|
||||
|
||||
## 引用
|
||||
|
||||
* [Design Pattern Reloaded by Remi Forax: ](https://www.youtube.com/watch?v=-k2X7guaArU)
|
89
localization/zh/factory-method/README.md
Normal file
89
localization/zh/factory-method/README.md
Normal file
@ -0,0 +1,89 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Factory Method
|
||||
folder: factory-method
|
||||
permalink: /patterns/factory-method/zh
|
||||
categories: Creational
|
||||
language: zh
|
||||
tags:
|
||||
- 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
|
||||
```
|
||||
|
||||
## 类图
|
||||

|
||||
|
||||
## 适用性
|
||||
使用工厂方法模式当
|
||||
|
||||
* 一个类无法预料它所要必须创建的对象的类
|
||||
* 一个类想要它的子类来指定它要创建的对象
|
||||
* 类将责任委派给几个帮助子类中的一个,而你想定位了解是具体之中的哪一个
|
||||
|
||||
## 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)
|
141
localization/zh/factory/README.md
Normal file
141
localization/zh/factory/README.md
Normal file
@ -0,0 +1,141 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Factory
|
||||
folder: factory
|
||||
permalink: /patterns/factory/
|
||||
categories: Creational
|
||||
tags:
|
||||
- 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<Car> constructor;
|
||||
|
||||
CarType(Supplier<Car> constructor) {
|
||||
this.constructor = constructor;
|
||||
}
|
||||
|
||||
public Supplier<Car> 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.
|
||||
```
|
||||
|
||||
## 类图
|
||||
|
||||

|
||||
|
||||
## 适用场景
|
||||
|
||||
在你只关心对象的创建,但不关心如何创建、管理它的时候,请使用简单工厂模式。
|
||||
|
||||
**优点**
|
||||
|
||||
* 可以把对象创建代码集中在一个地方,避免在代码库存散布 "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/)
|
||||
|
36
localization/zh/interpreter/README.md
Normal file
36
localization/zh/interpreter/README.md
Normal file
@ -0,0 +1,36 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Interpreter
|
||||
folder: interpreter
|
||||
permalink: /patterns/interpreter/zh
|
||||
categories: Behavioral
|
||||
language: zh
|
||||
tags:
|
||||
- Gang of Four
|
||||
---
|
||||
|
||||
## 目的
|
||||
给定一种语言,请定义其语法的表示形式,以及使用该表示形式来解释该语言中的句子的解释器。
|
||||
|
||||
## 类图
|
||||

|
||||
|
||||
## 适用性
|
||||
有一种要解释的语言时,请使用解释器模式,并且可以将语言中的语句表示为抽象语法树。解释器模式在以下情况下效果最佳
|
||||
|
||||
* 语法很简单。 对于复杂的语法,语法的类层次结构变得庞大且难以管理。 在这种情况下,解析器生成器之类的工具是更好的选择。 他们可以在不构建抽象语法树的情况下解释表达式,这可以节省空间并可能节省时间
|
||||
* 效率不是关键问题。 通常,最有效的解释器不是通过直接解释解析树来实现的,而是先将其转换为另一种形式。 例如,正则表达式通常会转换为状态机。 但是即使这样,翻译器也可以通过解释器模式实现,因此该模式仍然适用。
|
||||
|
||||
## 真实世界例子
|
||||
|
||||
* [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)
|
135
localization/zh/iterator/README.md
Normal file
135
localization/zh/iterator/README.md
Normal file
@ -0,0 +1,135 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Iterator
|
||||
folder: iterator
|
||||
permalink: /patterns/iterator/zh
|
||||
categories: Behavioral
|
||||
language: zh
|
||||
tags:
|
||||
- Gang of Four
|
||||
---
|
||||
|
||||
## 又被称为
|
||||
游标
|
||||
|
||||
## 目的
|
||||
提供一种在不暴露其基础表示的情况下顺序访问聚合对象的元素的方法。
|
||||
|
||||
## 解释
|
||||
|
||||
真实世界例子
|
||||
|
||||
> 百宝箱包含一组魔法物品。有多种物品,例如戒指,药水和武器。可以使用藏宝箱提供的迭代器按类型浏览商品。
|
||||
|
||||
通俗地说
|
||||
|
||||
> 容器可以提供与表示形式无关的迭代器接口,以提供对元素的访问。
|
||||
|
||||
维基百科说
|
||||
|
||||
> 在面向对象的编程中,迭代器模式是一种设计模式,其中迭代器用于遍历容器并访问容器的元素。
|
||||
|
||||
**程序示例**
|
||||
|
||||
在我们的示例中包含物品的藏宝箱是主要类。
|
||||
|
||||
```java
|
||||
public class TreasureChest {
|
||||
|
||||
private final List<Item> 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<Item> iterator(ItemType itemType) {
|
||||
return new TreasureChestItemIterator(this, itemType);
|
||||
}
|
||||
|
||||
public List<Item> 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<T> {
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
## 类图
|
||||

|
||||
|
||||
## 适用性
|
||||
以下情况使用迭代器模式
|
||||
|
||||
* 在不暴露其内部表示的情况下访问聚合对象的内容
|
||||
* 为了支持聚合对象的多种遍历方式
|
||||
* 提供一个遍历不同聚合结构的统一接口
|
||||
|
||||
## 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)
|
157
localization/zh/observer/README.md
Normal file
157
localization/zh/observer/README.md
Normal file
@ -0,0 +1,157 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Observer
|
||||
folder: observer
|
||||
permalink: /patterns/observer/zh
|
||||
categories: Behavioral
|
||||
language: zh
|
||||
tags:
|
||||
- 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<WeatherObserver> 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
|
||||

|
||||
|
||||
## 应用
|
||||
在下面任何一种情况下都可以使用观察者模式
|
||||
|
||||
* 当抽象具有两个方面时,一个方面依赖于另一个方面。将这些方面封装在单独的对象中,可以使你分别进行更改和重用
|
||||
* 当一个对象的改变的同时需要改变其他对象,同时你又不知道有多少对象需要改变时
|
||||
* 当一个对象可以通知其他对象而无需假设这些对象是谁时。换句话说,你不想让这些对象紧耦合。
|
||||
|
||||
## 典型用例
|
||||
|
||||
* 一个对象的改变导致其他对象的改变
|
||||
|
||||
## 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)
|
129
localization/zh/private-class-data/README.md
Normal file
129
localization/zh/private-class-data/README.md
Normal file
@ -0,0 +1,129 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Private Class Data
|
||||
folder: private-class-data
|
||||
permalink: /patterns/private-class-data/zh
|
||||
categories: Idiom
|
||||
language: zh
|
||||
tags:
|
||||
- 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
|
||||
```
|
||||
|
||||
## 类图
|
||||
|
||||

|
||||
|
||||
## 适用性
|
||||
|
||||
在以下情况下使用私有类数据模式
|
||||
|
||||
* 您要阻止对类数据成员的写访问。
|
22
localization/zh/producer-consumer/README.md
Normal file
22
localization/zh/producer-consumer/README.md
Normal file
@ -0,0 +1,22 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Producer Consumer
|
||||
folder: producer-consumer
|
||||
permalink: /patterns/producer-consumer/zh
|
||||
categories: Concurrency
|
||||
language: zh
|
||||
tags:
|
||||
- Reactive
|
||||
---
|
||||
|
||||
## 目的
|
||||
生产者消费者设计模式是一种经典的并发模式,通过将工作与执行工作任务分开来减少生产者与消费者之间的耦合。
|
||||
|
||||
## 类图
|
||||

|
||||
|
||||
## 适用性
|
||||
在以下情况下使用生产者消费者
|
||||
|
||||
* 通过将工作分成生产和消费两个工作进程来解耦系统
|
||||
* 解决生产工作和消费工作需要不同时间的问题
|
162
localization/zh/proxy/README.md
Normal file
162
localization/zh/proxy/README.md
Normal file
@ -0,0 +1,162 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Proxy
|
||||
folder: proxy
|
||||
permalink: /patterns/proxy/zh
|
||||
categories: Structural
|
||||
language: zh
|
||||
tags:
|
||||
- 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!
|
||||
```
|
||||
|
||||
## 类图
|
||||
|
||||

|
||||
|
||||
## 适用性
|
||||
|
||||
代理适用于需要比简单指针更广泛或更复杂的对象引用的情况。这是代理模式适用的几种常见情况。
|
||||
|
||||
* 远程代理为不同地址空间中的对象提供了本地代表。
|
||||
* 虚拟代理根据需要创建昂贵的对象。
|
||||
* 保护代理控制对原始对象的访问。当对象有不同的接入权限时保护代理很有用。
|
||||
|
||||
## 典型用例
|
||||
|
||||
* 对象的访问控制
|
||||
* 懒加载
|
||||
* 实现日志记录
|
||||
* 简化网络连接
|
||||
* 对象的访问计数
|
||||
|
||||
## 教程
|
||||
|
||||
* [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)
|
30
localization/zh/sharding/README.md
Normal file
30
localization/zh/sharding/README.md
Normal file
@ -0,0 +1,30 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Sharding
|
||||
folder: sharding
|
||||
permalink: /patterns/sharding/
|
||||
categories: Behavioral
|
||||
tags:
|
||||
- Performance
|
||||
- Cloud distributed
|
||||
---
|
||||
|
||||
## 含义
|
||||
分片模式是指将数据存储划分为水平分区或分片。每个分片都有相同的模式,但持有自己独特的数据子集。
|
||||
|
||||
一个分片本身就是一个数据存储(它可以包含许多不同类型的实体的数据),运行在作为存储节点的服务器上。
|
||||
|
||||
## 类图
|
||||

|
||||
|
||||
## 适用场景
|
||||
这种设计模式提供了一下的好处:
|
||||
|
||||
- 你可以通过增加在额外的存储节点上,运行的更多分片来实现系统扩容。
|
||||
- 系统可以使用现成的廉价硬件,而不是为每个存储节点使用专门(或者昂贵)的服务器硬件。
|
||||
- 你可以通过平衡各分片之间的工作负载来减少竞争,以提高性能。
|
||||
- 在云环境中,分片可以在物理上靠近访问该节点数据的用户。
|
||||
|
||||
## 引用
|
||||
|
||||
* [Sharding pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/sharding)
|
156
localization/zh/state/README.md
Normal file
156
localization/zh/state/README.md
Normal file
@ -0,0 +1,156 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: State
|
||||
folder: state
|
||||
permalink: /patterns/state/zh
|
||||
categories: Behavioral
|
||||
language: zh
|
||||
tags:
|
||||
- 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.
|
||||
```
|
||||
|
||||
## 类图
|
||||

|
||||
|
||||
## 适用性
|
||||
|
||||
在以下两种情况下,请使用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)
|
134
localization/zh/strategy/README.md
Normal file
134
localization/zh/strategy/README.md
Normal file
@ -0,0 +1,134 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Strategy
|
||||
folder: strategy
|
||||
permalink: /patterns/strategy/zh
|
||||
categories: Behavioral
|
||||
language: zh
|
||||
tags:
|
||||
- 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!
|
||||
```
|
||||
|
||||
## 类图
|
||||

|
||||
|
||||
## 应用
|
||||
使用策略模式当
|
||||
|
||||
* 许多相关的类只是行为不同。策略模式提供了一种为一种类配置多种行为的能力。
|
||||
* 你需要一种算法的不同变体。比如,你可能定义反应不用时间空间权衡的算法。当这些算法的变体使用类的层次结构来实现时就可以使用策略模式。
|
||||
* 一个算法使用的数据客户不应该对其知晓。使用策略模式来避免暴露复杂的,特定于算法的数据结构。
|
||||
* 一个类定义了许多行为,这些行为在其操作中展现为多个条件语句。移动相关的条件分支到它们分别的策略类中来代替这些条件语句。
|
||||
|
||||
## 教学
|
||||
|
||||
* [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)
|
146
localization/zh/template-method/README.md
Normal file
146
localization/zh/template-method/README.md
Normal file
@ -0,0 +1,146 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Template method
|
||||
folder: template-method
|
||||
permalink: /patterns/template-method/zh
|
||||
categories: Behavioral
|
||||
language: zh
|
||||
tags:
|
||||
- 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();
|
||||
```
|
||||
|
||||
## 类图
|
||||

|
||||
|
||||
## 适用性
|
||||
|
||||
使用模板方法模式可以
|
||||
|
||||
* 一次性实现一个算法中不变的部分并将其留给子类来实现可能变化的行为。
|
||||
* 子类之间的共同行为应分解并集中在一个共同类中,以避免代码重复。如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)
|
164
localization/zh/version-number/README.md
Normal file
164
localization/zh/version-number/README.md
Normal file
@ -0,0 +1,164 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Version Number
|
||||
folder: versionnumber
|
||||
permalink: /patterns/versionnumber/zh
|
||||
description: Entity versioning with version number
|
||||
language: zh
|
||||
|
||||
categories:
|
||||
- Concurrency
|
||||
|
||||
tags:
|
||||
- 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<Long, Book> 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
|
||||
```
|
||||
|
||||
## 类图
|
||||
|
||||

|
||||
|
||||
## 适用性
|
||||
|
||||
将版本号用于:
|
||||
|
||||
* 解决对数据的并发写访问
|
||||
* 强的数据一致性
|
||||
|
||||
## 教程
|
||||
* [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)
|
225
localization/zh/visitor/README.md
Normal file
225
localization/zh/visitor/README.md
Normal file
@ -0,0 +1,225 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Visitor
|
||||
folder: visitor
|
||||
permalink: /patterns/visitor/zh
|
||||
categories: Behavioral
|
||||
language: zh
|
||||
tags:
|
||||
- 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
|
||||
|
||||

|
||||
|
||||
## 适用性
|
||||
|
||||
使用访问者模式当
|
||||
|
||||
* 对象结构包含许多具有不同接口的对象类,并且你希望根据这些对象的具体类对这些对象执行操作。
|
||||
* 需要对对象结构中的对象执行许多不同且不相关的操作,并且你想避免使用这些操作“污染”它们的类。 访问者可以通过在一个类中定义相关操作来将它们保持在一起。当许多应用程序共享对象结构时,请使用访问者模式将操作仅放在需要它们的那些应用程序中
|
||||
* 定义对象结构的类很少变化,但是你经常想在结构上定义新的操作。更改对象结构类需要重新定义所有访问者的接口,这可能会导致成本高昂。如果对象结构类经常更改,则最好在这些类中定义操作。
|
||||
|
||||
## 真实例子
|
||||
|
||||
* [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)
|
Reference in New Issue
Block a user