From 44d38436d866e2d2a11c58872d0449e951632ef5 Mon Sep 17 00:00:00 2001 From: "bbwang1992@foxmail.com" Date: Sat, 11 Mar 2023 09:49:48 +0800 Subject: [PATCH] #add changer1 chinese edition --- .../README.md" | 131 ++++++++++++ .../README.md" | 187 ++++++++++++++++++ README.md | 6 +- 3 files changed, 323 insertions(+), 1 deletion(-) create mode 100644 "1-1-\345\267\245\345\216\202\346\250\241\345\274\217/README.md" create mode 100644 "2-2-\346\212\275\350\261\241\345\267\245\345\216\202\346\250\241\345\274\217/README.md" diff --git "a/1-1-\345\267\245\345\216\202\346\250\241\345\274\217/README.md" "b/1-1-\345\267\245\345\216\202\346\250\241\345\274\217/README.md" new file mode 100644 index 0000000..2b6c12e --- /dev/null +++ "b/1-1-\345\267\245\345\216\202\346\250\241\345\274\217/README.md" @@ -0,0 +1,131 @@ +# 工厂模式 🏭 + +_**本文最初发表于此 [博客](shubhamzanwar.com/blog)**_ + +工厂模式是一种常用的创建型设计模式。 用户通常使用它在多个选项中进行选择。让我们举个例子来理解。 + +### 宠物店 + +让我们以一个在宠物店如何工作的场景为例。为了完全理解这一点,我们将从店主(创建工厂的“开发人员”)和客户(使用接口的“用户”)两个角度来观察实现方式。 + +#### 店主视角 + +假设你是一家狗店的老板(你只把小狗送人领养)。因为你是在软件世界中,每条狗都是你拥有的`Dog `类的一个实例。现在,当客户到来时,您只需创建一个新的`Dog`实例,并让他们领养。🐶 + +然而,最近顾客开始要求多样化。他们也在寻找收养猫的选择。😼 + +作为一个聪明的店主,你已经意识到这种需求只会变得越来越多样化。人们将继续期待更多的变化。😨😤 + +**_你需要一个健壮的、可扩展的系统来为客户生成新的宠物_** +进入,工厂模式 + +你列出宠物的所有共同特征。它们可以让你知道它们的名字,它们发出的声音和它们的年龄。该列表允许您创建具有以下功能的接口: + +```go +type Pet interface { + GetName() string + GetAge() int + GetSound() string +} +``` + +现在,你可以创建任意数量具有相同功能的宠物(“实现相同的接口”)。你可以养猫,狗,鱼,鹦鹉,任何东西-只要实现`Pet`接口!😯 现在,让我们创建狗和猫: + +```go +// pet is a struct that implements Pet interface and +// would be used in any animal struct that we create. +// See `Dog` and `Cat` below +type pet struct { + name string + age int + sound string +} + +func (p *pet) GetName() string { + return p.name +} + +func (p *pet) GetSound() string { + return p.sound +} + +func (p *pet) GetAge() int { + return p.age +} + +type Dog struct { + pet +} + +type Cat struct { + pet +} +``` + +你还需要一个工厂,它会根据用户的请求返回不同的宠物(狗/猫)。简单地说,如果用户想要一只狗,就给他们一只可爱的狗。🙄🦮 + +```go +func GetPet(petType string) Pet { + if petType == "dog" { + return &Dog{ + pet{ + name: "Chester", + age: 2, + sound: "bark", + }, + } + } + + if petType == "cat" { + return &Cat{ + pet{ + name: "Mr. Buttons", + age: 3, + sound: "meow", + }, + } + } + + return nil +} +``` + +注意`GetPet`'函数如何只告诉它返回`Pet`-而不是显式地返回` Dog `或`Cat`。因此,这个函数是开放扩展的(通过编写更多结构图实现 `Pet `接口)。增加更多的`Pet`类型不会影响现有用户只想要`Dog`。恭喜你!你已经使用工厂模式创建了一个宠物商店。🎉❤️ + +#### 客户视角 + +让我们从用户的角度来看这个问题。他们所需要做的就是用他们想要的任何配置(在本例中为`type`)调用`GetPet`函数。通过返回值,他们只知道他们得到了一个`pet`。🤔这在现实世界的意义上可能听起来很奇怪,但在代码方面,最好保持抽象。😌 + +用户可以随心所欲地“使用”`Pet`。不管他们得到的是什么类型的宠物,这种“用法”都是一样的(因为所有的宠物都实现了公共接口!!) + +让我们来测试一下 + +```go +func describePet(pet Pet) string { + return fmt.Sprintf("%s is %d years old. Its sound is %s", pet.GetName(), pet.GetAge(), pet.GetSound()) +} + +func main() { + petType := "dog" + + dog := GetPet(petType) + petDescription := describePet(dog) + + fmt.Println(petDescription) + fmt.Println("-------------") + + petType = "cat" + cat := GetPet(petType) + petDescription = describePet(cat) + + fmt.Println(petDescription) +} +``` + +输出应该如下所示: + +```text +Chester is 2 years old. Its sound is bark +------------- +Mr. Buttons is 3 years old. Its sound is meow +``` diff --git "a/2-2-\346\212\275\350\261\241\345\267\245\345\216\202\346\250\241\345\274\217/README.md" "b/2-2-\346\212\275\350\261\241\345\267\245\345\216\202\346\250\241\345\274\217/README.md" new file mode 100644 index 0000000..d9e4a4c --- /dev/null +++ "b/2-2-\346\212\275\350\261\241\345\267\245\345\216\202\346\250\241\345\274\217/README.md" @@ -0,0 +1,187 @@ +# 抽象工厂模式 🏭 + +抽象工厂模式是一种创建型的设计模式。当我们需要创建多个类似产品家族时,就会使用它。 +让我们以披萨连锁店为例来理解这种模式。 + +### 披萨店 + +假设你是一家在镇上开披萨店的老板。其中一项职责是保证某家店的所有食品(在本例中是披萨和蒜蓉面包)属于同一个品牌——要么是达美乐比萨,要么是必胜客。 + +有很多种方法可以做到。想到的最简单的方法之一是创建一个工厂来生产必胜客或达美乐类型的披萨,以及另一个类似的工厂来生产大蒜面包。 + +> 注:如果你不确定正常的工厂是如何运作的,你可以去看看[这个例子](https://github.com/coolbook/design-patterns/tree/master/1-1-%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F) + +拥有独立工厂的缺点是,现在我们相信,在给定的商店里,用户会选择他们想要的正确类型的披萨和蒜蓉面包。如果他们犯了一个错误,把达美乐披萨和必胜客蒜蓉面包放在一起供应,你的顾客会很生气,你也违反了你与这些连锁店签订的合同。 + +别担心。 **_有一个更简单的方法_** + +你可以为每个单独的品牌创建一个工厂,而不是为每个单独的产品(披萨和大蒜面包)创建一个工厂。强制这些工厂提供“制作披萨”和“制作蒜蓉面包”的条款。 + +然后,在建立商店时,你可以给商店经理一个必胜客工厂或达美乐工厂,然后自信地相信他们不会不小心混合搭配产品。 + +让我们把它转换成代码: + +在我们开始编写工厂之前,让我们先创建产品: + +##### 通用的披萨 + +```go +type iPizza interface { + GetPrice() float64 + GetName() string + GetToppings() []string +} + +type pizza struct { + name string + price float64 + toppings []string +} + +func (p *pizza) GetName() string { + return p.name +} + +func (p *pizza) GetPrice() float64 { + return p.price +} + +func (p *pizza) GetToppings() []string { + return p.toppings +} +``` + +##### 品牌的披萨 + +```go +type pizzaHutPizza struct { + pizza +} + +type dominosPizza struct { + pizza +} +``` + +##### 通用的蒜蓉面包 + +```go +type iGarlicBread interface { + GetPrice() float64 + GetName() string +} + +type garlicBread struct { + name string + price float64 +} + +func (g *garlicBread) GetName() string { + return g.name +} + +func (g *garlicBread) GetPrice() float64 { + return g.price +} +``` + +##### 品牌的蒜蓉面包 + +```go +type pizzaHutGarlicBread struct { + garlicBread +} + +type dominosGarlicBread struct { + garlicBread +} +``` + +我们创造了这两种产品。它们都实现了一个公共接口,使得最终用户更容易使用它们。双关语 😉 + +现在让我们看看如何为这些产品创建工厂: + +##### 通用工厂 + +```go +type iPizzaFactory interface { + createPizza() iPizza + createGarlicBread() iGarlicBread +} +``` + +现在,`PizzaHutFactory `和`DominosFactory`都可以实现这个接口,以确保它们公开统一的功能 + +##### 品牌工厂 + +```go +type PizzaHutFactory struct {} + +func (p *PizzaHutFactory) createPizza() iPizza { + return &pizzaHutPizza{ + pizza{ + name: "pepperoni", + price: 230.3, + toppings: []string{"olives", "mozzarella", "pork"}, + }, + } +} + +func (p *pizzaHutFactory) createGarlicBread() iGarlicBread { + return &pizzaHutGarlicBread{ + garlicBread{ + name: "garlic bread", + price: 180.99, + }, + } +} +``` + +```go +type dominosFactory struct{} + +func (d *dominosFactory) createPizza() iPizza { + return &dominosPizza{ + pizza{ + name: "margherita", + price: 200.5, + toppings: []string{"tomatoes", "basil", "olive oil"}, + }, + } +} + +func (d *dominosFactory) createGarlicBread() iGarlicBread { + return &dominosGarlicBread{ + garlicBread{ + name: "cheesy bread sticks", + price: 150.00, + }, + } +} +``` + +现在我们可以在这两家工厂中任选一家,然后继续生产披萨或蒜蓉面包,而且绝对可以确保一家工厂生产的任何产品都属于同一个家族/品牌。 + +**_我们就快完成了_**。让我们通过创建一个将返回我们所选择的工厂的工厂来结束它。困惑吗?🤯花一分钟时间再读一遍这个句子 😋 + +基本上,把我们的工厂想象成另一个对象。现在,根据我们想要建立的类型或比萨饼店(必胜客或达美乐),我们可以请求特定的工厂(只是另一个对象)。为了自动获取这些“对象”,我们可以创建另一个工厂,该工厂将返回这些对象中的一个。 + +一些代码可以帮助你: + +##### 工厂的工厂 + +```go +func getPizzaFactory(chain string) (iPizzaFactory, error) { + if chain == "P" { + return &pizzaHutFactory{}, nil + } + if chain == "D" { + return &dominosFactory{}, nil + } + return nil, fmt.Errorf("Enter a valid chain type next time") +} +``` + +也许这样更有意义 💡😁 + +要记住的要点是:抽象工厂模式实现了工厂的工厂。这些内部工厂可以用来制造属于特定种类的产品。 diff --git a/README.md b/README.md index 3f2c290..2952611 100644 --- a/README.md +++ b/README.md @@ -22,4 +22,8 @@ In this repository, I have curated a list of design patterns that may help you i 8. [Flyweight Pattern](./8-flyweight) 9. [Chain of responsibility](./9-chain-of-responsibility) -wip +This is the Chinese Edition, thanks to the contribution of [coolbook](https://github.com/coolbook/). +1. [工厂模式](./1-1-工厂模式) +1. [抽象工厂模式](./2-2-抽象工厂模式) + +WIP