|
| 1 | +# 工厂模式 🏭 |
| 2 | + |
| 3 | +_**本文最初发表于此 [博客](shubhamzanwar.com/blog)**_ |
| 4 | + |
| 5 | +工厂模式是一种常用的创建型设计模式。 用户通常使用它在多个选项中进行选择。让我们举个例子来理解。 |
| 6 | + |
| 7 | +### 宠物店 |
| 8 | + |
| 9 | +让我们以一个在宠物店如何工作的场景为例。为了完全理解这一点,我们将从店主(创建工厂的“开发人员”)和客户(使用接口的“用户”)两个角度来观察实现方式。 |
| 10 | + |
| 11 | +#### 店主视角 |
| 12 | + |
| 13 | +假设你是一家狗店的老板(你只把小狗送人领养)。因为你是在软件世界中,每条狗都是你拥有的`Dog `类的一个实例。现在,当客户到来时,您只需创建一个新的`Dog`实例,并让他们领养。🐶 |
| 14 | + |
| 15 | +然而,最近顾客开始要求多样化。他们也在寻找收养猫的选择。😼 |
| 16 | + |
| 17 | +作为一个聪明的店主,你已经意识到这种需求只会变得越来越多样化。人们将继续期待更多的变化。😨😤 |
| 18 | + |
| 19 | +**_你需要一个健壮的、可扩展的系统来为客户生成新的宠物_** |
| 20 | +进入,工厂模式 |
| 21 | + |
| 22 | +你列出宠物的所有共同特征。它们可以让你知道它们的名字,它们发出的声音和它们的年龄。该列表允许您创建具有以下功能的接口: |
| 23 | + |
| 24 | +```go |
| 25 | +type Pet interface { |
| 26 | + GetName() string |
| 27 | + GetAge() int |
| 28 | + GetSound() string |
| 29 | +} |
| 30 | +``` |
| 31 | + |
| 32 | +现在,你可以创建任意数量具有相同功能的宠物(“实现相同的接口”)。你可以养猫,狗,鱼,鹦鹉,任何东西-只要实现`Pet`接口!😯 现在,让我们创建狗和猫: |
| 33 | + |
| 34 | +```go |
| 35 | +// pet is a struct that implements Pet interface and |
| 36 | +// would be used in any animal struct that we create. |
| 37 | +// See `Dog` and `Cat` below |
| 38 | +type pet struct { |
| 39 | + name string |
| 40 | + age int |
| 41 | + sound string |
| 42 | +} |
| 43 | + |
| 44 | +func (p *pet) GetName() string { |
| 45 | + return p.name |
| 46 | +} |
| 47 | + |
| 48 | +func (p *pet) GetSound() string { |
| 49 | + return p.sound |
| 50 | +} |
| 51 | + |
| 52 | +func (p *pet) GetAge() int { |
| 53 | + return p.age |
| 54 | +} |
| 55 | + |
| 56 | +type Dog struct { |
| 57 | + pet |
| 58 | +} |
| 59 | + |
| 60 | +type Cat struct { |
| 61 | + pet |
| 62 | +} |
| 63 | +``` |
| 64 | + |
| 65 | +你还需要一个工厂,它会根据用户的请求返回不同的宠物(狗/猫)。简单地说,如果用户想要一只狗,就给他们一只可爱的狗。🙄🦮 |
| 66 | + |
| 67 | +```go |
| 68 | +func GetPet(petType string) Pet { |
| 69 | + if petType == "dog" { |
| 70 | + return &Dog{ |
| 71 | + pet{ |
| 72 | + name: "Chester", |
| 73 | + age: 2, |
| 74 | + sound: "bark", |
| 75 | + }, |
| 76 | + } |
| 77 | + } |
| 78 | + |
| 79 | + if petType == "cat" { |
| 80 | + return &Cat{ |
| 81 | + pet{ |
| 82 | + name: "Mr. Buttons", |
| 83 | + age: 3, |
| 84 | + sound: "meow", |
| 85 | + }, |
| 86 | + } |
| 87 | + } |
| 88 | + |
| 89 | + return nil |
| 90 | +} |
| 91 | +``` |
| 92 | + |
| 93 | +注意`GetPet`'函数如何只告诉它返回`Pet`-而不是显式地返回` Dog `或`Cat`。因此,这个函数是开放扩展的(通过编写更多结构图实现 `Pet `接口)。增加更多的`Pet`类型不会影响现有用户只想要`Dog`。恭喜你!你已经使用工厂模式创建了一个宠物商店。🎉❤️ |
| 94 | + |
| 95 | +#### 客户视角 |
| 96 | + |
| 97 | +让我们从用户的角度来看这个问题。他们所需要做的就是用他们想要的任何配置(在本例中为`type`)调用`GetPet`函数。通过返回值,他们只知道他们得到了一个`pet`。🤔这在现实世界的意义上可能听起来很奇怪,但在代码方面,最好保持抽象。😌 |
| 98 | + |
| 99 | +用户可以随心所欲地“使用”`Pet`。不管他们得到的是什么类型的宠物,这种“用法”都是一样的(因为所有的宠物都实现了公共接口!!) |
| 100 | + |
| 101 | +让我们来测试一下 |
| 102 | + |
| 103 | +```go |
| 104 | +func describePet(pet Pet) string { |
| 105 | + return fmt.Sprintf("%s is %d years old. Its sound is %s", pet.GetName(), pet.GetAge(), pet.GetSound()) |
| 106 | +} |
| 107 | + |
| 108 | +func main() { |
| 109 | + petType := "dog" |
| 110 | + |
| 111 | + dog := GetPet(petType) |
| 112 | + petDescription := describePet(dog) |
| 113 | + |
| 114 | + fmt.Println(petDescription) |
| 115 | + fmt.Println("-------------") |
| 116 | + |
| 117 | + petType = "cat" |
| 118 | + cat := GetPet(petType) |
| 119 | + petDescription = describePet(cat) |
| 120 | + |
| 121 | + fmt.Println(petDescription) |
| 122 | +} |
| 123 | +``` |
| 124 | + |
| 125 | +输出应该如下所示: |
| 126 | + |
| 127 | +```text |
| 128 | +Chester is 2 years old. Its sound is bark |
| 129 | +------------- |
| 130 | +Mr. Buttons is 3 years old. Its sound is meow |
| 131 | +``` |
0 commit comments