一、什么是工厂模式
专门定义一个类来负责创建其他类的实例,根据参数的不同创建不同类的实例,被创建的实例通常具有共同的父类,这个模式叫简单工厂模式(Simple Factory Pattern)。
简单工厂模式又称为静态工厂方法模式。之所以叫“静态”,是因为在很多静态语言(如Java、C++)中方法通常被定义成一个静态(static)方法,这样便可通过类名来直接调用方法。
1.1根据剧情来体会
生活中咖啡机的使用也非常简单,咖啡机旁边有已经准备好的咖啡豆,想喝咖啡,只要往咖啡机里加入少量的咖啡豆,然后选择杯数和浓度,再按一下开关,10分钟后,带着浓香的咖啡就为你准备好了!当然,如果你想喝一些其他口味的咖啡,也可以自备咖啡豆,无论你要拿铁还是摩卡,都不是问题。那么问题来了,你要拿铁还是摩卡呢?
用python代码来演绎:
from abc import ABCMeta, abstractmethod
# 引入ABCMeta和abstractmethod来定义抽象类和抽象方法
class Coffee(metaclass=ABCMeta):
def __init__(self, name):
self.name = name
def getName(self):
return self.name
@abstractmethod
def getTaste(self):
pass
class LatteCaffe(Coffee):
"""拿铁咖啡"""
def __init__(self, name):
super().__init__(name)
def getTaste(self):
return "轻柔而香醇"
class MochaCoffee(Coffee):
"""摩卡咖啡"""
def __init_(self, name):
super().__init__(name)
def getTaste(self):
return "丝滑与醇厚"
class Coffeemaker:
"""咖啡机"""
@staticmethod
def makeCoffee(coffeeBean):
"""通过staticmethod装饰器修饰来定义一个静态方法。"""
if coffeeBean == "拿铁咖啡豆":
coffee = LatteCaffe("拿铁咖啡")
elif coffeeBean == "摩卡咖啡豆":
coffee = MochaCoffee("摩卡咖啡")
else:
raise ValueError("不支持的参数:%s", coffeeBean)
return coffee
def testCoffeeMaker():
latte = Coffeemaker.makeCoffee("拿铁咖啡豆")
print("%s己为您准各好了,口感:%s。请慢慢享用!" % (latte.getName(), latte.getTaste()))
mocha = Coffeemaker.makeCoffee("摩卡咖啡豆")
print("%s己为您准备好了,口感:%s。请慢慢享用!" % (mocha.getName(), mocha.getTaste()))
if __name__ == "__main__":
testCoffeeMaker()
用golang代码来演示:
package main
import (
"errors"
"fmt"
)
type abcCoffee interface {
GetName() string
GetTask() string
}
// Coffee 咖啡的抽象类
type Coffee struct {
Name string
}
func (c Coffee) GetName() string {
return c.Name
}
func (c Coffee) GetTask() string {
return ""
}
// LatteCoffee 拿铁咖啡
type LatteCoffee struct {
Coffee
}
func (l LatteCoffee) GetTask() string {
return "轻柔而香醇"
}
// MochaCoffee 摩卡咖啡
type MochaCoffee struct {
Coffee
}
func (m MochaCoffee) GetTask() string {
return "丝滑与醇厚"
}
// CoffeeMaker 咖啡机
type CoffeeMaker struct {
}
func (c CoffeeMaker) makeCoffee(coffeeBean string) (abcCoffee, error) {
if coffeeBean == "拿铁咖啡豆" {
coffee := LatteCoffee{Coffee{coffeeBean}}
return coffee, nil
}
if coffeeBean == "摩卡咖啡豆" {
coffee := MochaCoffee{Coffee{coffeeBean}}
return coffee, nil
}
return nil, errors.New(fmt.Sprintf("不支持的参数:%s", coffeeBean))
}
func main() {
cm := CoffeeMaker{}
if latte, err := cm.makeCoffee("拿铁咖啡豆"); err != nil {
fmt.Println(err)
} else {
fmt.Printf("%s 已经为您准备好了,口感:%s。请慢慢享用!\n", latte.GetName(), latte.GetTask())
}
if mocha, err := cm.makeCoffee("摩卡咖啡豆"); err != nil {
fmt.Println(err)
} else {
fmt.Printf("%s 已经为您准备好了,口感:%s。请慢慢享用!\n", mocha.GetName(), mocha.GetTask())
}
}
运行结果:
拿铁咖啡豆 已经为您准备好了,口感:轻柔而香醇。请慢慢享用!
摩卡咖啡豆 已经为您准备好了,口感:丝滑与醇厚。请慢慢享用!
1.2定义
简单工厂模式
定义一个创建对象(实例化对象)的接口,通过参数来决定创建哪个类的实例。
1.3 优缺点
优点:
(1)实现简单、结构清晰。
(2)抽象出一个专门的类来负责某类对象的创建,分割出创建的职责,不能直接创建具体的对象,只需传入适当的参数即可。
(3)使用者可以不关注具体对象的类名称,只需知道传入什么参数可以创建哪些需要的对象。
缺点:
(1)不易拓展,一旦添加新的产品类型,就不得不修改工厂的创建逻辑。不符合“开放封闭”原则,如果要增加或删除一个产品类型,就要修改switch...case...(或if...else...)的判断代码。
(2)当产品类型较多时,工厂的创建逻辑可能过于复杂,switch...case...(或if...else...)判断会变得非常多。一旦出错可能造成所有产品创建失败,不利于系统的维护。
1.4 应用场景
(1)产品具有明显的继承关系,且产品的类型不太多。
(2)所有的产品具有相同的方法和类似的属性,使用者不关心具体的类型,只希望传入合适的参数能返回合适的对象。
尽管简单工厂模式不符合“开放-封闭”原则,但因为它简单,所以仍然能在很多项目中看到它。
二、工厂方法模式
工厂方法模式是简单工厂模式的一个升级版本,为解决简单工厂模式不符合“开放-封闭”原则的问题,我们对 SimpleFactory 进行了一个拆分,抽象出一个父类 Factory,并增加多个子类分别负责创建不同的具体产品。
2.1 定义
定义一个创建对象(实例化对象)的接口,让子类来决定创建哪个类的实例。工厂方法使一个类的实例化延迟到其子类。
2.2 代码实例:
package main
import (
"errors"
"fmt"
)
type Coffee interface {
getName() string
getTask() string
}
type AbsCoffee struct {
Name string
}
func (c AbsCoffee) getName() string {
return c.Name
}
func (c AbsCoffee) getTask() string {
return ""
}
type LatteCoffee struct {
AbsCoffee
}
func (c LatteCoffee) getTask() string {
return "轻柔而香醇"
}
type MochaCoffee struct {
AbsCoffee
}
func (c MochaCoffee) getTask() string {
return "丝滑与醇厚"
}
type AbsCoffeeMaker struct {
}
func (m AbsCoffeeMaker) makeCoffee() (Coffee, error) {
return AbsCoffee{}, errors.New("空的咖啡杯")
}
type LatteCoffeeMaker struct {
AbsCoffeeMaker
}
func (c LatteCoffeeMaker) makeCoffee() (Coffee, error) {
return LatteCoffee{AbsCoffee{"拿铁咖啡"}}, nil
}
type MochaCoffeeMaker struct {
AbsCoffeeMaker
}
func (c MochaCoffeeMaker) makeCoffee() (Coffee, error) {
return MochaCoffee{AbsCoffee{"摩卡咖啡"}}, nil
}
func main() {
lcm := LatteCoffeeMaker{}
if latte, err := lcm.makeCoffee(); err != nil {
fmt.Println(err)
} else {
fmt.Printf("%s 已经为您准备好了,口感:%s。请慢慢享用!\n", latte.getName(), latte.getTask())
}
mcm := MochaCoffeeMaker{}
if mocha, err := mcm.makeCoffee(); err != nil {
fmt.Println(err)
} else {
fmt.Printf("%s 已经为您准备好了,口感:%s。请慢慢享用!\n", mocha.getName(), mocha.getTask())
}
}
2.3 优缺点
“优点:
(1)解决了简单工厂模式不符合“开放-封闭”原则的问题,使程序更容易拓展。
(2)实现简单。
缺点:
对于有多种分类的产品,或具有二级分类的产品,工厂方法模式并不适用。
多种分类:如我们有一个电子白板程序,可以绘制各种图形,那么画笔的绘制功能可以理解为一个工厂,而图形可以理解为一种产品;图形可以根据形状分为直线、矩形、椭圆等,也可以根据颜色分为红色图形、绿色图形、蓝色图形等。
二级分类:如一个家电工厂,它可能同时生产冰箱、空调和洗衣机,那么冰箱、空调、洗衣机属于一级分类;而洗衣机又可分为高效型的和节能型的,高效型洗衣机和节能型洗衣机就属于二级分类。
2.4.应用场景
(1)客户端不知道它所需要的对象的类。
(2)工厂类希望通过其子类来决定创建哪个具体类的对象。
三、抽象工厂模式
抽象工厂模式是工厂方法模式的升级版本,工厂方法模式不能解决具有二级分类的产品的创建问题,抽象工厂模式就是用来解决这一问题的。
3.1 定义
提供一个创建一系列相关或相互依赖的对象的接口,而无须指定它们的具体类。
3.2代码实现
package main
import "fmt"
type HomeAppliances interface {
getName() string
}
// Refrigerator 冰箱
type Refrigerator struct {
name string
}
func (r Refrigerator) getName() string {
return "冰箱"
}
// EfficientRefrigerator 高效冰箱
type EfficientRefrigerator struct {
Refrigerator
}
func (e EfficientRefrigerator) getName() string {
return e.name
}
// EnergySavingRefrigerator 节能冰箱
type EnergySavingRefrigerator struct {
Refrigerator
}
func (e EnergySavingRefrigerator) getName() string {
return e.name
}
// WashingMachine 洗衣机
type WashingMachine struct {
name string
}
func (w WashingMachine) getName() string {
return "洗衣机"
}
// EfficientWashingMachine 高效洗衣机
type EfficientWashingMachine struct {
WashingMachine
}
func (e EfficientWashingMachine) getName() string {
return e.name
}
// EnergySavingWashingMachine 节能洗衣机
type EnergySavingWashingMachine struct {
WashingMachine
}
func (e EnergySavingWashingMachine) getName() string {
return e.name
}
type AppliancesFactory interface {
CreateRefrigerator() HomeAppliances
CreateWashingMachine() HomeAppliances
}
type EfficientFactory struct {
}
func (e EfficientFactory) CreateRefrigerator() HomeAppliances {
return &EfficientRefrigerator{Refrigerator{
name: "高效冰箱",
}}
}
func (e EfficientFactory) CreateWashingMachine() HomeAppliances {
return &EfficientWashingMachine{WashingMachine{
name: "高效洗衣机",
}}
}
type EnergySavingFactory struct {
}
func (e EnergySavingFactory) CreateRefrigerator() HomeAppliances {
return &EnergySavingRefrigerator{Refrigerator{
name: "节能冰箱",
}}
}
func (e EnergySavingFactory) CreateWashingMachine() HomeAppliances {
return &EnergySavingWashingMachine{WashingMachine{
name: "节能洗衣机",
}}
}
func main() {
efficientFactory := new(EfficientFactory)
energySavingFactory := new(EnergySavingFactory)
efficRefrigerator := efficientFactory.CreateRefrigerator()
efficWashingMachine := efficientFactory.CreateWashingMachine()
engryRefrigerator := energySavingFactory.CreateRefrigerator()
engryWashingMachine := energySavingFactory.CreateWashingMachine()
fmt.Println(efficRefrigerator.getName())
fmt.Println(efficWashingMachine.getName())
fmt.Println(engryRefrigerator.getName())
fmt.Println(engryWashingMachine.getName())
}
AppliancesFactory是一个抽象的工厂类,定义了三个方法,分别用来生产冰箱(Refrigerator)、空调(Air-conditioner)、洗衣机(WashingMachine)。EfficientFactory和EnergySavingFactory是两个具体的工厂类,分别用来生产高效型的家电和节能型的家电。
3.3 优缺点
优点:
解决了具有二级分类的产品的创建。
缺点:
(1)如果产品的分类超过二级,如三级甚至更多级,抽象工厂模式将会变得非常臃肿。
(2)不能解决产品有多种分类、多种组合的问题。
3.4应用场景
(1)系统中有多于一个的产品族,而每次只使用其中某一产品族。
(2)产品等级结构稳定,设计完成之后,不会向系统中增加新的产品等级结构或者删除已有的产品等级结构。
资料:
《人人都懂设计模式:从生活中领悟设计模式》