文档章节

设计模式学习笔记(五):工厂方法模式

氷泠
 氷泠
发布于 07/16 21:58
字数 1775
阅读 21
收藏 0

行业解决方案、产品招募中!想赚钱就来传!>>>

1 概述

1.1 引言

尽管简单工厂模式实现了对象的创建和使用分离,但是仍然存在以下两个问题:

  • 工厂类过于庞大,包含了大量的判断代码,导致维护和测试难度增大
  • 系统扩展不灵活,如果增加了新的产品类型,必须修改静态工厂方法的业务逻辑,违反了开闭原则
  • 具体产品与工厂类之间的耦合度高,严重影响了系统的灵活性和扩展性

一个更好的办法是使用工厂方法模式。

1.2 定义

工厂方法模式:定义一个用于创建对象的接口,让子类决定将哪一个类实例化。

工厂方法又简称工厂模式或虚拟构造器模式或多态工厂模式,让一个类的实例化延迟到其子类,是一种类创建型模式。

1.3 结构图

在这里插入图片描述

1.4 角色

  • Product(抽象产品):定义产品的接口,是工厂方法模式所创建的超类型,也就是产品对象的公共父类
  • ConcreteProduct(具体产品):实现了抽象产品接口,某种类型的具体产品由专门的具体工厂创建,具体工厂与具体产品一一对应
  • Factory(抽象工厂):在抽象工厂类中,声明了工厂方法,用于返回一个产品。抽象工厂是工厂方法模式的核心,所有创建对象的工厂类都必须实现该接口
  • ConcreteFactory(具体工厂):它是抽象工厂类的子类,实现了抽象工厂中定义的工厂方法,并可由客户端调用,返回一个具体产品类的实例

2 典型实现

2.1 步骤

  • 定义抽象产品:定义为接口/抽象类,是所有具体产品的父类
  • 定义具体产品:实现/继承抽象产品,一个类对应与一个具体产品
  • 定义抽象工厂:定义为接口/抽象类,声明工厂方法,但不需要实现,在运行时确定具体工厂
  • 定义具体工厂:实现/继承抽象工厂,实现其中的工厂方法

2.2 抽象产品

这里定义为接口:

interface Product
{
    void method();
}

2.3 具体产品

两个示例具体产品:

class ConcreteProductA implements Product
{
    public void method()
    {
        System.out.println("Concrete Product A");
    }
}

class ConcreteProductB implements Product
{
    public void method()
    {
        System.out.println("Concrete Product B");
    }
}

2.4 抽象工厂

这里定义为接口:

interface Factory
{
    Product get();
}

抽象工厂中声明了工厂方法但没有实现,交由子类具体工厂负责。

2.5 具体工厂

class ConcreteFactoryA implements Factory
{
    public Product get()
    {
        return new ConcreteProductA();
    }
}

class ConcreteFactoryB implements Factory
{
    public Product get()
    {
        return new ConcreteProductB();
    }
}

一个具体产品对应一个具体工厂,每一个具体工厂返回不同的具体产品。

2.6 客户端

客户端针对抽象产品以及抽象工厂进行编程,无需知道具体产品的类名,只需要知道具体产品对应的工厂,即可获取具体产品:

Factory factory = new ConcreteFactoryA();
Product product = factory.get();
product.method();
factory = new ConcreteFactoryB();
product = factory.get();
product.method();

3 实例

日志记录器的设计:该记录器可以通过多种途径保存系统的运行日志,例如文件记录或者数据库记录,使用工厂方法模式进行设计。

设计如下:

  • 抽象产品:Logger
  • 具体产品:DatabaseLogger+FileLogger
  • 抽象工厂:LoggerFactory
  • 具体工厂:DatabaseLoggerFactory+FileLoggerFactory

代码如下:

public class Test
{
    public static void main(String[] args) {
        LoggerFactory factory = new FileLoggerFactory();        
        Logger logger = factory.createLogger();
        logger.log();
    }
}

//抽象产品
interface Logger
{
    void log();
}

//具体产品:DatabaseLogger
class DatabaseLogger implements Logger
{
    public void log()
    {
        System.out.println("数据库日志记录");
    }
}

//具体产品:FileLogger
class FileLogger implements Logger
{
    public void log()
    {
        System.out.println("文件日志记录");
    }
}

//抽象工厂
interface LoggerFactory
{
    Logger createLogger();
}

//具体工厂:DatabaseLoggerFactory
class DatabaseLoggerFactory implements LoggerFactory
{
    public Logger createLogger()
    {
        return new DatabaseLogger();
    }
}

//具体工厂:FileLoggerFactory
class FileLoggerFactory implements LoggerFactory
{
    public Logger createLogger()
    {
        return new FileLogger();
    }
}

4 隐藏

可以把抽象工厂设置为抽象类,工厂方法直接可以对客户端隐藏,也就是说可以直接通过抽象工厂调用具体产品类的业务方法,客户端无需创建具体产品,直接通过工厂类调用即可,代码修改如下(抽象产品以及具体产品类不用修改):

//抽象工厂
abstract class LoggerFactory
{
    public void log()
    {
        this.createLogger().log();
    }
    public abstract Logger createLogger();
}

//具体工厂:DatabaseLoggerFactory
class DatabaseLoggerFactory extends LoggerFactory
{
    public Logger createLogger()
    {
        return new DatabaseLogger();
    }
}

//具体工厂:FileLoggerFactory
class FileLoggerFactory extends LoggerFactory
{
    public Logger createLogger()
    {
        return new FileLogger();
    }
}

public class Test
{
    public static void main(String[] args) {
        LoggerFactory factory = new FileLoggerFactory();
        factory.log();
    }
}

5 主要优点

  • 封装细节:工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节,用户只需关心所需产品对应的工厂,无需关心创建细节,甚至无须知道具体产品类的类名
  • 多态:工厂方法的多态性能够让工厂可以自主确定创建何种产品对象,而如何创建对象的细节则完全封装在具体工厂内部
  • 扩展性好:加入新产品时无须修改抽象工厂,抽象产品的接口,也无须修改客户端与其他的具体产品和具体工厂,只需要增加一个具体工厂以及具体产品,系统扩展性很好,完全符合开闭原则

6 主要缺点

  • 类数量多:在添加新产品时,需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统中类的个数将成对增加,一定程度上增加了系统的复杂度,有更多的类需要编译和运行,给系统带来额外开销
  • 增加理解难度:基于系统的扩展性需要引入抽象层,在客户端中均使用了抽象层的定义,增加了系统的抽象性以及理解难度

7 适用场景

  • 客户端不知道其所需要的对象的类:在工厂方法模式中,客户端不需要知道具体的产品类名,只需要知道所对应的工厂即可
  • 抽象工厂类通过子类来指定创建哪个对象:工厂方法模式中,抽象工厂类只需要提供一个创建产品的接口,而有其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更加容易扩展

8 总结

在这里插入图片描述

如果觉得文章好看,欢迎点赞。

同时欢迎关注微信公众号:氷泠之路。

在这里插入图片描述

氷泠

氷泠

粉丝 0
博文 154
码字总数 238773
作品 0
广州
私信 提问
加载中
请先登录后再评论。
我的架构演化笔记 功能1: 基本的用户注册

“咚咚”,一阵急促的敲门声, 我从睡梦中惊醒,我靠,这才几点,谁这么早, 开门一看,原来我的小表弟放暑假了,来南京玩,顺便说跟我后面学习一个网站是怎么做出来的。 于是有了下面的一段...

强子哥哥
2014/05/31
976
3
Swift百万线程攻破单例(Singleton)模式

一、不安全的单例实现 在上一篇文章我们给出了单例的设计模式,直接给出了线程安全的实现方法。单例的实现有多种方法,如下面: class SwiftSingleton { } 这段代码的实现,在shared中进行条...

一叶博客
2014/06/20
3.3K
16
Nutch学习笔记4-Nutch 1.7 的 索引篇 ElasticSearch

上一篇讲解了爬取和分析的流程,很重要的收获就是: 解析过程中,会根据页面的ContentType获得一系列的注册解析器, 依次调用每个解析器,当其中一个解析成功后就返回,否则继续执行下一个解...

强子哥哥
2014/06/26
712
0
桌面即时贴软件--GloboNote

GloboNote 是一个桌面记事软件,可帮你创建待办事宜、提醒和其他笔记信息。无限制即时贴的数量,可分组整理,支持搜索,可定制文本的显示格式(字体、颜色和大小),可将某个即时贴始终显示在...

匿名
2013/01/21
6.7K
1
Swing界面分析和调试工具--Swing Inspector

Swing Inspector是一个Java Swing/AWT用户界面分析和调试工具,功能与firebug类似,具有强大的Swing/AWT用户界面分析和调试相关功能。 适用于从java swing初级到高级的所有开发人员,能够快速...

匿名
2013/03/06
3.3K
0

没有更多内容

加载失败,请刷新页面

加载更多

如何在Android中以像素为单位获取屏幕尺寸 - How to get screen dimensions as pixels in Android

问题: I created some custom elements, and I want to programmatically place them to the upper right corner ( n pixels from the top edge and m pixels from the right edge). 我创建......

javail
46分钟前
7
0
如何在不安装Microsoft Office的情况下用C#创建Excel(.XLS和.XLSX)文件?

问题: 如何在不使用运行代码的计算机上安装Excel的情况下使用C#创建Excel电子表格? 解决方案: 参考一: https://stackoom.com/question/dHZ/如何在不安装Microsoft-Office的情况下用C-创...

技术盛宴
今天
7
0
如何使用pip升级所有Python软件包? - How to upgrade all Python packages with pip?

问题: Is it possible to upgrade all Python packages at one time with pip ? 是否可以通过pip一次升级所有Python软件包? Note : that there is a feature request for this on the off......

法国红酒甜
今天
9
0
活体检测+合成图鉴别面前,人脸“照片活化”黑产攻击一秒被擒

本文作者:y****n 如今,随着人脸技术的日趋成熟,新兴娱乐文化得到了极大的推动,尤其是随着 DeepFake、FaceSwap 等人脸编辑及生成技术的发展,虚拟主播、人脸合成带给人们全新的体验,但同...

百度开发者中心
昨天
12
0
如何在SQL Server中将多行文本合并为单个文本字符串?

问题: Consider a database table holding names, with three rows: 考虑一个包含名称的数据库表,该表具有三行: PeterPaulMary Is there an easy way to turn this into a single str......

富含淀粉
今天
19
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部