工厂模式

一般情况下,工厂模式分为三种更加细分的类型:简单工厂、工厂方法和抽象工厂。不过,在GoF的《设计模式》一书中,它将简单工厂模式看作是工厂方法模式的一种特例,所以工厂模式只被分成了工厂方法和抽象工厂两类。

在这三种细分的工厂模式中,简单工厂、工厂方法原理比较简单,在实际的项目中也比较常用。我们这里来重点的介绍下这两种。

简单工厂模式(Simple Factory)

定义

定义一个工厂类,它可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类。

因为在简单工厂模式用于创建实例的方法是静态的方法,因此简单工厂模式又被称为静态工厂方法模式,它属于类创建型模式。

优点

  • 工厂类含有必要的判断逻辑,可以决定在什么时候创建哪一个产品类的实例,客户端可以免除直接创建产品对象的责任,而仅仅“消费”产品;简单工厂模式通过这种做法实现了对责任的分割,它提供了专门的工厂类用于创建对象。

  • 客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可,对于一些复杂的类名,通过简单工厂模式可以减少使用者的记忆量。

  • 通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。

缺点

  • 由于工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都要受到影响。

  • 使用简单工厂模式将会增加系统中类的个数,在一定程序上增加了系统的复杂度和理解难度。

  • 系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展和维护。

  • 简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。

适用范围

工厂类负责创建的对象比较少,客户只知道传入了工厂类的参数,对于始何创建对象(逻辑)不关心。

代码实现

package main

import "fmt"

func main() {
f := getFruit("apple")
fmt.Println(f.Fruit())
} type FruitFactory interface {
Fruit() string
} func getFruit(t string) FruitFactory {
switch t {
case "apple":
return &apple{}
case "banana":
return &banana{}
} return nil
} type apple struct{} func (*apple) Fruit() string {
return "我是苹果,我很好吃"
} type banana struct{} func (*banana) Fruit() string {
return "我是香蕉,我最好吃了"
}

总结下:

主要是通过 if 来判断逻辑,当我们有新的实现需要加入,只需要添加对应的 if 判断就好了。

UML 类图

工厂方法模式(Factory Method)

定义

工厂方法模式(英语:Factory method pattern)是一种实现了“工厂”概念的面向对象设计模式。就像其他创建型模式一样,它也是处理在不指定对象具体类型的情况下创建对象的问题。工厂方法模式的实质是“定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类。工厂方法让类的实例化推迟到子类中进行。”

优点

  • 一个调用者想创建一个对象,只要知道其名称就可以了。

  • 扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。

  • 屏蔽产品的具体实现,调用者只关心产品的接口。

缺点

每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。

适用范围

当对象的创建逻辑比较复杂,不只是简单的 new 一下就可以,而是要组合其他类对象,做各种初始化操作的时候,推荐使用工厂方法模式,将复杂的创建逻辑拆分到多个工厂类中,让每个工厂类都不至于过于复杂。

代码实现

package main

import "fmt"

func main() {
apple := appleFactory{}
fmt.Println(apple.Fruit()) banana := bananaFactory{}
fmt.Println(banana.Fruit())
} type Fruit interface {
Fruit() string
} type appleFactory struct{} func (*appleFactory) Fruit() string {
return "我是苹果,我很好吃"
} type bananaFactory struct{} func (*bananaFactory) Fruit() string {
return "我是香蕉,我最好吃了"
}

总结:

工厂方法模式实现的时,客户端需要决定实例化哪一个工厂来决定选择那种水果,还是需要判断的,只不过这个判断交给客户端进行了。简单工厂是修改工厂类,这里只需要修改客户端。

UML 类图

抽象工厂模式(Abstract Factory)

定义

抽象工厂模式(Abstract Factory),提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

优点

抽象工厂模式除了具有工厂方法模式的优点外,最主要的优点就是可以在类的内部对产品族进行约束。所谓的产品族,一般或多或少的都存在一定的关联,抽象工厂模式就可以在类内部对产品族的关联关系进行定义和描述,而不必专门引入一个新的类来进行管理。

  • 易于交换产品系列,由于具体的工场类,在使用的时候只需要在应用中初始化一次,所以改变工厂就很简单,只需要改变具体地工厂就能使用对应的配置信息。

  • 它让具体地创建过程和客户端分离,客户端通过他们的抽象接口操作实例,产品的具体类名也和具体工厂分离,不会出现在客户端代码中。

缺点

抽象工厂模式在于难于应付“新对象”的需求变动。难以支持新种类的产品。难以扩展抽象工厂以生产新种类的产品。这是因为抽象工厂几乎确定了可以被创建的产品集合,支持新种类的产品就需要扩展该工厂接口,这将涉及抽象工厂类及其所有子类的改变。

适用范围

  • 1.一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有形态的工厂模式都是重要的。

  • 2.这个系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。

  • 3.同属于同一个产品族的产品是在一起使用的,这一约束必须在系统的设计中体现出来。(比如:Intel主板必须使用Intel CPU、Intel芯片组)

  • 4.系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于实现。

代码实现

package main

import "fmt"

func main() {
f := WuhanFruitFactory{}
b := f.ChooseApple()
b.Fruit()
} type FruitInterface interface {
ChooseApple() ProductInterface
ChooseBanana() ProductInterface
} type ProductInterface interface {
Fruit()
} type HainanApple struct {
} func (h HainanApple) Fruit() {
fmt.Println("我是苹果,来自海南")
} type HainanBanana struct {
} func (h HainanBanana) Fruit() {
fmt.Println("我是香蕉,来自海南")
} type WuhanApple struct {
} func (w WuhanApple) Fruit() {
fmt.Println("我是苹果,来自武汉")
} type WuhanBanana struct {
} func (w WuhanBanana) Fruit() {
fmt.Println("我是香蕉,来自武汉")
} type WuhanFruitFactory struct {
} func (w WuhanFruitFactory) ChooseApple() ProductInterface {
return WuhanApple{}
} func (w WuhanFruitFactory) ChooseBanana() ProductInterface {
return WuhanBanana{}
} type HainanFruitFactory struct {
} func (gd HainanFruitFactory) ChooseApple() ProductInterface {
return HainanApple{}
} func (gd HainanFruitFactory) ChooseBanana() ProductInterface {
return HainanBanana{}
}

UML 类图

总结:

这里抽象出了两个工厂,然后每个工厂中实现自己的方法。

客户端在使用的时候只需要在应用中初始化一次,所以改变工厂就很简单,只需要改变具体地工厂就能使用对应的配置信息。

针对抽象工厂模式,我们会声明那个工厂,当然进一步扩展,我们可以结合依赖注入,上游将工厂注入进来,下游根据注入的工厂进行初始化,这样就更加灵活了,依赖注入模式,我们后面在接着分析。

参考

【工厂方法模式】https://wiki.jikexueyuan.com/project/java-design-pattern/factory-pattern.html

【抽象工厂模式】https://refactoringguru.cn/design-patterns/abstract-factory

【极客时间】设计模式之美

【抽象工厂】https://www.liaoxuefeng.com/wiki/1252599548343744/1281319134822433

【简单工厂模式,工厂方法模式和抽象工厂模式的异同】https://blog.csdn.net/gatieme/article/details/17525805

【大话设计模式】https://book.douban.com/subject/2334288/

【工厂模式】https://boilingfrog.github.io/2021/10/12/基于go学习工厂模式/

基于go语言学习工厂模式的更多相关文章

  1. IOS之Objective-C学习 工厂模式

    工厂模式在父类里声明(可实现)创建对象的一个接口,让子类决定实例化哪个类,也就是说让一个类的实例化延迟到子类中生产. 工厂模式一般用于在不同地方创建对象和项目部署依赖多个数据库的时候. 工厂模式有三种 ...

  2. [javaSE] 看知乎学习工厂模式

    factory的“本质”就是根据不同的输入创建出不同类型的对象. 引入factory的原因就是你需要根据不同的输入创建不同类型的对象. 简单工厂模式相当于是一个工厂中有各种产品,创建在一个类中,客户无 ...

  3. C++模式学习------工厂模式

    工厂模式属于创建型模式,大致可以分为简单工厂模式.抽象工厂模式. 简单工厂模式,它的主要特点是需要在工厂类中做判断,从而创造相应的产品. enum PTYPE { ProdA = , ProdB = ...

  4. 从BWM生产学习工厂模式

    工厂模式应用非常之广,在JDK底层源码以及各大主流框架中随处可见,一般以Factory结尾命名的类,比如Mybatis中的SqlSessionFactory,Spring中的BeanFactory等, ...

  5. 设计模式学习——工厂模式(Factory Pattern)

    1.有一个工厂,专门生产不同品牌的汽车.当有人需要从此工厂提货的时候,只需要告诉他,要什么品牌的,就可以了,并不关心这些车是怎么生产出来的. 2.以上方式,如果增加品牌的时候,也要修改工厂,有点麻烦. ...

  6. 由XML解析学习工厂模式

    代码段1: startupData = new StartupData(); /* 设定自定义的MyHandler给XMLReader */ StartupXMLHandler startupData ...

  7. 基于C#反射机制的工厂模式

    简单介绍 反射提供了描写叙述程序集.模块和类型的对象(Type 类型). 能够使用反射动态创建类型的实例,将类型绑定到现有对象,或从现有对象获取类型并调用其方法或訪问其字段和属性. 假设代码中使用了特 ...

  8. 设计模式(C#)——02简单工厂模式

    推荐阅读:  我的CSDN  我的博客园  QQ群:704621321       工厂模式主要是为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来.通俗来说,你只关心怎么用,不用关心怎么做 ...

  9. java模式-工厂模式

    今天在学习工厂模式,从最简单的简单工厂模式开始. 我们现在需要通过工厂Factory生产A,B两款产品(都是产品,实现了接口Product). 产品A: public class A implemen ...

随机推荐

  1. CentOS7中apache的部署与配置

    一.apache的部署 输入命令 yum list | grep httpd 查看可安装的软件包,选择"httpd.x86_64"安装. 输入命令 yum install http ...

  2. Docker安装MySQL集群【读写分离】

    Centos7系统Docker安装 目录 Centos7系统Docker安装 目录 Centos7系统Docker安装 1.下载mysql镜像 2.创建Master实例并启动 参数说明 3.创建Sla ...

  3. Ajax的GET,POST方法传输数据和接收返回数据

    //首先创建一个Ajax对象 function ajaxFunction(){ var xmlHttp; try{ // Firefox, Opera 8.0+, Safari xmlHttp=new ...

  4. vscode 1.32.x按下鼠标左键无法拖曳选择,而旧一点的版本1.30.2可以

    最近升级vscode后,无法通过鼠标左键选择文本,恢复到旧版本1.30.2就可以了. 另外:1.32.x和1.31.1都不正常 进一步测试发现:在中文输入法下(无论中英输入模式),都有问题:切换到纯英 ...

  5. 一个double free相关问题的澄清

    引言 前一阵定位 Oracle 的 OCI 接口相关的一个内存释放问题,在网上看到了链接如下的这篇文章: 一个C++bug引入的许多知识 看到后面说 vector 里的两个单元里的内部成员指针地址是一 ...

  6. Java同步之线程池详解

    带着问题阅读 1.什么是池化,池化能带来什么好处 2.如何设计一个资源池 3.Java的线程池如何使用,Java提供了哪些内置线程池 4.线程池使用有哪些注意事项 池化技术 池化思想介绍 池化思想是将 ...

  7. Qt5完美解决 界面显示中文乱码

    最近在学习Qt,可是一直头疼于中文乱码问题,上网搜了一下,很多都是Qt4中使用如下方法: QTextCodec *codec = QTextCodec::codecForName("gbk& ...

  8. Python面向对象编程及内置方法

    在程序开发中,要设计一个类,通常需要满足以下三个要求: [1]类名 这类事物的名字,满足大驼峰命名法 [2]属性 这类事物具有什么样的特征 [3]方法 这类事物具有什么样的行为 定义简单的类: 定义只 ...

  9. IPv6 QoS 多媒体应用:性能分析 (上)

    IPv6 QoS 多媒体应用:性能分析   Assured Forwarding (AF):保证转发 Expedited Forwarding (EF):快速转发 Traffic aggregatio ...

  10. 菜鸟入门Linux之路(方法论浅谈)

    Linux是为人熟知的OS之王,已"统治"世界.要想学好绝非易事. 作为菜鸟,可以与Linux亲密接触的方法很多,如视频.书籍.各种企培资料等等,如今的在线教育也如火如荼. 总结说 ...