当前位置: 首页 > news >正文

设计模式之旅:工厂模式全方位解析

简介


设计模式中与工厂模式相关的主要有三种,它们分别是:

  1. 简单工厂模式(Simple Factory):这不是GoF(四人帮,设计模式的开创者)定义的标准模式,但被广泛认为是工厂模式的一种简化。它实际上是一个类(工厂类),用于创建其他类的实例。客户端不直接创建对象,而是通过工厂类进行创建,这样就隐藏了实例创建的细节。简单工厂模式实际上违背了开闭原则(对扩展开放,对修改关闭),因为每次增加新的产品类时,都需要修改工厂类。
  2. 工厂方法模式(Factory Method):这是一个真正的设计模式,是GoF所定义的。在工厂方法模式中,创建对象的任务被转移到了一个方法中,但这个方法不是由创建者类(也就是工厂类)直接调用的,而是在其子类中实现的,这样的设计允许系统在不修改现有客户端代码的情况下引入新的产品类。这个模式遵循了开闭原则,使得增加新的产品类时,只需添加新的具体工厂类即可。
  3. 抽象工厂模式(Abstract Factory):这是一种更为复杂和灵活的工厂模式。它提供了一个接口,用于创建一系列相关或相互依赖的对象,而无需指定它们具体的类。抽象工厂允许客户端使用抽象的接口来创建一组相关的产品,而不需要知道(或关心)产品的具体类型。这种模式非常适用于系统中产品类的家族很多,而且这些产品是以系列的方式提供的情况。

简单来说,简单工厂模式注重于创建单一产品,工厂方法模式允许类的扩展,通过子类来指定创建哪个对象,而抽象工厂模式则提供一个创建产品家族的接口,使得创建一系列相关或依赖对象的客户端不依赖于产品的具体类。每种模式都有其适用的场景和优缺点,选择哪种模式取决于你面临的问题和设计的需求。

一、简单工厂模式


简单工厂模式(Simple Factory Pattern)是一种创建型设计模式,用于创建对象而不必暴露创建逻辑给客户端,并且通过使用一个共同的接口来指向新创建的对象。这种模式是工厂方法模式的一个特殊实现,但并不是一个真正的设计模式,更多的是一种编程习惯。

目的

简单工厂模式的主要目的是为了实现对象的创建与使用的分离。通过一个单独的工厂类来决定哪种产品类应当被实例化,这样可以减少系统的耦合度,增加系统的灵活性。

如何实现

简单工厂模式通常由三个角色组成:

  1. 工厂类(Factory):一个负责实现创建所有实例的方法,根据不同的参数返回不同类的实例。
  2. 抽象产品类(Product):所有具体产品类的父类,负责描述所有实例所共有的公共接口。
  3. 具体产品类(ConcreteProduct):工厂类所创建的对象类,它们都继承自抽象产品类。

示例

假设我们有一个软件,需要根据不同的文件类型(如文本文件、图像文件)来创建不同的视图来显示内容。我们可以使用简单工厂模式来设计这个功能。

首先,定义一个抽象产品类:

public abstract class FileView {public abstract void display();
}

然后,创建具体产品类:

public class TextView extends FileView {@Overridepublic void display() {System.out.println("Displaying text file");}
}public class ImageView extends FileView {@Overridepublic void display() {System.out.println("Displaying image file");}
}

接下来,创建工厂类:

public class FileViewFactory {// 使用 getFileView 方法获取类型的对象public static FileView getFileView(String fileType) {if (fileType == null) {return null;}if (fileType.equalsIgnoreCase("TEXT")) {return new TextView();} else if (fileType.equalsIgnoreCase("IMAGE")) {return new ImageView();}return null;}
}

最后,客户端代码可以这样使用工厂类:

public class FactoryDemo {public static void main(String[] args) {FileView fileView = FileViewFactory.getFileView("TEXT");fileView.display();fileView = FileViewFactory.getFileView("IMAGE");fileView.display();}
}

优点

  • 工厂类包含必要的逻辑判断,可以决定在什么时候创建哪一个产品的实例。客户端可以免除直接创建对象的责任,而仅仅"消费"产品。
  • 增加新的产品类时,只需要扩展工厂类即可,符合开闭原则(对扩展开放,对修改封闭)。

缺点

  • 工厂类的职责相对过重,增加新的产品时,需要修改工厂类的判断逻辑,违背了开闭原则。
  • 不易于扩展过于复杂的产品结构。如果产品的创建逻辑过于复杂,可能会导致工厂类变得非常庞大,难以维护。

简单工厂模式是理解工厂方法模式和抽象工厂模式的基础,它通过专门的工厂类将创建实例的任务和客户端使用分离,从而实现了系统的解耦和灵活性的提高。

二、工厂方法模式


工厂方法模式(Factory Method Pattern)是一种创建型设计模式,它定义了一个创建对象的接口,但让实例化的工作由其子类去完成。这样的设计模式使得一个类在不改变其代码的情况下增加新类型的产品。它是实现软件框架的一个重要方法,允许用户扩展框架中的部分功能,而无需修改框架的代码。

目的与应用场景

工厂方法模式主要用于以下情况:

  • 当一个类不知道它所必须创建的对象的类的时候。
  • 当一个类希望由它的子类来指定创建对象的时候。
  • 当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪个帮助子类是代理者这一信息局部化的时候。

结构

工厂方法模式主要包含以下四个角色:

  1. 产品(Product):定义了工厂方法所创建的对象的接口,也就是实际产品共有的接口或抽象类。
  2. 具体产品(Concrete Product):实现或继承了产品接口的具体类,这些类将由具体工厂创建,它们之间是继承关系。
  3. 创建者(Creator):声明了工厂方法,该方法返回一个产品类型的对象。创建者也可以定义一个工厂方法的默认实现,返回一个默认的具体产品对象。
  4. 具体创建者(Concrete Creator):重写或实现创建者中的工厂方法,以返回一个具体产品实例。

实现步骤

  1. 定义产品接口:所有具体产品类都应该实现这个接口,以便工厂方法可以返回这些类的实例。
  2. 创建具体产品类:实现产品接口的具体类。
  3. 定义创建者类:包含抽象的工厂方法,该方法应该返回一个产品接口的对象。创建者类通常会包含依赖于抽象产品的代码,而这些产品是由其子类通过工厂方法创建的。
  4. 实现具体创建者:为工厂方法提供实现,创建并返回具体产品实例。

优点

  • 提高了系统的灵活性:因为工厂方法模式把具体产品的创建推迟到子类中,因此增加新的具体产品不需要修改现有类的代码,符合“开闭原则”。
  • 扩展性强:在增加产品时只需要增加具体产品类和对应的具体工厂类,无需修改现有系统,符合“单一职责原则”。

缺点

  • 可能导致类的个数增加:每增加一个具体产品类,就需要增加一个相应的具体工厂类,这会导致系统类的个数成倍增加,增加了系统的复杂度和管理难度。

示例

假设我们有一个日志记录器(Logger)的框架,日志记录器可以是文件日志记录器、数据库日志记录器或网络日志记录器等。在这个例子中,Logger就是产品,文件日志记录器、数据库日志记录器等就是具体产品,LoggerCreator是创建者,具体的文件日志记录器创建者、数据库日志记录器创建者等则是具体创建者。每个具体创建者都知道如何创建相应的具体产品。

工厂方法模式通过这种方式提供了一种将客户端代码从具体产品类中解耦的机制,使得增加新的产品类变得更加简单和灵活。下面是一个使用Java实现的工厂方法模式的简单例子,演示了如何构建一个简单的日志记录器框架。在这个框架中,我们有一个Logger接口,它定义了日志记录功能;有两种具体的日志记录器,分别是FileLoggerConsoleLogger;还有相应的工厂类,FileLoggerFactoryConsoleLoggerFactory,它们负责创建具体的日志记录器实例。

1. 定义产品接口

public interface Logger {void log(String message);
}

2. 创建具体产品类

文件日志记录器:

public class FileLogger implements Logger {@Overridepublic void log(String message) {// 实现将消息记录到文件的逻辑System.out.println("Logging to a file: " + message);}
}

控制台日志记录器:

public class ConsoleLogger implements Logger {@Overridepublic void log(String message) {// 实现在控制台输出日志的逻辑System.out.println("Logging to the console: " + message);}
}

3. 定义创建者(工厂)接口

public abstract class LoggerFactory {public abstract Logger createLogger();
}

4. 实现具体创建者

文件日志工厂:

public class FileLoggerFactory extends LoggerFactory {@Overridepublic Logger createLogger() {// 返回一个新的文件日志记录器对象return new FileLogger();}
}

控制台日志工厂:

public class ConsoleLoggerFactory extends LoggerFactory {@Overridepublic Logger createLogger() {// 返回一个新的控制台日志记录器对象return new ConsoleLogger();}
}

5. 客户端代码

public class FactoryMethodDemo {public static void main(String[] args) {LoggerFactory factory;Logger logger;// 创建文件日志记录器factory = new FileLoggerFactory();logger = factory.createLogger();logger.log("This is a message to the file logger.");// 创建控制台日志记录器factory = new ConsoleLoggerFactory();logger = factory.createLogger();logger.log("This is a message to the console logger.");}
}

在这个例子中,LoggerFactory充当创建者的角色,定义了一个抽象的createLogger方法,用于返回一个Logger对象。FileLoggerFactoryConsoleLoggerFactory是具体的创建者,它们实现了createLogger方法,分别用于创建FileLoggerConsoleLogger对象。这样,客户端代码就可以在不直接实例化日志记录器对象的情况下,通过工厂来获取日志记录器实例,从而实现了客户端代码和具体产品类之间的解耦。

三、抽象工厂模式


抽象工厂模式(Abstract Factory Pattern)是一种创建型设计模式,它提供了一种创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。这个模式的关键点是多个工厂类,这些工厂类负责创建一系列相关的对象。抽象工厂模式通常用于管理产品族的概念,并帮助构建组件库或框架。

抽象工厂模式的组成部分

  1. 抽象工厂(Abstract Factory):提供一个接口,用于创建相关的对象家族。
  2. 具体工厂(Concrete Factory):实现抽象工厂的接口,负责创建具体的产品实例。
  3. 抽象产品(Abstract Product):为一类产品对象声明一个接口。
  4. 具体产品(Concrete Product):实现抽象产品角色所定义的接口。

优点

  • 增加新的具体工厂和产品族很方便,无需修改已有的系统,符合开闭原则。
  • 隔离了具体类的生成,使客户端不需要知道什么被创建。

缺点

  • 添加新的产品对象到现有的工厂需要修改抽象工厂的接口,这将涉及到抽象工厂类以及所有的子类的修改,违反了开闭原则。

Java代码示例

假设我们有一个用于创建UI控件的框架,我们可以用抽象工厂模式来为不同的操作系统创建不同的UI控件。

首先,定义抽象工厂和抽象产品:

interface GUIFactory {Button createButton();Checkbox createCheckbox();
}interface Button {void paint();
}interface Checkbox {void paint();
}

然后,实现具体的工厂和产品:

class WinFactory implements GUIFactory {public Button createButton() {return new WinButton();}public Checkbox createCheckbox() {return new WinCheckbox();}
}class MacFactory implements GUIFactory {public Button createButton() {return new MacButton();}public Checkbox createCheckbox() {return new MacCheckbox();}
}class WinButton implements Button {public void paint() {System.out.println("Render a button in a Windows style.");}
}class MacButton implements Button {public void paint() {System.out.println("Render a button in a Mac style.");}
}class WinCheckbox implements Checkbox {public void paint() {System.out.println("Render a checkbox in a Windows style.");}
}class MacCheckbox implements Checkbox {public void paint() {System.out.println("Render a checkbox in a Mac style.");}
}

最后,客户端代码可以根据需要选择使用哪个具体的工厂实例:

class Application {private Button button;private Checkbox checkbox;public Application(GUIFactory factory) {button = factory.createButton();checkbox = factory.createCheckbox();}public void paint() {button.paint();checkbox.paint();}
}public class Demo {public static void main(String[] args) {Application app = new Application(new WinFactory());app.paint();}
}

这个示例展示了如何使用抽象工厂模式来解耦客户端和具体类的实例化过程,使客户端代码能够无需修改就可以使用不同的产品家族。

相关文章:

  • 【MySQL】多表查询全解-【多表关系/内外自连接/子查询/多表查询案例链接】(可cv代码&案例演示)
  • QT子窗口关闭时自动释放及注意事项
  • VSCode好用插件
  • 手写简易操作系统(十一)--可编程中断控制器8259A
  • Vue-Electron配置及踩坑
  • 每日一题 第六十六期 洛谷 小朋友排队
  • Maven是什么? Maven的概念+作用
  • 计算机网络-HTTP相关知识-RSA和ECDHE及优化
  • Unity类银河恶魔城学习记录11-15 p117 Ice and Fire item Effect源代码
  • 【详细介绍WebKit的结构】
  • 缓存最佳实践
  • Pointnet++改进即插即用系列:全网首发OREPA在线重新参数化卷积,替代普通卷积 |即插即用,提升特征提取模块性能
  • Fractions Again?!(UVA 10976)
  • linux系统编程 线程 p1
  • C#字典学习笔记
  • [译]Python中的类属性与实例属性的区别
  • golang中接口赋值与方法集
  • HTML中设置input等文本框为不可操作
  • JAVA 学习IO流
  • JavaScript创建对象的四种方式
  • Python利用正则抓取网页内容保存到本地
  • React-Native - 收藏集 - 掘金
  • SpiderData 2019年2月23日 DApp数据排行榜
  • Spring-boot 启动时碰到的错误
  • Spring核心 Bean的高级装配
  • Vue.js源码(2):初探List Rendering
  • Vue官网教程学习过程中值得记录的一些事情
  • 包装类对象
  • 工作手记之html2canvas使用概述
  • 名企6年Java程序员的工作总结,写给在迷茫中的你!
  • 前端代码风格自动化系列(二)之Commitlint
  • 如何打造100亿SDK累计覆盖量的大数据系统
  • 数组的操作
  • 主流的CSS水平和垂直居中技术大全
  • Python 之网络式编程
  • Spring第一个helloWorld
  • 新年再起“裁员潮”,“钢铁侠”马斯克要一举裁掉SpaceX 600余名员工 ...
  • 组复制官方翻译九、Group Replication Technical Details
  • ​ 全球云科技基础设施:亚马逊云科技的海外服务器网络如何演进
  • ​水经微图Web1.5.0版即将上线
  • #Z0458. 树的中心2
  • #微信小程序:微信小程序常见的配置传旨
  • (C语言)strcpy与strcpy详解,与模拟实现
  • (Matalb分类预测)GA-BP遗传算法优化BP神经网络的多维分类预测
  • (附源码)springboot猪场管理系统 毕业设计 160901
  • (论文阅读笔记)Network planning with deep reinforcement learning
  • (牛客腾讯思维编程题)编码编码分组打印下标题目分析
  • (算法二)滑动窗口
  • (五)大数据实战——使用模板虚拟机实现hadoop集群虚拟机克隆及网络相关配置
  • .bat批处理(四):路径相关%cd%和%~dp0的区别
  • .net最好用的JSON类Newtonsoft.Json获取多级数据SelectToken
  • /etc/shadow字段详解
  • :“Failed to access IIS metabase”解决方法
  • @DataRedisTest测试redis从未如此丝滑
  • [2019.2.28]BZOJ4033 [HAOI2015]树上染色