1 引例
接上文Golang设计模式-创建型-工厂方法 ,有一天,李雷听说韩梅梅不但喜欢吃汉堡,还喜欢吃鸡翅,怎么办呢?让工厂的Create方法既生产汉堡,又生产鸡翅吗?小皮说:不好不好,这违反了“单一职责”原则,生产汉堡与生产鸡翅耦合在一起,任何一种方法发生变更都会毫无必要地影响另外一种产品的生产,有没有更好的办法呢?当然有,且听小皮道来…
2 抽象工厂
2.1 定义
抽象工厂在工厂方法的基础上引入了“产品簇”的概念,简单理解,工厂方法只生产一种产品,抽象工厂则生产多种产品。
2.2 抽象工厂三元素
2.2.1 产品
抽象工厂与简单工厂和工厂方法最显著的差别就是抽象工厂的产品有多种,如引例的汉堡和鸡翅。
产品一:汉堡,具体包括肯德基的汉堡和麦当劳的汉堡:
// product type one: hamburger
type Hamburger interface {
Deliver()
}
type KfcHamburger struct{}
func (h KfcHamburger) Deliver() {
fmt.Println("This is a hamburger from KFC.")
}
type McdonaldsHamburger struct{}
func (h McdonaldsHamburger) Deliver() {
fmt.Println("This is a hamburger from McDonalds.")
}
产品二:鸡翅,也派生出肯德基和麦当劳的鸡翅:
// product type two: chicken wing
type ChickenWing interface {
Deliver()
}
type KfcChickenWing struct{}
func (wing KfcChickenWing) Deliver() {
fmt.Println("This is a chicken wing from KFC.")
}
type McdonaldsChickenWing struct{}
func (wing McdonaldsChickenWing) Deliver() {
fmt.Println("This is a chicken wing from McDonalds.")
}
2.2.2 工厂
抽象工厂需要生产两张产品,因此我们分别定义生产汉堡和生产鸡翅的接口如下:
// abstract factory
type FastFoodFactory interface {
CreateHamburger() Hamburger
CreateChickenWing() ChickenWing
}
具体工厂一:肯德基,负责生产肯德基的汉堡和鸡翅:
// concrete factory one: KFC
type Kfc struct{}
func (factory Kfc) CreateHamburger() Hamburger {
return new(KfcHamburger)
}
func (factory Kfc) CreateChickenWing() ChickenWing {
return new(KfcChickenWing)
}
具体工厂二:麦当劳,负责生产麦当劳的汉堡和鸡翅:
// concrete factory two: McDonalds
type Mcdonalds struct{}
func (factory Mcdonalds) CreateHamburger() Hamburger {
return new(McdonaldsHamburger)
}
func (factory Mcdonalds) CreateChickenWing() ChickenWing {
return new(McdonaldsChickenWing)
}
2.2.3 客户端
抽象工厂的客户端与工厂方法几乎没有差别,仅需要指定工厂类型,就可以得到最终想要的具体产品,只不过这一次的产品种类更多而已:
func main() {
prefer := GetPreferredFastFood()
var factory FastFoodFactory
switch prefer {
case "KFC":
factory = new(Kfc)
case "McDonalds":
factory = new(Mcdonalds)
default:
fmt.Printf("%s not supported yet.\n", prefer)
os.Exit(1)
}
hamburger := factory.CreateHamburger()
chickenWing := factory.CreateChickenWing()
hamburger.Deliver()
chickenWing.Deliver()
}
我们测试一下:
$ go build -o abstract_factory.bin abstract_factory.go
$ ll abstract_factory.bin
-rwxrwxr-x 1 pirlo pirlo 1681701 9月 2 22:12 abstract_factory.bin*
$ ./abstract_factory.bin -prefer KFC
This is a hamburger from KFC.
This is a chicken wing from KFC.
$ ./abstract_factory.bin -prefer KFC
This is a hamburger from KFC.
This is a chicken wing from KFC.
使用类图表示如下:
2.3 抽象工厂的优缺点
- 优点
- 与简单工厂和工厂方法相比,抽象工厂可以生产更多种类的产品,即产品形态更加多样化
- 不同种类的产品之间完全解耦,添加新产品时对已有产品实现没有任何影响,即符合“开放-封闭”原则
缺点
- 相比工厂方法,抽象工厂模式的继承关系更多,增加了系统的复杂性
- 添加新产品时,除了新增产品的继承关系外,还需要修改工厂基类和所有子类的逻辑,客户端也需要做相应适配,增加了系统扩展的难度。
完整代码:抽象工厂