文档章节

二、工厂方法模式(Factory Method)

chapin
 chapin
发布于 2015/01/14 22:59
字数 3084
阅读 27
收藏 0

一、工厂方法模式简介

    在23种设计模式中,工厂模式主要是为创建对象提供了接口。工厂模式按照《Java与模式》中的提法分为三类:

    1.简单工厂(Simple Factory

    2.工厂方法模式(Factory Method)

    3.抽象工厂模式(Abstract Factory)

    这三种模式从上到下逐步抽象,并且具有一般性。 还有一种分法,就是将简单工厂模式看为工厂方法模式的一种特例,两个归属一类。两者皆可,这本为使用《JAVA与模式》的分类方法。因此,在本节我们将会学习简单工厂(Simple Factory)工厂方法模式(Factory Method)

二、简单工厂

2.1 简单工厂概述

    简单工厂模式在开发中应用的情况也非常多,单其并不是GOF二十三种设计模式之一,最多只能算是工厂方法的一种特殊形式,从设计模式的类型上来说,简单工厂模式是属于创建型模式,又叫静态工厂方法(Static Factory Method)模式。简单工厂模式的实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品(这些产品类继承自一个父类或接口)类的实例。

2.2 简单工厂模式举例

    假设我是一间只卖绿茶的饮料店,客人买了一杯绿茶时我们会这样做,如下:

// 绿茶
func GreenTeaOrders() GreenTea {
	greenTea := GreenTea{}
	greenTea.AddMaterial()  // 加料
	greenTea.Brew()         // 冲泡
	greenTea.PouredCup()    // 装杯
	return greenTea
}

    但是,如果只卖绿茶已经不能应付客人想多选择的需求,我们就必须增加更多个饮料品项,现在我们增加了红茶供客人选择,如下:

// 绿茶
func GreenTeaOrders() GreenTea {
	greenTea := GreenTea{}
	greenTea.AddMaterial()    // 加料
	greenTea.Brew()           // 冲泡
	greenTea.PouredCup()      // 装杯
	return greenTea
} 

// 红茶
func BlackTeaOrders() BlackTea {
	blackTea := BlackTea{}
	blackTea.AddMaterial()    // 加料
	blackTea.Brew()           // 冲泡
	blackTea.PouredCup()      // 装杯
	return blackTea
}

    这样客人就多了红茶可以选择。但是当饮料品继续增加的时候,我的BeverageStores类别就必须加入对应的饮品方法,这些饮品方法内做的事情其实也是一样的,我们可以发现AddMaterial()、Brew()、PouredCup()三个方法都是固定必须处理的事情,这时候我们就可以透过使用接口将这些动作进行封装,所以我们将这三个方法加入接口中,如下:

type IBeverageStores interface {
	AddMaterial()
	Brew()
	PouredCup()
}

    接着必须将饮料类别实现接口方法后,再调用产生饮料的方法,如下:

// GreenTea,实现了接口IBeverageStores
type GreenTea struct {}
func (this * GreenTea) AddMaterial() {}
func (this * GreenTea) Brew() {}
func (this * GreenTea) PouredCup() {}

//BlackTea, 实现了接口IBeverageStores
type BlackTea struct {}
func (this * BlackTea) AddMaterial() {}
func (this * BlackTea) Brew() {}
func (this * BlackTea) PouredCup() {}

func BeverageOrder(beverageType string) (tea IBeverageStores)  {
	if beverageType == "BlackTea" {
		tea = new(BlackTea)
	} else if beverageType == "GreenTea" {
		tea = new(GreenTea)
	} else {
		tea = nil 
	}

	if tea != nil {
		tea.AddMaterial()
		tea.Brew()
		tea.PouredCup()
	}

	return
}

    现在透过回传IBeverageProvide介面的方式,我可以不用再增加多余的饮品方法了,但这时又产生了新的问题,我想到在OO的原则下因遵循[类别应该开放便于扩展,应该关闭禁止修改],这里由于我们使用的是IF ELSE 与new关键字来实例化封装类别,所以当增加新饮品时修改应是不可避免的,然后我们已经知道了哪些地方是必须要修改的,所以我应该将这些地方提出来进行封装,以减少 BeverageStores类别对于饮料类别的依赖性。

    我再次调整BeverageOrders类别,将产生实例化的部分提取出到一个新的SimpleBeverageFactory类别中,如下:

// SimpleBeverageFactory
type SimpleBeverageFactory struct {}
func (this *SimpleBeverageFactory) CreateBeverage(beverageType string) (tea IBeverageStores) {
	if beverageType == "BlackTea" {
		tea = new(BlackTea)
	} else if beverageType == "GreenTea" {
		tea = new(GreenTea)
	} else {
		tea = nil 
	}

	return
}

    由以上程序段可以看到SimpleBeverageFactory 类别将产生饮品实例的逻辑置入 CreateBeverage 方法中,透过将产生实例的逻辑区断提出后放置到另一个类别中,我们就可以称此类别为一个工厂类别,由这个类别的CreateBeverage 方法来决定要产生哪一个饮品类别,日后如要增加新饮品类别就由此处修改即可。

    接着我们需要调整原本的BeverageStores类别,让此类别能够透过传入对应的工厂类别建立饮品的实例,如下:

// BeverageStores
type BeverageStores struct {
	beverageFactory SimpleBeverageFactory
}

func NewBeverageStores(beverageFactory SimpleBeverageFactory) * BeverageStores {
	return &BeverageStores{beverageFactory : beverageFactory}
}

func (this * BeverageStores) BeverageOrders(beverageType string) {
	tea := this.beverageFactory.CreateBeverage(beverageType)
	if tea != nil {
		tea.AddMaterial()
		tea.Brew()
		tea.PouredCup()
	}
}

    套用工厂模式前的类别前图如下,BeverageStores类别直接关联GreenTea类别与BlackTea类别


    而透过界面与面单工厂模式封装后如下,BeverageStores类别切开了与GreenTea类别及BlackTea类别的关联性,让BeverageStroes类别关联于SimpleBeverageFactory工厂类别,SimpleBeverageFactory工厂类别则透过 IBeverageProvide饮品介面切开与饮品类别的关联,降低了各类别的耦合性。


最后让我们来制作饮料给客人吧:P

func main() {
	beverageStore := NewBeverageStores(SimpleBeverageFactory{})
	beverageStore.BeverageOrders("GreenTea")
	beverageStore.BeverageOrders("BlackTea")
}

2.3 简单工厂模式总结

  • 优点:

    1.用过使用工厂类,外接不再需要关心如何创造各种具体的产品,只需要提供一个产品的名称作为参数传给工厂,就可以直接得到一个想要的产品对象,并且可以不按接口规范来调用产品对象的所有功能(方法)。

    2.容易构造,逻辑简单。

  • 缺点:

    1.细心的朋友肯能早就发现了,这么多if else 判断完全是苦力活啊,如果我有一个新产品要加进来,就要同时添加一个新产品类,并且必须要修改工厂类,再加入一个 else if 分支才可以,这样就违背了开放-关闭原则中的对修改关闭的原则了。 当系统中的具体产品类不断增多的时候,就要不断修改工厂类,对系统维护和扩展不利。

    2.一个工厂类中集合了所有的类的实例创建逻辑,违反了高内聚的责任分配原则,将全部的创建逻辑都集中到了一个工厂类当中,因此一般只在很简单的情况下应用,比如当工厂类负责创建的对象比较少时。

三、工厂方法模式

3.1 工厂方法模式概述

    上一节说明了简单工厂模式,接下里继续说明工厂方法模式,工厂方法模式主要的定义为:让子类别决定要实体化的类别为何。也就是说当建立产品时你可以自行决定由那个子类来建立实际产品。

简单工厂方法其实与工厂方法有些相似,但是仔细看看,简单工厂是将全部的事情于简单工厂的方法处理完成,而工厂方法却会将要处理的事情交由实际实践的子类别处理,也就是当产品的种类增加的时候在简单工厂的情况下必须要修改简单工厂的方法,而工厂方法模式下只需要多增加一个新产品工厂的子类别去实践,如此就符合【开放封闭】原则,但工厂方法有个缺点就是会产生大量的工厂子类别。

组成工厂方法模式有四个角色,如下:

  • 抽象工厂角色:定义抽象方法或介面(接口),交由子类去实现。
  • 具体工厂角色:实现抽象方法或介面,包含实例化对象的商业逻辑。
  • 抽象产品角色:定义产品抽象方法或介面,定义产品共同执行的动作。
  • 具体产品角色:实现抽象产品的方法或介面,实现产品共同方法的商业逻辑。

3.2 工厂方法举例

    同样以上一篇的范例为例,在简单工厂时我将建立实例的动作置于简单工厂方法中的CreateBeverage()方法中,如下:

// SimpleBeverageFactory
type SimpleBeverageFactory struct {}
func (this *SimpleBeverageFactory) CreateBeverage(beverageType string) (tea IBeverageStores) {
	if beverageType == "BlackTea" {
		tea = new(BlackTea)
	} else if beverageType == "GreenTea" {
		tea = new(GreenTea)
	} else {
		tea = nil 
	}

	return
}

但现在饮料店为了满足顾客对于饮料种类的喜好而需要让提供更多不同种类的饮料时,就必须要修改CreateBeverage() 此方法中的判断,添加更多不同种类的饮料物件,如下:

// SimpleBeverageFactory
type SimpleBeverageFactory struct {}
func (this *SimpleBeverageFactory) CreateBeverage(beverageType string) (tea IBeverageStores) {
	if beverageType == "BlackTea" {
		tea = new(BlackTea)
	} else if beverageType == "GreenTea" {
		tea = new(GreenTea)
	} else if pBeverageType == "MilkTea" // 多了奶茶
		tea = new(MilkTea)
       else {
		tea = nil 
	}

	return
}


    由上所展示的代码可以看出为了应付更多种类的饮料,次简单工厂方法开始变得复杂且依赖性提高,并且不管是更改绿茶或红茶的口味都需要动到此方法,所以为了降低此方法的复杂度和依赖性,我们应该将饮料种类进行切割处理。

    首先需要建立一个产品的接口,由实现此产品接口的产品类别来建立产品的实例,因此我们需要将绿茶和红茶分别建立各自的工厂方法并且实现产品接口的CreateBeverage 方法。如下:

type IBeverageFactory interface {
	CreateBeverage() IBeverageProvide
}
// GreenTeaFactory
type GreenTeaFactory struct {}
func (this * GreenTeaFactory) CreateBeverage() IBeverageProvide {
	return new(GreenTea)
}
// BlackTeaFactory
type BlackTeaFactory struct {}
func (this * BlackTeaFactory) CreateBeverage() IBeverageProvide {
	return new(BlackTea)
}

    将绿茶与红茶切开建立各自的工厂方法后,现在我们如果要再增加奶茶时也不需要动到原本绿茶和红茶的工厂方法了,只需要增加一个实现IBeverageFactory接口的子类别,如下:

// MilkTeaFactory
type MilkTeaFactory struct {}
func (this * MilkTeaFactory) CreateBeverage() IBeverageProvide {
	return new(MilkTea)
}

    接着定义抽象产品角色,通过接口定义产品的共同方法让实现此接口的产品类别去实践,如下: 

type IBeverageProvide  interface {
	AddMaterial()
	Brew()
	PouredCup()
}

    当产品的接口定义完成后,就要建立该产品工厂所要创建的产品类别,也就是加入绿茶、红茶产品类别并且实现IBeverageProvide 接口的方法。如下:

// GreenTea,实现了接口IBeverageStores
type GreenTea struct {}
func (this * GreenTea) AddMaterial() {}
func (this * GreenTea) Brew() {}
func (this * GreenTea) PouredCup() {}

//BlackTea, 实现了接口IBeverageStores
type BlackTea struct {}
func (this * BlackTea) AddMaterial() {}
func (this * BlackTea) Brew() {}
func (this * BlackTea) PouredCup() {}

//MilkTea, 实现了接口IBeverageStores
type MilkTea struct {}
func (this * MilkTea) AddMaterial() {}
func (this * MilkTea) Brew() {}
func (this * MilkTea) PouredCup() {}

    经过以上的几个动作后,来看看转变为工厂方法模式后的类别图,如下:

    由上图可见,GreenTeaFactory与BlackTeaFactory工厂方法共同实现了IBeverageFactory 接口的CreateBeverage() 方法,在 CreateBeverage() 方法中单纯的实例化了各自的产品类型GreenTea与BlackTea,看的出来实践饮料的做法已经交由各自的工厂方法处理,而当饮料种类增加时只需要加入新的具体工厂角色与具体产品角色,从而也就有降低物件的耦合性了。

    最后来呼叫看看执行的语法,如下:

func main() {
	beverageStore := NewBeverageStores(new(MilkTeaFactory))
	beverageStore.BeverageOrders()

	beverageStore = NewBeverageStores(new(BlackTeaFactory))
	beverageStore.BeverageOrders()

	beverageStore = NewBeverageStores(new(GreenTeaFactory))
	beverageStore.BeverageOrders()
}

3.3 工厂方法模式总结


    首先,代码结构清晰,封装良好,客户端只需要知道具体的工厂类名称即可(如果命名非常规范,则只需要知道产品的名字就够了),根本不用关心创建对象的复杂过程(实例代码很简单,但实际应用中创建和构造对象的过程可能会非常复杂),减少了模块的耦合性。

    其次,符合了开放原则的要求,在新增加新产品的情况下,唔、无需修改现有产品类和工厂类,只要追加新的具体产品类和具体工厂类就够了,系统的拓展新得到了很大的提升。这也弥补了简单工厂模式对修改开放的诟病

    再次,屏蔽了上层调用者和具体产品实现的牵连。 不管产品的实现如何变化,只要接口不边,上层调用都不会随着变化,只要修改相应的产品类或产品工厂类就够了。

    最后,工厂方法模式还满足了迪米特原则,依赖倒置原则,里氏替换原则,是典型的解耦框架。



© 著作权归作者所有

共有 人打赏支持
chapin
粉丝 20
博文 83
码字总数 102961
作品 0
成都
程序员
私信 提问
设计模式分类

一.创建型模式(主要解决对象的创建) 1.简单工厂模式(Simple Factory) 2.工厂方法模式(Factory Method) 3.抽象工厂模式(Abstract Factory) 4.创建者模式(Builder) 5.原型模式 (Prototype) ...

黄孟
2016/10/16
5
0
设计模式之工厂模式(三)

意图: 定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使得一个类的实例化延迟到子类(即:工厂子类)。 工厂模式结构图: 工厂模式代码实现 using System; using ...

NickHomeSelf
2016/08/20
21
0
设计模式----工厂方法模式UML和实现代码(5个必须掌握的设计模式)

一、什么是工厂方法模式? 工厂方法模式(FACTORY METHOD)是一种常用的对象创建型设计模式,此模式的核心精神是封装类中不变的部分,提取其中个性化善变的部分为独立类,通过依赖注入以达到解...

amosli
2015/10/22
39
0
java设计模式---三种工厂模式

工厂模式提供创建对象的接口。 工厂模式分为三类:简单工厂模式(Simple Factory), 工厂方法模式(Factory Method)和抽象工厂模式(Abstract Factory). GOF在《设计模式〉中将工厂模式分为两类:...

jason_wu_2
2016/12/22
2
0
Java创建型模式的讨论

创建型模式抽象了实例化过程。它们帮助一个系统独立于如何创建、组合和表示它的那些对象。一个类创建型模式使用继承改变被实例化的类,而一个对象创建型模式将实例化委托给另一个对象。 创建...

tequliapop
2016/01/13
96
0

没有更多内容

加载失败,请刷新页面

加载更多

OSChina 周三乱弹 —— 哽住

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @蓝瞳 :分享高橋広樹的单曲《銀河沿線'05<Vo>》:一直暗恋却无法表白的女孩在电车上疲倦不堪累得靠在他的肩上,播磨拳儿终于忍不住流下了眼泪...

小小编辑
40分钟前
79
5
shell输入内容时不显示内容

简介 大家都知道当我们ssh登录linux服务器的时候输入密码密码是不显示在屏幕上的,同样在我们写shell脚本的时候在获取用户密码的时候,有时候也希望不显示密码,这个是怎么做的呢 实例脚本 ...

bboysoulcn
今天
3
0
第11章 多线程

程序、进程、线程 程序(program)是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。 **进程(process)**是程序的一次执行过程或是正在运行的一个程序。动...

流小文
今天
6
0
SpringBoot引入第三方jar包或本地jar包的处理方式

在开发过程中有时会用到maven仓库里没有的jar包或者本地的jar包,这时没办法通过pom直接引入,那么该怎么解决呢 一般有两种方法 - 第一种是将本地jar包安装在本地maven库 - 第二种是将本地j...

独钓渔
今天
2
0
五、MyBatis缓存

一、MyBatis缓存介绍 缓存的使用可以明显的加快访问数据速度,提升程序处理性能,生活和工作中,使用缓存的地方很多。在开发过程中,从前端-->后端-->数据库等都涉及到缓存。MyBatis作为数据...

yangjianzhou
今天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部