From 2207ea4ce9a76deaa91b0181950e11237c246ffc Mon Sep 17 00:00:00 2001 From: Mike Liu <657829312@qq.com> Date: Mon, 8 Feb 2021 23:37:48 +0800 Subject: [PATCH] Translation zh (#1646) * add state and callback pattern * add command and template-method pattern * add iterator pattern * add bridege and DI pattern * fix issue #1600 * add converter,proxy,visitor pattern * add caching,composite,delegation,dirty-flag,interpreter patterns * add dao and producer-consumer * add dto and provate class data pattern * fix #1646 png path problems * fix #1646 composite png path case problem Co-authored-by: Mike Co-authored-by: Subhrodip Mohanta --- zh/caching/README.md | 27 ++++ zh/composite/README.md | 174 +++++++++++++++++++++++ zh/converter/README.md | 101 ++++++++++++++ zh/dao/README.md | 162 +++++++++++++++++++++ zh/data-transfer-object/README.md | 111 +++++++++++++++ zh/delegation/README.md | 29 ++++ zh/dirty-flag/README.md | 29 ++++ zh/interpreter/README.md | 35 +++++ zh/private-class-data/README.md | 128 +++++++++++++++++ zh/producer-consumer/README.md | 21 +++ zh/proxy/README.md | 161 +++++++++++++++++++++ zh/state/README.md | 2 +- zh/template-method/README.md | 2 +- zh/visitor/README.md | 224 ++++++++++++++++++++++++++++++ 14 files changed, 1204 insertions(+), 2 deletions(-) create mode 100644 zh/caching/README.md create mode 100644 zh/composite/README.md create mode 100644 zh/converter/README.md create mode 100644 zh/dao/README.md create mode 100644 zh/data-transfer-object/README.md create mode 100644 zh/delegation/README.md create mode 100644 zh/dirty-flag/README.md create mode 100644 zh/interpreter/README.md create mode 100644 zh/private-class-data/README.md create mode 100644 zh/producer-consumer/README.md create mode 100644 zh/proxy/README.md create mode 100644 zh/visitor/README.md diff --git a/zh/caching/README.md b/zh/caching/README.md new file mode 100644 index 000000000..54cb982b2 --- /dev/null +++ b/zh/caching/README.md @@ -0,0 +1,27 @@ +--- +layout: pattern +title: Caching +folder: caching +permalink: /patterns/caching/ +categories: Behavioral +tags: + - Performance + - Cloud distributed +--- + +## 目的 +为了避免昂贵的资源重新获取,方法是在资源使用后不立即释放资源。资源保留其身份,保留在某些快速访问的存储中,并被重新使用,以避免再次获取它们。 + +## 类图 +![alt text](../../caching/etc/caching.png "Caching") + +## 适用性 +在以下情况下使用缓存模式 + +* 重复获取,初始化和释放同一资源会导致不必要的性能开销。 + +## 鸣谢 + +* [Write-through, write-around, write-back: Cache explained](http://www.computerweekly.com/feature/Write-through-write-around-write-back-Cache-explained) +* [Read-Through, Write-Through, Write-Behind, and Refresh-Ahead Caching](https://docs.oracle.com/cd/E15357_01/coh.360/e15723/cache_rtwtwbra.htm#COHDG5177) +* [Cache-Aside pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/cache-aside) diff --git a/zh/composite/README.md b/zh/composite/README.md new file mode 100644 index 000000000..1f0f7b3b4 --- /dev/null +++ b/zh/composite/README.md @@ -0,0 +1,174 @@ +--- +layout: pattern +title: Composite +folder: composite +permalink: /patterns/composite/ +categories: Structural +tags: + - Gang of Four +--- + +## 目的 + +将对象组合成树结构以表示部分整体层次结构。 组合可以使客户统一对待单个对象和组合对象。 + +## 解释 + +真实世界例子 + +> 每个句子由单词组成,单词又由字符组成。这些对象中的每一个都是可打印的,它们可以在它们之前或之后打印一些内容,例如句子始终以句号结尾,单词始终在其前面有空格。 + +通俗的说 + +> 组合模式使客户能够以统一的方式对待各个对象。 + +维基百科说 + +> 在软件工程中,组合模式是一种分区设计模式。组合模式中,一组对象将像一个对象的单独实例一样被对待。组合的目的是将对象“组成”树状结构,以表示部分整体层次结构。实现组合模式可使客户统一对待单个对象和组合对象。 + +**程序示例** + +使用上面的句子例子。 这里我们有基类`LetterComposite`和不同的可打印类型`Letter`,`Word`和`Sentence`。 + +```java +public abstract class LetterComposite { + + private final List children = new ArrayList<>(); + + public void add(LetterComposite letter) { + children.add(letter); + } + + public int count() { + return children.size(); + } + + protected void printThisBefore() { + } + + protected void printThisAfter() { + } + + public void print() { + printThisBefore(); + children.forEach(LetterComposite::print); + printThisAfter(); + } +} + +public class Letter extends LetterComposite { + + private final char character; + + public Letter(char c) { + this.character = c; + } + + @Override + protected void printThisBefore() { + System.out.print(character); + } +} + +public class Word extends LetterComposite { + + public Word(List letters) { + letters.forEach(this::add); + } + + public Word(char... letters) { + for (char letter : letters) { + this.add(new Letter(letter)); + } + } + + @Override + protected void printThisBefore() { + System.out.print(" "); + } +} + +public class Sentence extends LetterComposite { + + public Sentence(List words) { + words.forEach(this::add); + } + + @Override + protected void printThisAfter() { + System.out.print("."); + } +} +``` + +然后我们有一个消息携带者来携带消息。 + +```java +public class Messenger { + + LetterComposite messageFromOrcs() { + + var words = List.of( + new Word('W', 'h', 'e', 'r', 'e'), + new Word('t', 'h', 'e', 'r', 'e'), + new Word('i', 's'), + new Word('a'), + new Word('w', 'h', 'i', 'p'), + new Word('t', 'h', 'e', 'r', 'e'), + new Word('i', 's'), + new Word('a'), + new Word('w', 'a', 'y') + ); + + return new Sentence(words); + + } + + LetterComposite messageFromElves() { + + var words = List.of( + new Word('M', 'u', 'c', 'h'), + new Word('w', 'i', 'n', 'd'), + new Word('p', 'o', 'u', 'r', 's'), + new Word('f', 'r', 'o', 'm'), + new Word('y', 'o', 'u', 'r'), + new Word('m', 'o', 'u', 't', 'h') + ); + + return new Sentence(words); + + } + +} +``` + +然后它可以这样使用: + +```java +var orcMessage = new Messenger().messageFromOrcs(); +orcMessage.print(); // Where there is a whip there is a way. +var elfMessage = new Messenger().messageFromElves(); +elfMessage.print(); // Much wind pours from your mouth. +``` + +## 类图 + +![alt text](../../composite/etc/composite.urm.png "Composite class diagram") + +## 适用性 + +使用组合模式当 + +* 你想要表示对象的整体层次结构 +* 你希望客户能够忽略组合对象和单个对象之间的差异。 客户将统一对待组合结构中的所有对象。 + +## 真实世界例子 + +* [java.awt.Container](http://docs.oracle.com/javase/8/docs/api/java/awt/Container.html) and [java.awt.Component](http://docs.oracle.com/javase/8/docs/api/java/awt/Component.html) +* [Apache Wicket](https://github.com/apache/wicket) component tree, see [Component](https://github.com/apache/wicket/blob/91e154702ab1ff3481ef6cbb04c6044814b7e130/wicket-core/src/main/java/org/apache/wicket/Component.java) and [MarkupContainer](https://github.com/apache/wicket/blob/b60ec64d0b50a611a9549809c9ab216f0ffa3ae3/wicket-core/src/main/java/org/apache/wicket/MarkupContainer.java) + +## 鸣谢 + +* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59) +* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b) +* [Refactoring to Patterns](https://www.amazon.com/gp/product/0321213351/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321213351&linkCode=as2&tag=javadesignpat-20&linkId=2a76fcb387234bc71b1c61150b3cc3a7) diff --git a/zh/converter/README.md b/zh/converter/README.md new file mode 100644 index 000000000..0b1f7a074 --- /dev/null +++ b/zh/converter/README.md @@ -0,0 +1,101 @@ +--- +layout: pattern +title: Converter +folder: converter +permalink: /patterns/converter/ +categories: Creational +tags: + - Decoupling +--- + +## 目的 + +转换器模式的目的是提供相应类型之间双向转换的通用方法,允许进行干净的实现,而类型之间无需相互了解。此外,Converter模式引入了双向集合映射,从而将样板代码减少到最少。 + +## 解释 + +真实世界例子 + +> 在真实的应用中经常有这种情况,数据库层包含需要被转换成业务逻辑层DTO来使用的实体。对于潜在的大量类进行类似的映射,我们需要一种通用的方法来实现这一点。 + +通俗的说 + +> 转换器模式让一个类的实例映射成另一个类的实例变得简单 + +**程序示例** + +我们需要一个通用的方案来解决映射问题。让我们来介绍一个通用的转换器。 + +```java +public class Converter { + + private final Function fromDto; + private final Function fromEntity; + + public Converter(final Function fromDto, final Function fromEntity) { + this.fromDto = fromDto; + this.fromEntity = fromEntity; + } + + public final U convertFromDto(final T dto) { + return fromDto.apply(dto); + } + + public final T convertFromEntity(final U entity) { + return fromEntity.apply(entity); + } + + public final List createFromDtos(final Collection dtos) { + return dtos.stream().map(this::convertFromDto).collect(Collectors.toList()); + } + + public final List createFromEntities(final Collection entities) { + return entities.stream().map(this::convertFromEntity).collect(Collectors.toList()); + } +} +``` + +专属的转换器像下面一样从基类继承。 + +```java +public class UserConverter extends Converter { + + public UserConverter() { + super(UserConverter::convertToEntity, UserConverter::convertToDto); + } + + private static UserDto convertToDto(User user) { + return new UserDto(user.getFirstName(), user.getLastName(), user.isActive(), user.getUserId()); + } + + private static User convertToEntity(UserDto dto) { + return new User(dto.getFirstName(), dto.getLastName(), dto.isActive(), dto.getEmail()); + } + +} +``` + +现在,在User和UserDto之间的映射变得轻而易举。 + +```java +var userConverter = new UserConverter(); +var dtoUser = new UserDto("John", "Doe", true, "whatever[at]wherever.com"); +var user = userConverter.convertFromDto(dtoUser); +``` + +## 类图 + +![alt text](../../converter/etc/converter.png "Converter Pattern") + +## 适用性 + +在下面这些情况下使用转换器模式: + +* 如果你的类型在逻辑上相互对应,并需要在它们之间转换实体 +* 当你想根据上下文提供不同的类型转换方式时 +* 每当你引入DTO(数据传输对象)时你可能都需要将其转换为 + DO + +## 鸣谢 + +* [Converter](http://www.xsolve.pl/blog/converter-pattern-in-java-8/) diff --git a/zh/dao/README.md b/zh/dao/README.md new file mode 100644 index 000000000..a16d4c7d6 --- /dev/null +++ b/zh/dao/README.md @@ -0,0 +1,162 @@ +--- +layout: pattern +title: Data Access Object +folder: dao +permalink: /patterns/dao/ +categories: Architectural +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 getAll() throws Exception; + + Optional getById(int id) throws Exception; + + boolean add(Customer customer) throws Exception; + + boolean update(Customer customer) throws Exception; + + boolean delete(Customer customer) throws Exception; +} + +public class InMemoryCustomerDao implements CustomerDao { + + private final Map idToCustomer = new HashMap<>(); + + // implement the interface using the map + ... +} + +public class DbCustomerDao implements CustomerDao { + + private static final Logger LOGGER = LoggerFactory.getLogger(DbCustomerDao.class); + + private final DataSource dataSource; + + public DbCustomerDao(DataSource dataSource) { + this.dataSource = dataSource; + } + + // implement the interface using the data source + ... +``` + +最后,这是我们使用DAO管理客户数据的方式。 + +```java + final var dataSource = createDataSource(); + createSchema(dataSource); + final var customerDao = new DbCustomerDao(dataSource); + + addCustomers(customerDao); + log.info(ALL_CUSTOMERS); + try (var customerStream = customerDao.getAll()) { + customerStream.forEach((customer) -> log.info(customer.toString())); + } + log.info("customerDao.getCustomerById(2): " + customerDao.getById(2)); + final var customer = new Customer(4, "Dan", "Danson"); + customerDao.add(customer); + log.info(ALL_CUSTOMERS + customerDao.getAll()); + customer.setFirstName("Daniel"); + customer.setLastName("Danielson"); + customerDao.update(customer); + log.info(ALL_CUSTOMERS); + try (var customerStream = customerDao.getAll()) { + customerStream.forEach((cust) -> log.info(cust.toString())); + } + customerDao.delete(customer); + log.info(ALL_CUSTOMERS + customerDao.getAll()); + + deleteSchema(dataSource); +``` + +程序输出: + +```java +customerDao.getAllCustomers(): +Customer{id=1, firstName='Adam', lastName='Adamson'} +Customer{id=2, firstName='Bob', lastName='Bobson'} +Customer{id=3, firstName='Carl', lastName='Carlson'} +customerDao.getCustomerById(2): Optional[Customer{id=2, firstName='Bob', lastName='Bobson'}] +customerDao.getAllCustomers(): java.util.stream.ReferencePipeline$Head@7cef4e59 +customerDao.getAllCustomers(): +Customer{id=1, firstName='Adam', lastName='Adamson'} +Customer{id=2, firstName='Bob', lastName='Bobson'} +Customer{id=3, firstName='Carl', lastName='Carlson'} +Customer{id=4, firstName='Daniel', lastName='Danielson'} +customerDao.getAllCustomers(): java.util.stream.ReferencePipeline$Head@2db0f6b2 +customerDao.getAllCustomers(): +Customer{id=1, firstName='Adam', lastName='Adamson'} +Customer{id=2, firstName='Bob', lastName='Bobson'} +Customer{id=3, firstName='Carl', lastName='Carlson'} +customerDao.getCustomerById(2): Optional[Customer{id=2, firstName='Bob', lastName='Bobson'}] +customerDao.getAllCustomers(): java.util.stream.ReferencePipeline$Head@12c8a2c0 +customerDao.getAllCustomers(): +Customer{id=1, firstName='Adam', lastName='Adamson'} +Customer{id=2, firstName='Bob', lastName='Bobson'} +Customer{id=3, firstName='Carl', lastName='Carlson'} +Customer{id=4, firstName='Daniel', lastName='Danielson'} +customerDao.getAllCustomers(): java.util.stream.ReferencePipeline$Head@6ec8211c +``` + +## 类图 + +![alt text](../../dao/etc/dao.png "Data Access Object") + +## 适用性 + +在以下情况下,请使用数据访问对象:: + +* 当您要巩固如何访问数据层时。 +* 当您要避免编写多个数据检索/持久层时。 + +## 鸣谢 + +* [J2EE Design Patterns](https://www.amazon.com/gp/product/0596004273/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596004273&linkCode=as2&tag=javadesignpat-20&linkId=48d37c67fb3d845b802fa9b619ad8f31) diff --git a/zh/data-transfer-object/README.md b/zh/data-transfer-object/README.md new file mode 100644 index 000000000..e7895f683 --- /dev/null +++ b/zh/data-transfer-object/README.md @@ -0,0 +1,111 @@ +--- +layout: pattern +title: Data Transfer Object +folder: data-transfer-object +permalink: /patterns/data-transfer-object/ +categories: Architectural +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 customers; + + public CustomerResource(List customers) { + this.customers = customers; + } + + public List getAllCustomers() { + return customers; + } + + public void save(CustomerDto customer) { + customers.add(customer); + } + + public void delete(String customerId) { + customers.removeIf(customer -> customer.getId().equals(customerId)); + } +} +``` + +现在拉取客户信息变得简单自从我们有了DTOs。 + +```java + var allCustomers = customerResource.getAllCustomers(); + allCustomers.forEach(customer -> LOGGER.info(customer.getFirstName())); + // Kelly + // Alfonso +``` + +## 类图 + +![alt text](../../data-transfer-object/etc/data-transfer-object.urm.png "data-transfer-object") + +## 适用性 + +使用数据传输对象模式当 + +* 客户端请求多种信息。信息都是相关的 +* 当你想提高获取资源的性能 +* 你想降低远程方法调用的次数 + +## 鸣谢 + +* [Design Pattern - Transfer Object Pattern](https://www.tutorialspoint.com/design_pattern/transfer_object_pattern.htm) +* [Data Transfer Object](https://msdn.microsoft.com/en-us/library/ff649585.aspx) +* [J2EE Design Patterns](https://www.amazon.com/gp/product/0596004273/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596004273&linkCode=as2&tag=javadesignpat-20&linkId=f27d2644fbe5026ea448791a8ad09c94) +* [Patterns of Enterprise Application Architecture](https://www.amazon.com/gp/product/0321127420/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321127420&linkCode=as2&tag=javadesignpat-20&linkId=014237a67c9d46f384b35e10151956bd) diff --git a/zh/delegation/README.md b/zh/delegation/README.md new file mode 100644 index 000000000..83ac8f331 --- /dev/null +++ b/zh/delegation/README.md @@ -0,0 +1,29 @@ +--- +layout: pattern +title: Delegation +folder: delegation +permalink: /patterns/delegation/ +categories: Structural +tags: + - Decoupling +--- + +## 或称 +代理模式 + +## 目的 +它是一种让对象将某种行为向外部表达,但实际上将实现该行为的责任委托给关联对象的技术。 + +## 类图 +![alt text](../../delegation/etc/delegation.png "Delegate") + +## 适用性 +使用委托模式以实现以下目的 + +* 降低类的耦合性 +* 组件的行为相同,但是意识到这种情况将来可能会改变。 + +## 鸣谢 + +* [Delegate Pattern: Wikipedia ](https://en.wikipedia.org/wiki/Delegation_pattern) +* [Proxy Pattern: Wikipedia ](https://en.wikipedia.org/wiki/Proxy_pattern) diff --git a/zh/dirty-flag/README.md b/zh/dirty-flag/README.md new file mode 100644 index 000000000..f055b0174 --- /dev/null +++ b/zh/dirty-flag/README.md @@ -0,0 +1,29 @@ +--- +layout: pattern +title: Dirty Flag +folder: dirty-flag +permalink: /patterns/dirty-flag/ +categories: Behavioral +tags: + - Game programming + - Performance +--- + +## 或称 +* 是否脏 模式 + +## 目的 +避免昂贵资源的重新获取。资源保留其身份,保留在某些快速访问的存储中,并被重新使用以避免再次获取它们。 + +## 类图 +![alt text](../../dirty-flag/etc/dirty-flag.png "Dirty Flag") + +## 适用性 +在以下情况下使用脏标志模式 + +* 重复获取,初始化,释放相同资源所导致不必要的性能开销 + +## 鸣谢 + +* [Design Patterns: Dirty Flag](https://www.takeupcode.com/podcast/89-design-patterns-dirty-flag/) +* [J2EE Design Patterns](https://www.amazon.com/gp/product/0596004273/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596004273&linkCode=as2&tag=javadesignpat-20&linkId=48d37c67fb3d845b802fa9b619ad8f31) diff --git a/zh/interpreter/README.md b/zh/interpreter/README.md new file mode 100644 index 000000000..7d7910121 --- /dev/null +++ b/zh/interpreter/README.md @@ -0,0 +1,35 @@ +--- +layout: pattern +title: Interpreter +folder: interpreter +permalink: /patterns/interpreter/ +categories: Behavioral +tags: + - Gang of Four +--- + +## 目的 +给定一种语言,请定义其语法的表示形式,以及使用该表示形式来解释该语言中的句子的解释器。 + +## 类图 +![alt text](../../interpreter/etc/interpreter_1.png "Interpreter") + +## 适用性 +有一种要解释的语言时,请使用解释器模式,并且可以将语言中的语句表示为抽象语法树。解释器模式在以下情况下效果最佳 + +* 语法很简单。 对于复杂的语法,语法的类层次结构变得庞大且难以管理。 在这种情况下,解析器生成器之类的工具是更好的选择。 他们可以在不构建抽象语法树的情况下解释表达式,这可以节省空间并可能节省时间 +* 效率不是关键问题。 通常,最有效的解释器不是通过直接解释解析树来实现的,而是先将其转换为另一种形式。 例如,正则表达式通常会转换为状态机。 但是即使这样,翻译器也可以通过解释器模式实现,因此该模式仍然适用。 + +## 真实世界例子 + +* [java.util.Pattern](http://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html) +* [java.text.Normalizer](http://docs.oracle.com/javase/8/docs/api/java/text/Normalizer.html) +* All subclasses of [java.text.Format](http://docs.oracle.com/javase/8/docs/api/java/text/Format.html) +* [javax.el.ELResolver](http://docs.oracle.com/javaee/7/api/javax/el/ELResolver.html) + + +## 鸣谢 + +* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59) +* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b) +* [Refactoring to Patterns](https://www.amazon.com/gp/product/0321213351/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321213351&linkCode=as2&tag=javadesignpat-20&linkId=2a76fcb387234bc71b1c61150b3cc3a7) diff --git a/zh/private-class-data/README.md b/zh/private-class-data/README.md new file mode 100644 index 000000000..735ca93f6 --- /dev/null +++ b/zh/private-class-data/README.md @@ -0,0 +1,128 @@ +--- +layout: pattern +title: Private Class Data +folder: private-class-data +permalink: /patterns/private-class-data/ +categories: Idiom +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 +``` + +## 类图 + +![alt text](../../private-class-data/etc/private-class-data.png "Private Class Data") + +## 适用性 + +在以下情况下使用私有类数据模式 + +* 您要阻止对类数据成员的写访问。 diff --git a/zh/producer-consumer/README.md b/zh/producer-consumer/README.md new file mode 100644 index 000000000..80ffeb9b9 --- /dev/null +++ b/zh/producer-consumer/README.md @@ -0,0 +1,21 @@ +--- +layout: pattern +title: Producer Consumer +folder: producer-consumer +permalink: /patterns/producer-consumer/ +categories: Concurrency +tags: + - Reactive +--- + +## 目的 +生产者消费者设计模式是一种经典的并发模式,通过将工作与执行工作任务分开来减少生产者与消费者之间的耦合。 + +## 类图 +![alt text](../../producer-consumer/etc/producer-consumer.png "Producer Consumer") + +## 适用性 +在以下情况下使用生产者消费者 + +* 通过将工作分成生产和消费两个工作进程来解耦系统 +* 解决生产工作和消费工作需要不同时间的问题 diff --git a/zh/proxy/README.md b/zh/proxy/README.md new file mode 100644 index 000000000..50f7575be --- /dev/null +++ b/zh/proxy/README.md @@ -0,0 +1,161 @@ +--- +layout: pattern +title: Proxy +folder: proxy +permalink: /patterns/proxy/ +categories: Structural +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! +``` + +## 类图 + +![alt text](../../proxy/etc/proxy.urm.png "Proxy pattern class diagram") + +## 适用性 + +代理适用于需要比简单指针更广泛或更复杂的对象引用的情况。这是代理模式适用的几种常见情况。 + +* 远程代理为不同地址空间中的对象提供了本地代表。 +* 虚拟代理根据需要创建昂贵的对象。 +* 保护代理控制对原始对象的访问。当对象有不同的接入权限时保护代理很有用。 + +## 典型用例 + +* 对象的访问控制 +* 懒加载 +* 实现日志记录 +* 简化网络连接 +* 对象的访问计数 + +## 教程 + +* [Controlling Access With Proxy Pattern](http://java-design-patterns.com/blog/controlling-access-with-proxy-pattern/) + +## 已知使用 + +* [java.lang.reflect.Proxy](http://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Proxy.html) +* [Apache Commons Proxy](https://commons.apache.org/proper/commons-proxy/) +* Mocking frameworks [Mockito](https://site.mockito.org/), +[Powermock](https://powermock.github.io/), [EasyMock](https://easymock.org/) + +## 相关设计模式 + +* [Ambassador](https://java-design-patterns.com/patterns/ambassador/) + +## 鸣谢 + +* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59) +* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b) diff --git a/zh/state/README.md b/zh/state/README.md index 6b46510e0..e222ac4d1 100644 --- a/zh/state/README.md +++ b/zh/state/README.md @@ -134,7 +134,7 @@ public class Mammoth { ``` ## 类图 -![alt text](../../state/etc/state_1.png "State") +![alt text](../../state/etc/state_urm.png "State") ## 适用性 diff --git a/zh/template-method/README.md b/zh/template-method/README.md index f848552c3..54d6fd1a1 100644 --- a/zh/template-method/README.md +++ b/zh/template-method/README.md @@ -118,7 +118,7 @@ public class HalflingThief { ``` ## 类图 -![alt text](../../template-method/etc/template-method_1.png "Template Method") +![alt text](../../template-method/etc/template_method_urm.png "Template Method") ## 适用性 diff --git a/zh/visitor/README.md b/zh/visitor/README.md new file mode 100644 index 000000000..74afe1a14 --- /dev/null +++ b/zh/visitor/README.md @@ -0,0 +1,224 @@ +--- +layout: pattern +title: Visitor +folder: visitor +permalink: /patterns/visitor/ +categories: Behavioral +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 + +![alt text](../../visitor/etc/visitor_1.png "Visitor") + +## 适用性 + +使用访问者模式当 + +* 对象结构包含许多具有不同接口的对象类,并且你希望根据这些对象的具体类对这些对象执行操作。 +* 需要对对象结构中的对象执行许多不同且不相关的操作,并且你想避免使用这些操作“污染”它们的类。 访问者可以通过在一个类中定义相关操作来将它们保持在一起。当许多应用程序共享对象结构时,请使用访问者模式将操作仅放在需要它们的那些应用程序中 +* 定义对象结构的类很少变化,但是你经常想在结构上定义新的操作。更改对象结构类需要重新定义所有访问者的接口,这可能会导致成本高昂。如果对象结构类经常更改,则最好在这些类中定义操作。 + +## 真实例子 + +* [Apache Wicket](https://github.com/apache/wicket) component tree, see [MarkupContainer](https://github.com/apache/wicket/blob/b60ec64d0b50a611a9549809c9ab216f0ffa3ae3/wicket-core/src/main/java/org/apache/wicket/MarkupContainer.java) +* [javax.lang.model.element.AnnotationValue](http://docs.oracle.com/javase/8/docs/api/javax/lang/model/element/AnnotationValue.html) and [AnnotationValueVisitor](http://docs.oracle.com/javase/8/docs/api/javax/lang/model/element/AnnotationValueVisitor.html) +* [javax.lang.model.element.Element](http://docs.oracle.com/javase/8/docs/api/javax/lang/model/element/Element.html) and [Element Visitor](http://docs.oracle.com/javase/8/docs/api/javax/lang/model/element/ElementVisitor.html) +* [java.nio.file.FileVisitor](http://docs.oracle.com/javase/8/docs/api/java/nio/file/FileVisitor.html) + +## 鸣谢 + +* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59) +* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b) +* [Refactoring to Patterns](https://www.amazon.com/gp/product/0321213351/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321213351&linkCode=as2&tag=javadesignpat-20&linkId=2a76fcb387234bc71b1c61150b3cc3a7)