这次介绍的设计模式是工厂模式,这是一个比较常见的创建型模式。一般情况下,工厂模式分为三种:简单工厂、工厂方法和抽象工厂,下面慢慢举例介绍下。

简单工厂

考虑一个加密程序的应用场景,一个加密程序可能提供了AES,DES等加密方法,这些加密方式都实现了同一个接口ICipher,它有两个方法分别是 Encript 和 Decript。我们使用加密程序的时候会希望简单的指定加密方式,然后传入原始数据以及必要参数,然后就能得到想要的加密数据。这个功能用简单工厂如何实现呢?

模式结构

简单工厂模式包含一下几个角色:

  • Factory(工厂角色),负责创建所有实例。
  • Product(抽象产品角色),指工厂所创建的实例的基类,在 golang 中通常为接口。
  • ConcreteProduce(具体产品),指工厂所创建的具体实例的类型。

在这个加密程序的例子中,工厂角色的职责是返回加密函数;抽象产品角色是所有加密类的基类,在 golang 中是定义了加密类通用方法的接口;具体产品是指具体的加密类,如 AES、DES 等等。我们可以用 UML 关系图来表示这几个角色之间的关系:

代码设计

依据 UML 关系图,我们可以设计出采用简单工厂模式的加密代码。首先是 ICipher 接口,定义了 Encript 和 Decript 两个方法:

type ICipher interface {
Encrypt([]byte) ([]byte, error)
Decrypt([]byte) ([]byte, error)
}

然后根据这个接口,分别实现 AESCipher 和 DESCipher 两个加密类。

AESCipher:

type AESCipher struct {
} func NewAESCipher() *AESCipher {
return &AESCipher{}
} func (c AESCipher) Encrypt(data []byte) ([]byte, error) {
return nil, nil
} func (c AESCipher) Decrypt(data []byte) ([]byte, error) {
return nil, nil
}

DESCipher:

type DESCipher struct {
} func NewDesCipher() *DESCipher {
return &DESCipher{}
} func (c DESCipher) Encrypt(data []byte) ([]byte, error) {
return nil, nil
} func (c DESCipher) Decrypt(data []byte) ([]byte, error) {
return nil, nil
}

最后是一个工厂角色,根据传入的参数返回对应的加密类,Java 需要实现一个工厂类,这里我们用一个函数来做加密类工厂:

func CipherFactory(cType string) ICipher {
switch cType {
case "AES":
return NewAESCipher()
case "DES":
return NewDesCipher()
default:
return nil
}
}

这样,通过调用 CipherFactory 传入所需的加密类型,就可以得到所需要的加密类实例了。

func TestCipherFactory(t *testing.T) {
c := CipherFactory("RSA")
if c != nil {
t.Fatalf("unsupport RSA")
} c = CipherFactory("AES")
if reflect.TypeOf(c) != reflect.TypeOf(&AESCipher{}) {
t.Fatalf("cipher type should be AES")
} c = CipherFactory("DES")
if reflect.TypeOf(c) != reflect.TypeOf(&DESCipher{}) {
t.Fatalf("cipher type should be DES")
}
}

小结

简单工厂将业务代码和创建实例的代码分离,使职责更加单一。不过,它将所有创建实例的代码都放到了 CipherFactory 中,当加密类增加的时候会增加工厂函数的复杂度,产品类型增加时需要更新工厂函数这一操作也是违反了“开闭原则”,所以简单工厂更适合负责创建的对象比较少的场景。

工厂方法

为了让代码更加符合“开闭原则”,我们可以给每个产品都增加一个工厂子类,每个子类生成具体的产品实例,将工厂方法化,也就是现在要介绍的工厂方法模式。

模式结构

工厂方法和和简单工厂相比,将工厂角色细分成抽象工厂和具体工厂:

  • Product(抽象产品):定义产品的接口。
  • ConcreteFactory(具体产品):具体的产品实例。
  • Factory(抽象工厂):定义工厂的接口。
  • ConcreteFactory(具体工厂):实现抽象工厂,生产具体产品。

可以使用如下的 UML 图来表示这几个角色直接的关系:

代码设计

抽象产品角色和具体产品角色就不再定义了,和简单工厂相同,具体展示一下抽象工厂角色和具体工厂角色。

抽象工厂角色定义了一个方法,用于创建对应的产品:

type ICipherFactory interface {
GetCipher() ICipher
}

根据这个接口,分别定义出 AESCipherFactory、和 DESCipherFactory 两个子类工厂。

AESCipherFactory

type AESCipherFactory struct {
} func (AESCipherFactory) GetCipher() ICipher {
return NewAESCipher()
} func NewAESCipherFactory() *AESCipherFactory {
return &AESCipherFactory{}
}

DESCipherFactory

type DESCipherFactory struct {
} func (DESCipherFactory) GetCipher() ICipher {
return NewDESCipher()
} func NewDESCipherFactory() *DESCipherFactory {
return &DESCipherFactory{}
}

然后编写一个单元测试来检验我们的代码:

func TestCipherFactory(t *testing.T) {
var f ICipherFactory = NewAESCipherFactory()
if reflect.TypeOf(f.GetCipher()) != reflect.TypeOf(&AESCipher{}) {
t.Fatalf("should be AESCipher")
} f = NewDESCipherFactory()
if reflect.TypeOf(f.GetCipher()) != reflect.TypeOf(&DESCipher{}) {
t.Fatalf("should be DESCipher")
}
}

小结

在工厂方法模式中,定义了一个工厂接口,然后根据各个产品子类定义实现这个接口的子类工厂,通过子类工厂来返回产品实例。这样修改创建实例代码只需要修改子类工厂,新增实例时只需要新增具体工厂和具体产品,而不需要修改其它代码,符合“开闭原则”。不过,当具体产品较多的时候,系统中类的数量也会成倍的增加,一定程度上增加了系统的复杂度。而且,在实际使用场景中,可能还需要使用反射等技术,增加了代码的抽象性和理解难度。

抽象工厂

下面再用加密这个例子可能不太好,不过我们假设需求都合理吧。现在需求更加细化了,分别需要 64 位 key 和 128 位 key 的 AES 加密库以及 64 位 key 和 128 位 key 的 DES 加密库。如果使用工厂方法模式,我们一共需要定义 4 个具体工厂和 4 个具体产品。

AESCipher64
AESCipher128
AESCipherFactory64
AESCipherFactory128
DESCipher64
DESCipher128
DESCipherFactory64
DESCipherFactory128

这时候,我们可以把有关联性的具体产品组合成一个产品组,例如AESCipher64 和 AESCipher128,让它们通过同一个工厂 AESCipherFactory 来生产,这样就可以简化成 2 个具体工厂和 4 个具体产品

AESCipher64
AESCipher128
AESCipherFactory
DESCipher64
DESCipher128
DESCipherFactory

这就是抽象工厂模式。

模式结构

抽象工厂共有 4 个角色:

  • AbstractFactory(抽象工厂):定义工厂的接口。
  • ConcreteFactory(具体工厂):实现抽象工厂,生产具体产品。
  • AbstractProduct(抽象产品):定义产品的接口。
  • Product(具体产品):具体的产品实例。

根据角色定义我们可以画出抽象工厂的 UML 关系图:

代码设计

抽象产品和具体产品的定义与工厂方法类似:

抽象产品

type ICipher interface {
Encrypt(data, key[]byte) ([]byte, error)
Decrypt(data, key[]byte) ([]byte, error)
}

AESCipher64

type AESCipher64 struct {
} func NewAESCipher64() *AESCipher64 {
return &AESCipher64{}
} func (AESCipher64) Encrypt(data, key []byte) ([]byte, error) {
return nil, nil
} func (AESCipher64) Decrypt(data, key []byte) ([]byte, error) {
return nil, nil
}

AESCipher128


type AESCipher128 struct {
} func NewAESCipher128() *AESCipher128 {
return &AESCipher128{}
} func (AESCipher128) Encrypt(data, key []byte) ([]byte, error) {
return nil, nil
} func (AESCipher128) Decrypt(data, key []byte) ([]byte, error) {
return nil, nil
}

AESCipher128

type c struct {
} func NewDESCipher64() *DESCipher64 {
return &DESCipher64{}
} func (DESCipher64) Encrypt(data, key []byte) ([]byte, error) {
return nil, nil
} func (DESCipher64) Decrypt(data, key []byte) ([]byte, error) {
return nil, nil
}

DESCipher128

type DESCipher128 struct {
} func NewDESCipher128() *DESCipher128 {
return &DESCipher128{}
} func (DESCipher128) Encrypt(data, key []byte) ([]byte, error) {
return nil, nil
} func (DESCipher128) Decrypt(data, key []byte) ([]byte, error) {
return nil, nil
}

抽象工厂角色和工厂方法相比需要增加 GetCipher64 和 GetCipher128 两个方法定义:

type ICipherFactory interface {
GetCipher64() ICipher
GetCipher128() ICipher
}

然后分别实现 AESCipherFactory 和 DesCipherFactory 两个具体工厂:

AESCipherFactory

type AESCipherFactory struct {
} func (AESCipherFactory) GetCipher64() ICipher {
return NewAESCipher64()
} func (AESCipherFactory) GetCipher128() ICipher {
return NewAESCipher128()
} func NewAESCipherFactory() *AESCipherFactory {
return &AESCipherFactory{}
}

DESCipherFactory

type DESCipherFactory struct {
} func (DESCipherFactory) GetCipher64() ICipher {
return NewDESCipher64()
} func (DESCipherFactory) GetCipher128() ICipher {
return NewDESCipher128()
} func NewDESCipherFactory() *DESCipherFactory {
return &DESCipherFactory{}
}

编写单元测试验证我们的代码:

func TestAbstractFactory(t *testing.T) {
var f = NewCipherFactory("AES")
if reflect.TypeOf(f.GetCipher64()) != reflect.TypeOf(&AESCipher64{}) {
t.Fatalf("should be AESCipher64")
} if reflect.TypeOf(f.GetCipher128()) != reflect.TypeOf(&AESCipher128{}) {
t.Fatalf("should be AESCipher128")
} f = NewCipherFactory("DES")
if reflect.TypeOf(f.GetCipher64()) != reflect.TypeOf(&DESCipher64{}) {
t.Fatalf("should be DESCipher64")
} if reflect.TypeOf(f.GetCipher128()) != reflect.TypeOf(&DESCipher128{}) {
t.Fatalf("should be DESCipher128")
}
}

小结

抽象工厂模式也符合单一职责原则和开闭原则,不过需要引入大量的类和接口,使代码更加复杂。并且,当增加新的具体产品时,需要修改抽象工厂和所有的具体工厂。

总结

今天介绍了创建型模式之工厂模式,工厂模式包括简单工厂、工厂方法和抽象工厂。简单工厂的复杂性比较低,但是不像工厂方法和抽象工厂符合单一职责原则和开闭原则。实际使用时,通常会选择符合开闭原则,复杂度也不是特别高的工厂方法。如果有特别需求可以选择使用抽象工厂。

[Design Pattern With Go]设计模式-工厂模式的更多相关文章

  1. .NET设计模式: 工厂模式

    .NET设计模式: 工厂模式(转) 转自:http://www.cnblogs.com/bit-sand/archive/2008/01/25/1053207.html   .NET设计模式(1): ...

  2. 【设计模式】Java设计模式 -工厂模式

    [设计模式]Java设计模式 -工厂模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 一个有梦有戏的人 @怒放吧德德 分享学习心得,欢迎指正,大家一起学习成长! 目 ...

  3. [03]Go设计模式:工厂模式(Factory Pattern)

    目录 工厂模式 一.简介 二.代码 三.参考资料 工厂模式 一.简介 工厂模式(Factory Pattern)是软件设计中最常用的设计模式之一.这种类型的设计模式属于创建型模式,它提供了一种创建对象 ...

  4. 设计模式-工厂模式(Factory Pattern)

    本文由@呆代待殆原创,转载请注明出处. 工厂模式遵循的设计原则之一:找出代码中常变化的部分,并把这一部分分离出来.(Dependency Inversion Principle) 工厂模式简述 当我们 ...

  5. 设计模式 - 工厂模式(factory pattern) 具体解释

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/u012515223/article/details/27081511 工厂模式(factory pa ...

  6. 23种设计模式--工厂模式-Factory Pattern

    一.工厂模式的介绍       工厂模式让我们相到的就是工厂,那么生活中的工厂是生产产品的,在代码中的工厂是生产实例的,在直白一点就是生产实例的类,代码中我们常用new关键字,那么这个new出来的实例 ...

  7. [Head First设计模式]饺子馆(冬至)中的设计模式——工厂模式

    系列文章 [Head First设计模式]山西面馆中的设计模式——装饰者模式 [Head First设计模式]山西面馆中的设计模式——观察者模式 [Head First设计模式]山西面馆中的设计模式— ...

  8. javascript 设计模式-----工厂模式

    所谓的工厂模式,顾名思义就是成批量地生产模式.它的核心作用也是和现实中的工厂一样利用重复的代码最大化地产生效益.在javascript中,它常常用来生产许许多多相同的实例对象,在代码上做到最大的利用. ...

  9. C#设计模式-工厂模式

    引入人.工厂.和斧子的问题 原始社会时,劳动社会基本没有分工,需要斧子的人(调用者)只好自己去磨一把斧子,每个人拥有自己的斧子,如果把大家的石斧改为铁斧,需要每个人都要学会磨铁斧的本领,工作效率极低. ...

随机推荐

  1. CSS & Architecture

    CSS & Architecture https://sass-guidelin.es/#architecture https://sass-guidelin.es/#the-7-1-patt ...

  2. 小程序 怎么发 beta 版本

    小程序 怎么发 beta 版本 微信 https://developers.weixin.qq.com/miniprogram/dev/devtools/mydev.html 小程序助手 支付宝 ht ...

  3. SVG to GeoJSON

    SVG to GeoJSON GEOJSON https://geojson.org/ http://geojson.io/ https://github.com/mapbox/geojson.io/ ...

  4. “Fatal error: Unable to find local grunt.” when running “grunt” command

    下载到本地 >npm install grunt >grunt 命令行运行:grunt,出现以下问题: 这些是Gruntfile.js中引用的,依次安装: npm install grun ...

  5. Techme INC解读基因魔剪,带来的是机遇还是风险?

    10月7日,诺贝尔化学奖颁给了法国美国生物学家Jennifer Doudna和生物化学家Emmanuelle Charpentier,以表彰她们对新一代基因技术CRISPR的贡献,全网沸腾. CRIS ...

  6. 【python接口自动化】- 正则用例参数化

    ​ 我们在做接口自动化的时候,处理接口依赖的相关数据时,通常会使用正则表达式来进行提取相关的数据. ​ 正则表达式,又称正规表示式.正规表示法.正规表达式.规则表达式.常规表示法(Regular Ex ...

  7. Python 爬虫使用动态切换ip防止封杀

    对于爬虫被封禁 ! 爬虫一般来说只要你的ip够多,是不容易被封的. 一些中小网站要封杀你,他的技术成本也是很高的,因为大多数网站没有vps,他们用的是虚拟空间或者是sae,bae这样的paas云. 其 ...

  8. 上天的源码要不要——GitHub 热点速览 v.21.08

    作者:HelloGitHub-小鱼干 前几天,"机智号" 所用的飞行软件框架 F´ 被 NASA 开源了,想看 F´ 这个嵌入式的代码不妨考虑下 Sourcetrail 这个神器, ...

  9. 【不在混淆的C】指针函数、函数指针、回调函数

    一.指针函数 函数的返回值是指针类型. int* fun(int a,int b); 指针函数使用: 返回字符串 这里要注意,"1234567890abc"是字符串常量,*p指向的 ...

  10. 重量级锁synchronized的优化----自旋锁、自适应自旋锁、锁消除、锁粗化

    synchronized是重量级锁,效率不高.但在jdk 1.6中对synchronize的实现进行了各种优化,使得它显得不是那么重了.jdk1.6对锁的实现引入了大量的优化,如自旋锁.自适应自旋锁. ...