概述
在工厂模式
中,使用一个公共的接口来创建对象,将创建逻辑封装在工厂类中,由工厂类负责各种对象的实例化。
工厂模式有如下优点:
- 若创建逻辑可能有所变动,将其封装在独立的工厂类中,可以实现代码复用。
- 创建逻辑不会暴露给调用者,亦即调用者无需了解创建对象的细节,实现对象的创建与使用之间的解耦合。
- 将创建逻辑抽离出来,使各函数职能专一化,简化代码。
工厂模式
一般细分为三种具体的类型:简单工厂
、工厂方法
、抽象工厂
,其抽象度依次提高。
简单工厂模式
定义
简单工厂模式(不属于23种设计模式)使用一个工厂类
,该类根据参数的不同返回不同类型的实例,一般情况下被创建的实例往往拥有相同的父类(或实现了相同的接口)。
此外,由于简单工厂模式使用静态的工厂方法,因此又称为静态工厂模式
。
优点
- 根据传入的不同参数,工厂类可以动态地选择创建哪种实例,调用方只需要负责”消费“即可。
缺点
- 工厂类扩展困难,一旦需要新增产品就必须修改工厂类的逻辑,违反了开闭原则。(对扩展开放,对修改关闭)。
- 随着产品种类的增多,简单工厂会变得愈加臃肿,为了解决这个问题,产生出了
抽象工厂
模式。
代码实现
与C++,Java等语言不同,Go语言没有构造函数,所以一般使用NewXxx函数来实现简单工厂,通过返回一个接口来实例化对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| package factory import "fmt"
Parse(data []byte) }
fmt.Println("implement json config parser") }
fmt.Println("implement yaml config parser") }
fmt.Println("implement xml config parser") }
switch t { case "json": return JsonConfigParser{} case "yaml": return YamlConfigParser{} case "xml": return XmlConfigParser{} default: return nil } }
|
测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package factory import ( "reflect" "testing") func TestNewConfigParser(t *testing.T) { tests := []struct { name string want ConfigParser }{ {name: "json", want: JsonConfigParser{}}, {name: "xml", want: XmlConfigParser{}}, {name: "yaml", want: YamlConfigParser{}}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := NewConfigParser(tt.name); !reflect.DeepEqual(got, tt.want) { t.Errorf("NewConfigParser() = %v, want %v", got, tt.want) } }) } }
|
工厂方法模式
定义
工厂方法模式(Factory Method)又称多态性工厂模式,属于23种设计模式的创建型模式。工厂方法模式定义了一个创建对象的“接口”,由实现这个接口的工厂类(子类)决定实例化哪个产品类,将类的实例化推迟到子类中进行。
原先的工厂不再负责具体的实例化,仅负责给出具体工厂子类必须实现的接口。
优点
- 扩展性高,当需要新增一个产品时,只要扩展一个工厂类即可,符合
开闭原则
。
- 用户只需要关心产品对应的工厂即可,无需关心创建细节。
- 在产品创建细节复杂的时候,将其分离到单独的具体工厂中,不至于让单个工厂类的逻辑过于复杂。
缺点
- 在增加产品时,要同时增加产品类和对应的具体工厂类,这会使类的数量过多,系统的复杂度增高。
- 只能生产一类产品,如果需要生产多种不同类的产品,请使用
抽象工厂模式
。
代码实现
很遗憾,Golang没有“继承”这一概念,在Go中实现“工厂方法模式”,采用让具体工厂类直接实现工厂接口的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| package factory
type ConfigParserFactory interface { Create() ConfigParser }
type JsonConfigParserFactory struct{}
func (f JsonConfigParserFactory) Create() ConfigParser { return JsonConfigParser{} }
type YamlConfigParserFactory struct{}
func (f YamlConfigParserFactory) Create() ConfigParser { return YamlConfigParser{} }
type XmlConfigParserFactory struct{}
func (f XmlConfigParserFactory) Create() ConfigParser { return XmlConfigParser{} }
|
测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package factory import ( "testing") func TestConfigParserFactory(t *testing.T) { tests := []struct { name string factory ConfigParserFactory want ConfigParser }{ {name: "json", factory: JsonConfigParserFactory{}, want: JsonConfigParser{}}, {name: "xml", factory: YamlConfigParserFactory{}, want: YamlConfigParser{}}, {name: "yaml", factory: XmlConfigParserFactory{}, want: XmlConfigParser{}}, } for _, tt := range tests { got := tt.factory.Create() if got != tt.want { t.Errorf("%s Create() = %v, want %v", tt.name, got, tt.want) } } }
|
抽象工厂模式
定义
抽象工厂模式(Abstract Factory)提供一个创建一系列相关或相互依赖对象的接口,而无须指定所要产品的具体类。
举例
考虑一个生产家具的场景:
- 我们需要关注3种产品:椅子、茶几、沙发
- 这3种产品必须配套:必须均为简约风格、美式风格或日式风格
- 同一种风格的产品在一起生产(显而易见的道理)
- 用户往往只关心某一套风格的家具的订购和使用
现在考虑如何实现上述要求:
- 首先,需要声明
抽象工厂
,该工厂包含所有产品的构造方法的接口。
例如:CreateChair
、CreateSofa
、CreateCoffeeTable
这些方法返回抽象产品
,即他们无论风格如何,都是椅子、茶几或沙发。
- 现在基于不同风格,但是成套的产品,分别声明其
具体工厂
,这些工厂分别负责生产某一风格的一套家具。
例如:现代家具工厂ModernFurnitureFactory
只能创建现代椅子ModernChair
、现代沙发ModernSofa
和现代咖啡桌ModernCoffeeTable
产品;其他工厂类似。
具体工厂
负责生产具体产品
以供用户使用。
- 接下来,用户只需要看重某一风格的家具即可成套购买。
用户无需关心具体的工厂类,他们只关心自己要获取的是一套家具而已。
至于家具如何生产,那是工厂要干的事。。。
另外,显然用户明确知道家具(抽象产品
)是干什么的——可以坐下、放茶杯等等,即抽象产品实现了哪些方法。
还有一件事,由于用户只关心抽象接口,因此需要根据配置文件等信息来选择具体工厂。
抽象工厂实现
根据上面的例子进行抽象:
- 产品族:即彼此之间有一定联系的产品,例如上例中的椅子、茶几或沙发共同组成一套家具。
- 不同等级产品:即同族中,各个不同的产品,例如同一风格的椅子、茶几或沙发属于3个不同等级的产品。
接下来实现整体的抽象工厂模式:
- 抽象工厂(Abstract Factory):提供了创建抽象产品的接口,它包含多个创建产品的方法,可以创建多个不同等级的产品。
- 具体工厂(Concrete Factory):实现抽象工厂中的多个抽象方法,完成具体产品的创建。
- 抽象产品(Product):定义产品的规范,描述产品的功能接口,抽象工厂可以创建多个抽象产品。
- 具体产品(ConcreteProduct):实现了抽象产品所定义的接口,由具体工厂来创建。
优点
- 同一工厂生产的产品相互匹配,即属于同一族。
- 客户端无需关心创建细节,通过抽象接口操作实例。
- 满足
开闭原则
: 向程序中引入新产品时,无需修改客户端代码。
缺点
- 代码结构愈加复杂。
- 新产品的加入非常困难:抽象工厂确定了产品集合,并且规定了工厂接口,新增产品涉及到所有工厂的接口扩展。
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
| package factory
import "fmt"
type Chair interface { Sit() }
type Table interface { PutDown() }
type Sofa interface { Sit() }
type FurnitureFactory interface { CreateChair() Chair CreateTable() Table CreateSofa() Sofa }
type ModernChair struct{}
func (c ModernChair) Sit() { fmt.Println("Sitting on modern chair") }
type ModernTable struct{}
func (t ModernTable) PutDown() { fmt.Println("Putting down modern table") }
type ModernSofa struct{}
func (s ModernSofa) Sit() { fmt.Println("Sitting on modern sofa") }
type ModernFurnitureFactory struct{}
func (f ModernFurnitureFactory) CreateChair() Chair { return ModernChair{} } func (f ModernFurnitureFactory) CreateTable() Table { return ModernTable{} } func (f ModernFurnitureFactory) CreateSofa() Sofa { return ModernSofa{} }
type ConciseChair struct{}
func (c ConciseChair) Sit() { fmt.Println("Sitting on concise chair") }
type ConciseTable struct{}
func (t ConciseTable) PutDown() { fmt.Println("Putting down concise table") }
type ConciseSofa struct{}
func (s ConciseSofa) Sit() { fmt.Println("Sitting on concise sofa") }
type ConciseFurnitureFactory struct{}
func (f ConciseFurnitureFactory) CreateChair() Chair { return ConciseChair{} } func (f ConciseFurnitureFactory) CreateTable() Table { return ConciseTable{} } func (f ConciseFurnitureFactory) CreateSofa() Sofa { return ConciseSofa{} }
func GetFactory(furnitureType string) FurnitureFactory { switch furnitureType { case "modern": return ModernFurnitureFactory{} case "concise": return ConciseFurnitureFactory{} default: return nil } }
|
测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| package factory import "testing" func TestAbstractFactory(t *testing.T) { type Unit struct { name string want []interface{} } tests := []Unit{ { name: "modern", want: []interface{}{ModernChair{}, ModernSofa{}, ModernTable{}}, }, { name: "concise", want: []interface{}{ConciseChair{}, ConciseSofa{}, ConciseTable{}}, }, } for _, tt := range tests { factory := GetFactory(tt.name) got := []interface{}{ factory.CreateChair(), factory.CreateSofa(), factory.CreateTable(), } for i, v := range got { if v != tt.want[i] { t.Errorf("%s got %T, want %T", tt.name, v, tt.want[i]) } } } }
|