通过“对象创建” 模式绕开new,来避免对象创建(new)过程中所导致的紧耦合(依赖具体类),从而支持对象创建的稳定。它是接口抽象之后的第一步工作。
典型模式:
定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使得一个类的实例化延迟(目的:解耦,手段:虚函数)到子类。
同样以文件分割器为例,有一个较大的文件,想要被分成多个小块的文件。
package factory_method
import "strconv"
type FileSplitter struct {
path string
nums int
}
func (s *FileSplitter) split() {
//do something
//split a big file to nums small files
for i := 0; i < s.nums; i++ {
}
}
type TextBox struct {
text string
}
func (box *TextBox) getText() string {
return box.text
}
type MainForm struct {
txtFilePath *TextBox
txtFileNumber *TextBox
}
func (main *MainForm) Button1_Click() {
filePath := main.txtFilePath.getText()
number, _ := strconv.Atoi(main.txtFileNumber.getText())
splitter := FileSplitter{filePath, number}
splitter.split()
}
当点击后就会执行分隔。
由于,FileSplitter
可能是想要变化的,在button
点击里被写死了,不能改变了。假设有BinarySplitter
, TxtSplitter
,PictureSplitter
,VideoSplitter
,这么多分割器,
首先想要抽象出一个分割器
// 分割器的抽象
type ISplitter interface {
split()
}
具体实现
// 二进制文件分割器
type BinarySplitter struct {
ISplitter //继承
}
func (bin *BinarySplitter) split() {
fmt.Println("BinarySplitter split")
}
// Txt文件分割器
type TxtSplitter struct {
ISplitter //继承
}
func (txt *TxtSplitter) split() {
fmt.Println("TxtSplitter split")
}
// Picture分割器
type PictureSplitter struct {
ISplitter //继承
}
func (pic *PictureSplitter) split() {
fmt.Println("PictureSplitter split")
}
// Video分割器
type VideoSplitter struct {
ISplitter //继承
}
func (video *VideoSplitter) split() {
fmt.Println("VideoSplitter split")
}
然后我们可以将实例化改变,
func (main *MainForm) Button1_Click() {
_ = main.txtFilePath.getText()
_, _ = strconv.Atoi(main.txtFileNumber.getText())
var splitter ISplitter
splitter = &BinarySplitter{}
splitter.split()
}
左边确实是抽象依赖了,但是BinarySplitter
仍然是依赖具体,打破了依赖倒置原则。
于是如何去除BinarySplitter
这样的具体依赖就是创建模式要解决的问题。这是面向接口编程的必然需求。
对象创建的方法,不用new
用一个方法来返回我们需要的对象。
// 分割工厂
type SplitterFactory struct {
}
func (sf *SplitterFactory) CreateSplitter() ISplitter {
return &BinarySplitter{}
}
使用
func (main *MainForm) Button1_Click() {
_ = main.txtFilePath.getText()
_, _ = strconv.Atoi(main.txtFileNumber.getText())
var splitter ISplitter
var factory SplitterFactory
splitter = factory.CreateSplitter()
splitter.split()
}
但是实际上由于CreateSplitter
(编译时)依赖于BinarySplitter
,由于依赖的传递性,所以仍然没有解决问题。
考虑将具体实现交给后续调用,设计为接口
// 分割工厂
type SplitterFactory interface {
CreateSplitter() ISplitter
}
func (main *MainForm) Button1_Click() {
_ = main.txtFilePath.getText()
_, _ = strconv.Atoi(main.txtFileNumber.getText())
var splitter ISplitter
var factory SplitterFactory
splitter = factory.CreateSplitter()
splitter.split()
}
那么SplitterFactory
又赋值什么呢?
通过工厂创建具体的对象,
// 具体工厂
// 二进制文件分割器工厂
type BinarySplitterFactory struct {
SplitterFactory //继承
}
func (bin *BinarySplitterFactory) CreateSplitter() ISplitter {
return &BinarySplitter{}
}
// Txt文件分割器工厂
type TxtSplitterFactory struct {
SplitterFactory //继承
}
func (txt *TxtSplitterFactory) CreateSplitter() ISplitter {
return &TxtSplitter{}
}
// Picture分割器工厂
type PictureSplitterFactory struct {
SplitterFactory //继承
}
func (pic *PictureSplitterFactory) CreateSplitter() ISplitter {
return &PictureSplitter{}
}
// Video分割器工厂
type VideoSplitterFactory struct {
SplitterFactory //继承
}
func (video *VideoSplitterFactory) CreateSplitter() ISplitter {
return &VideoSplitter{}
}
那么SplitterFactory
又什么时候赋值呢?
一般是构造具体的MainForm
的时候
type MainForm struct {
factory SplitterFactory
}
func NewMainForm(factory SplitterFactory) *MainForm {
return &MainForm{factory: factory}
}
func (main *MainForm) Button1_Click() {
splitter := main.factory.CreateSplitter()
splitter.split()
}
这就实现了多态实例化的功能。
问题,将来实际上还是有具体的实现。
答:MainForm
里不依赖了,具体不由我来实现了,而把这个变化放到其他对方去了。
使用
func TestMainForm_Button1_Click(t *testing.T) {
//真正实例化的地方,
main := NewMainForm(&PictureSplitterFactory{})
main.Button1_Click()
}
类图
书上类图:
总结:
提供一个接口,让该接口负责创建一系列“相关或者相互依赖的对象”,无需指定它们具体的类。
对不同的数据库需要不同的实现,SqlConnection
,SqlCommand
,想法是先抽象
如果按照工厂方法,解决一个类对应一个工厂。一个具体实现一个具体工厂类。
package abstract_factory
// 数据库访问有关的接口
type IDBConnection interface {
}
// 工厂
type IDBConnectionFactory interface {
CreateDBConnection() IDBConnection
}
type IDBCommand interface {
SetConnection(connection IDBConnection)
}
type IDBCommandFactory interface {
CreateDBCommand() IDBCommand
}
type IDBReader interface {
}
type IDBReaderFactory interface {
CreateDBReader() IDBReader
}
// 对SqlServer的支持
type SqlConnection struct {
IDBConnection
}
// 具体工厂
type SqlConnectionFactory struct {
IDBConnectionFactory
}
type SqlCommand struct {
IDBCommand
}
type SqlCommandFactory struct {
IDBCommandFactory
}
type SqlDataReader struct {
IDBReader
}
type SqlDataReaderFactory struct {
IDBReaderFactory
}
// oracle 的支持
type OracleConnection struct {
IDBConnection
}
type OracleConnectionFactory struct {
IDBConnectionFactory
}
type OracleCommand struct {
IDBCommand
}
type OracleCommandFactory struct {
IDBCommandFactory
}
type OracleDataReader struct {
IDBReader
}
type OracleDataReaderFactory struct {
IDBReaderFactory
}
type Employee struct{}
type EmployeeDAO struct {
dbConnectionFactory IDBConnectionFactory //指针
dbCommandFactory IDBCommandFactory
dbReaderFactory IDBReaderFactory
}
func (dao *EmployeeDAO) GetEmployees() []Employee {
connection := dao.dbConnectionFactory.CreateDBConnection()
command := dao.dbCommandFactory.CreateDBCommand()
command.SetConnection(connection)
return nil
}
看上去是解决了,但实际上没有体现依赖性,如果是 SQL
的Connection
,传给Oracle
的Command
,肯定会报错。
想法是把IDBConnectionFactory
,IDBCommandFactory
,IDBReaderFactory
把这三个工厂变成一个工厂,一一对应,相干的
package abstract_factory
// 数据库访问有关的接口
type IDBConnection interface {
}
type IDBCommand interface {
SetConnection(connection IDBConnection)
ExecuteReader() IDBReader
}
type IDBReader interface {
Read() bool
}
type IDBFactory interface {
CreateDBConnection() IDBConnection
CreateDBCommand() IDBCommand
CreateDBReader() IDBReader
}
对于具体的数据库
package abstract_factory
import "fmt"
// 对SqlServer的支持
type SqlConnection struct {
IDBConnection
}
type SqlCommand struct {
IDBCommand
}
type SqlDataReader struct {
IDBReader
}
type SqlDBFactory struct {
IDBFactory
}
// oracle 的支持
type OracleConnection struct {
IDBConnection
}
type OracleCommand struct {
IDBCommand
}
type OracleDataReader struct {
IDBReader
}
func (reader *OracleDataReader) Read() bool {
fmt.Println("OracleDataReader Read")
return true
}
type OracleDBFactory struct {
IDBFactory
}
// 为工厂实现具体方法
func (factory *OracleDBFactory) CreateDBConnection() IDBConnection {
return &OracleConnection{}
}
func (factory *OracleDBFactory) CreateDBCommand() IDBCommand {
return &OracleCommand{}
}
func (factory *OracleDBFactory) CreateDBReader() IDBReader {
return &OracleDataReader{}
}
type Employee struct{}
type EmployeeDAO struct {
dbFactory IDBFactory
}
// 实例化DAO
func NewEmployeeDAO(factory IDBFactory) *EmployeeDAO {
return &EmployeeDAO{dbFactory: factory}
}
func (dao *EmployeeDAO) GetEmployees() []Employee {
connection := dao.dbFactory.CreateDBConnection() //同一个工厂可以保证创建的对象是同一类
command := dao.dbFactory.CreateDBCommand()
command.SetConnection(connection) //关联
reader := command.ExecuteReader() //关联
for reader.Read() {
}
return nil
}
使用原型实例指定创建对象的种类,然后通过拷贝这些原型来创建新的对象
复杂对象,只要原型被创建了,以后需要原型都可以通过克隆实现。
而工厂方法如果创建过于复杂,不能解决中间状态,则用原型模式。
原型模式将工厂方法抽取到自身,同时重命名为clone
,是深拷贝!深拷贝可以通过序列化反序列化实现。
原型不是直接用的,而是通过clone
创建新的对象供使用,
package prototype
import "fmt"
// 分割器的抽象
type ISplitter interface {
split()
clone() ISplitter //通过原型克隆自己
}
// 二进制文件分割器
type BinarySplitter struct {
ISplitter //继承
count int
}
func (bin *BinarySplitter) split() {
bin.count++
fmt.Println("BinarySplitter split,", bin.count)
}
func (bin *BinarySplitter) clone() ISplitter {
fmt.Println("BinarySplitter clone")
// 通过原型克隆自己
// 创建一个新的BinarySplitter实例,并复制原对象的属性
clone := &BinarySplitter{}
clone.ISplitter = bin.ISplitter
clone.count = bin.count
return clone
}
type MainForm struct {
prototype ISplitter //原型对象
}
func NewMainForm(prototype ISplitter) *MainForm {
return &MainForm{prototype: prototype}
}
func (main *MainForm) Button1_Click() {
// 通过原型对象创建新的分割器对象
splitter := main.prototype.clone()
splitter.split()
}
类图
将一个复杂对象的构建与其表示相分离,使得同样的构建过程(稳定)可以创建不同的表示(变化)
假设有一个游戏,创建房屋,
不管怎么样的材质地基,墙壁,天花板,门、窗是需要的,流程是固定的,但具体的材料可能不一样,
package builder
type BuildPart interface {
BuildPart01()
BuildPart02()
BuildPart03() bool
BuildPart04()
BuildPart05()
}
type House struct {
BuildPart //是House的虚方法,必须要实现
flag bool
}
// 真正的构造方法
func (this *House) Init() {
this.BuildPart01()
for i := 0; i < 4; i++ {
this.BuildPart02()
}
flag := this.BuildPart03()
if flag {
this.BuildPart04()
}
this.BuildPart05()
}
问题是由于子步骤是变化的,但是流程是固定的,所以抽离了BuildPart*
。与模板方法非常像。
package builder
type BuildPart interface {
BuildPart01()
BuildPart02()
BuildPart03() bool
BuildPart04()
BuildPart05()
}
// 房子基类
type House struct {
BuildPart //是House的虚方法,必须要实现
}
// 真正的构造方法
func (this *House) Init() {
this.BuildPart01()
for i := 0; i < 4; i++ {
this.BuildPart02()
}
flag := this.BuildPart03()
if flag {
this.BuildPart04()
}
this.BuildPart05()
}
// 石头房子
type StoneHouse struct {
House //继承
}
// 实例化石头房子
func NewStoneHouse() *StoneHouse {
stone := &StoneHouse{}
stone.BuildPart = stone //实现了虚方法
return stone
}
func (this *StoneHouse) BuildPart01() {
println("StoneHouse BuildPart01")
}
func (this *StoneHouse) BuildPart02() {
println("StoneHouse BuildPart02")
}
func (this *StoneHouse) BuildPart03() bool {
println("StoneHouse BuildPart03")
return true
}
func (this *StoneHouse) BuildPart04() {
println("StoneHouse BuildPart04")
}
func (this *StoneHouse) BuildPart05() {
println("StoneHouse BuildPart05")
}
使用
func TestHouse_Init(t *testing.T) {
stoneHouse := NewStoneHouse()
stoneHouse.Init()
}
由于有时候一个类本身功能就比较复杂,于是将Builder
进一步抽离,
package builder
// 房子基类
type House struct {
// 各种字段
door string
floor string
wall string
roof string
light string
}
type BuildPart interface {
BuildPart01()
BuildPart02()
BuildPart03() bool
BuildPart04()
BuildPart05()
GetResult() *House
}
// 负责构造房子的Builder
type HouseBuilder struct {
pHouse BuildPart // 是BuildPart的虚方法,必须要实现
}
func (hb *HouseBuilder) GetResult() *House {
return hb.pHouse.GetResult()
}
// 此前Init的具体实现,改名为Construct
type HouseDirector struct {
pHouseBuilder BuildPart
}
// 构造器
func NewHouseDirector() *HouseDirector {
return &HouseDirector{}
}
func (hd *HouseDirector) SetBuilder(builder BuildPart) {
hd.pHouseBuilder = builder
}
func (hd *HouseDirector) Construct() *House {
hd.pHouseBuilder.BuildPart01()
for i := 0; i < 4; i++ {
hd.pHouseBuilder.BuildPart02()
}
flag := hd.pHouseBuilder.BuildPart03()
if flag {
hd.pHouseBuilder.BuildPart04()
}
hd.pHouseBuilder.BuildPart05()
return hd.pHouseBuilder.GetResult()
}
// 石头房子
type StoneHouse struct {
House // 继承
}
// 实例化石头房子
func NewStoneHouse() *StoneHouse {
return &StoneHouse{}
}
// 石头房子Builder
type StoneHouseBuilder struct {
HouseBuilder // 继承
pHouse *StoneHouse
}
// 实例化
func NewStoneHouseBuilder(house *StoneHouse) *StoneHouseBuilder {
stone := &StoneHouseBuilder{pHouse: house}
return stone
}
func (shb *StoneHouseBuilder) BuildPart01() {
println("StoneHouse BuildPart01")
shb.pHouse.wall = "Stone"
}
func (shb *StoneHouseBuilder) BuildPart02() {
println("StoneHouse BuildPart02")
shb.pHouse.door = "Stone"
}
func (shb *StoneHouseBuilder) BuildPart03() bool {
println("StoneHouse BuildPart03")
shb.pHouse.roof = "Stone"
return true
}
func (shb *StoneHouseBuilder) BuildPart04() {
println("StoneHouse BuildPart04")
shb.pHouse.floor = "Stone"
}
func (shb *StoneHouseBuilder) BuildPart05() {
println("StoneHouse BuildPart05")
shb.pHouse.light = "Stone"
}
func (shb *StoneHouseBuilder) GetResult() *House {
return &shb.pHouse.House
}
这个版本太复杂,使用第一个版本也可以,使用
func TestHouse(t *testing.T) {
director := NewHouseDirector()
stoneHouseBuilder := NewStoneHouseBuilder(NewStoneHouse())
director.SetBuilder(stoneHouseBuilder)
house := director.Construct()
fmt.Printf("%+v\n", house)
}