软件设计模式系列之三———工厂方法模式

2023-09-12 11:01:15

1 模式的定义

工厂方法模式是一种常见的设计模式,属于创建型设计模式之一,它在软件工程中用于对象的创建。该模式的主要思想是将对象的创建过程抽象化,将具体对象的实例化延迟到子类中完成,以便在不同情况下可以创建不同类型的对象,而客户端代码不需要知道实际创建的对象类型。

2 举例说明

在这里插入图片描述

不同公司都生产自己的手机,就符合工厂方法模式。比如小米和华为都是手机制造商,而手机则是产品。每家公司都有自己的生产流程和标准,但它们都需要生产手机以满足市场需求。
工厂方法模式中,每家公司都有一个具体的工厂(小米工厂和华为工厂),这些工厂实现了一个共同的抽象工厂接口。抽象工厂定义了一个方法用于生产手机,但具体的手机生产过程由每家公司的工厂来实现。
客户端代码可以根据需要选择使用小米工厂或华为工厂来生产手机。这种方式下,客户端代码无需关心手机的具体制造过程,只需知道如何与抽象工厂交互即可。
工厂方法模式的优点在于它实现了对象的创建和客户端代码的解耦,使得不同的工厂可以独立扩展,适用于多样化的产品生产场景。小米和华为手机的制造就是一个生动的示例,其中工厂方法模式为不同公司的手机生产提供了一种灵活的、可扩展的解决方案

3 结构

工厂方法模式的结构包括以下几个关键角色:

在这里插入图片描述

  • 抽象工厂接口(Factory):这是工厂方法模式的核心部分,它定义了一个或多个工厂方法的接口,这些方法用于创建具体产品对象。通常,这个接口中会包含多个方法,每个方法用于创建不同类型的产品。客户端代码通过调用这些工厂方法来获取产品对象。
  • 具体工厂类(Concrete Factory):这是实现抽象工厂接口的具体类,每个具体工厂类负责创建一类具体产品。它实现了工厂方法,根据客户端的需求创建具体的产品对象。每个具体工厂类通常对应一个特定的产品族。
  • 抽象产品接口(Product):这个接口定义了产品对象的通用属性和方法,所有具体产品类都必须实现这个接口。客户端代码将与抽象产品接口进行交互,而不直接与具体产品类进行交互。
  • 具体产品类(Concrete Product):这些是实际的产品类,它们实现了抽象产品接口,定义了具体产品的属性和行为。每个具体产品类对应一个具体工厂类。
  • 客户端(Client):客户端代码需要创建产品对象,但它不直接与具体产品类或具体工厂类交互。客户端通过调用抽象工厂接口中的工厂方法来创建产品对象,从而实现了解耦的目的。

工厂方法模式的结构允许在不修改客户端代码的情况下引入新的具体产品类和具体工厂类,从而增加系统的可扩展性。客户端只需要知道抽象工厂接口和抽象产品接口,而不需要关心具体的产品类和工厂类,这使得系统更易于维护和扩展。

总结来说,工厂方法模式的结构包括抽象工厂接口、具体工厂类、抽象产品接口、具体产品类以及客户端。这个模式通过将对象的创建委托给具体工厂类来实现对象的创建和客户端与产品之间的解耦。

4 实现步骤

工厂方法模式的实现步骤通常包括以下几个关键步骤:

  • 定义抽象产品接口(Product):定义一个抽象产品接口,它声明了产品对象的通用属性和方法。所有具体产品类都必须实现这个接口。
  • 创建具体产品类(Concrete Product):实现抽象产品接口的具体产品类。每个具体产品类表示一种具体的产品。
  • 定义抽象工厂接口(Factory):创建一个抽象工厂接口,该接口声明了工厂方法,用于创建产品对象。
  • 创建具体工厂类(Concrete Factory):实现抽象工厂接口的具体工厂类,每个具体工厂类负责创建特定类型的产品。在客户端中使用工厂方法:
  • 客户端代码通过具体工厂类的工厂方法来创建产品对象,而不直接实例化具体产品类。

通过上述步骤,您就可以实现工厂方法模式。这种模式允许客户端根据需要选择合适的具体工厂来创建产品,从而实现了解耦和可扩展性。如果需要添加新的产品类型,只需创建新的具体产品类和对应的具体工厂类,而不需要修改现有的客户端代码。

5 代码实现

以下是使用工厂方法模式实现抽象工厂(AbstractFactory)、具体工厂(XiaomiFactoryHuaweiFactory)、抽象产品(AbstractMobile)和具体产品(XiaomiMobileHuaweiMobile)的Java代码示例:

// 抽象产品接口
interface AbstractMobile {
    void displayInfo();
}

// 具体产品类 - 小米手机
class XiaomiMobile implements AbstractMobile {
    @Override
    public void displayInfo() {
        System.out.println("这是小米手机。");
    }
}

// 具体产品类 - 华为手机
class HuaweiMobile implements AbstractMobile {
    @Override
    public void displayInfo() {
        System.out.println("这是华为手机。");
    }
}

// 抽象工厂接口
interface AbstractFactory {
    AbstractMobile createMobile();
}

// 具体工厂类 - 小米工厂
class XiaomiFactory implements AbstractFactory {
    @Override
    public AbstractMobile createMobile() {
        return new XiaomiMobile();
    }
}

// 具体工厂类 - 华为工厂
class HuaweiFactory implements AbstractFactory {
    @Override
    public AbstractMobile createMobile() {
        return new HuaweiMobile();
    }
}

// 客户端
public class Client {
    public static void main(String[] args) {
        // 使用具体工厂创建具体产品
        AbstractFactory xiaomiFactory = new XiaomiFactory();
        AbstractMobile xiaomiMobile = xiaomiFactory.createMobile();
        xiaomiMobile.displayInfo();

    AbstractFactory huaweiFactory = new HuaweiFactory();
        AbstractMobile huaweiMobile = huaweiFactory.createMobile();
        huaweiMobile.displayInfo();
    }
}

在这个代码示例中,我们定义了抽象产品接口 AbstractMobile 和两个具体产品类 XiaomiMobileHuaweiMobile。然后,我们定义了抽象工厂接口 AbstractFactory,其中包含了一个工厂方法 createMobile(),用于创建手机产品。

接下来,我们创建了两个具体工厂类 XiaomiFactoryHuaweiFactory,分别实现了 AbstractFactory 接口,并在其中实现了工厂方法,分别用于创建小米手机和华为手机。

最后,在客户端代码中,我们使用不同的具体工厂来创建不同的手机产品,从而实现了工厂方法模式,客户端代码不需要知道具体产品的细节,只需要知道如何使用抽象工厂来创建产品

6 典型应用场景

6.1 数据库驱动程序的创建

在这里插入图片描述

使用工厂方法模式来进行数据库驱动程序创建的场景是在软件开发中需要连接不同类型的数据库(例如MySQL、PostgreSQL、Oracle等)时。通过工厂方法模式,可以将不同数据库的连接对象的创建过程封装在各自的工厂类中,客户端代码无需关心具体的数据库驱动程序实现,只需使用相应的工厂来创建数据库连接对象,从而实现了灵活性和可扩展性,能够轻松地切换和添加新的数据库驱动程序,而不影响现有的代码。这种模式在多数据库支持和数据库连接的抽象化方面非常有用。

6.1 UI控件的创建

使用工厂方法模式进行UI控件创建的场景是在图形用户界面(GUI)开发中。不同操作系统或界面库(如Windows、macOS、Android等)可能使用不同的UI控件(如按钮)。通过工厂方法模式,可以将每种UI控件的创建过程封装在各自的工厂类中,客户端代码无需关心具体的控件实现,只需使用相应的工厂来创建控件对象,从而实现了跨平台和可扩展性,使得应用程序能够在不同的操作系统或界面库下创建合适的UI控件,而不需要重写整个界面代码。这种模式在多平台UI开发和UI组件的抽象化方面非常有用。

6.3 日志记录器

在应用程序中需要记录日志以进行调试、故障排除和性能监测时。不同的日志记录方式(如文件日志、数据库日志、控制台日志等)可能需要使用不同的日志记录器。通过工厂方法模式,可以将每种日志记录器的创建过程封装在各自的工厂类中,客户端代码无需关心具体的日志记录器实现,只需使用相应的工厂来创建日志记录器对象,从而实现了灵活性和可扩展性,使得应用程序可以根据需求选择不同的日志记录方式,而不需要修改主要的应用逻辑。这种模式在日志记录方式的切换和定制化日志记录方面非常有用。

6.4 游戏中不同角色的创建

使用工厂方法模式来创建游戏中不同角色的场景是在游戏开发中。在游戏中,有各种不同类型的角色(如玩家、敌人、道具、NPC等),每个角色都有自己独特的属性和行为。通过工厂方法模式,可以将每种角色的创建过程封装在各自的工厂类中,客户端代码无需关心具体的角色实现,只需使用相应的工厂来创建角色对象,从而实现了游戏角色的动态创建和管理。这种模式在游戏中角色的扩展和多样化方面非常有用,使得游戏开发人员可以轻松地添加新的角色类型,而不需要修改游戏引擎或核心逻辑。

7 优缺点

工厂方法模式是一种常用的设计模式,它具有一些优点和缺点:

优点:

松耦合 :工厂方法模式将对象的创建过程封装在工厂类中,客户端代码与具体产品类解耦。客户端代码只需要关心抽象工厂和产品接口,无需了解具体产品的细节。

可扩展性 :通过添加新的具体工厂类和具体产品类,可以轻松扩展系统以支持新的产品类型,而不需要修改现有的代码。这使得系统更容易维护和扩展。

多态性 :工厂方法模式利用了多态性,使得客户端可以根据需要创建不同类型的产品对象,而无需改变客户端代码。

符合开闭原则 :工厂方法模式遵循开闭原则,对修改关闭,对扩展开放。添加新的产品类型只需要添加新的具体工厂类和具体产品类,而不需要修改现有代码。

缺点:

类的数量增加 :引入工厂方法模式会增加类的数量,每个具体产品类都需要一个对应的具体工厂类。对于一些简单的情况,可能会使代码变得复杂。

复杂性增加 :在有多层继承结构的情况下,可能会引入更多的层次,增加代码的复杂性。

不适用于单例 :工厂方法模式通常用于创建多个对象,不适用于创建单例对象。对于单例模式,通常使用其他模式,如单例模式或者静态工厂方法。

增加代码复杂度 :在简单情况下,使用工厂方法模式可能会增加不必要的复杂性,不如直接实例化对象来得简单。

总之,工厂方法模式在实现对象的创建和客户端代码解耦方面有很多优点,但也需要谨慎使用,根据具体情况来决定是否使用该模式,以确保代码的可维护性和扩展性。

8 类似模式

工厂方法模式、抽象工厂模式和简单工厂模式都是创建型设计模式,用于对象的创建,但它们在关注点和应用场景上存在差异。

简单工厂模式是一种简化对象创建的方法,不是 GoF(Gang of Four)的正式设计模式。简单工厂适用于创建少量的对象,但不符合开闭原则,不支持扩展新的产品类型。

工厂方法模式是一种正式的设计模式,定义了一个创建对象的接口,具体的对象创建延迟到子类中完成。用于创建单一产品等级结构。

抽象工厂模式也是一种正式的设计模式,提供一个接口,用于创建一系列相关或依赖的对象,不需要指定具体的类。每个具体工厂类可以创建一组相关的产品,用于创建多个相关的产品等级结构或整个产品族。

它们都以不同的方式提供了对象创建的灵活性和可扩展性,根据需求选择合适的模式。

9 小结

工厂方法模式是一种常用的设计模式,属于创建型设计模式,其核心思想是将对象的创建延迟到子类中去完成。特别适合在需要创建多个相关但不同类型的对象时,以提高代码的可维护性和可扩展性。它将对象的创建过程进行了抽象,使得客户端代码更加灵活,可以根据需要选择不同的具体工厂来创建对象。

更多推荐

服务器搭建(TCP套接字)-select版(服务端)

一、select头文件#include<sys/select.h>二、select原型intselect(intnfds,fd_set*readfds,fd_set*writefds,fd_set*exceptfds,structtimeval*timeout);select()是一个系统调用函数,用于在多个文件描述符

K8S架构原理

目录一、k8s概述1、什么是k8s?2、特性3、主要功能二、集群架构与组件1.Master组件(1)Kube-apiserver(2)Kube-controller-manager(3)Kube-scheduler调度算法:2.配置存储中心3.Node组件(1)Kubelet(2)Kube-Proxy(3)docker

【SpringMVC】之自定义注解

文章目录一、Java注解1.1简介1.2分类1.2.1JDK基本注解1.2.2JDK元注解1.3自定义注解二、使用自定义注解2.1案例一(获取类与方法上的注解值)2.2案例二(获取类属性上的注解属性值)2.3案例三(获取参数修饰注解对应的属性值)三、Aop自定义注解的应用一、Java注解1.1简介Java注解是附加在代

[论文阅读]Coordinate Attention for Efficient Mobile Network Design

摘要最近关于移动网络设计的研究已经证明了通道注意力(例如,theSqueeze-and-Excitationattention)对于提高模型的性能有显著的效果,但它们通常忽略了位置信息,而位置信息对于生成空间选择性注意图非常重要。在本文中,我们提出了一种新的移动网络注意力机制,将位置信息嵌入到通道注意力中,我们称之为“

【论文笔记】Baidu Apollo EM Motion Planner

文章目录AbstractI.INTRODUCTIONA.MultilaneStrategyB.Path-SpeedIterativeAlgorithmC.DecisionsandTrafficRegulationsII.EMPLANNERFRAMEWORKWITHMULTILANESTRATEGYIII.EMPLANN

nginx知识点详解:反向代理+负载均衡+动静分离+高可用集群

一、nginx基本概念1.nginx是什么,做什么事情?Nginx是一个高性能的HTTP和反向代理服务器,特点是占有内存少,并发能力强。Nginx转为性能优化而开发,能经受高负载考验。支持热部署,启动容易,运行时间长。2.反向代理正向代理:在客户端(浏览器)配置代理服务器,通过代理服务器进行互联网访问。反向代理:客户端

Python 08学习之文件操作

😀前言欢迎来到Python08学习之文件操作。在本文中,我们将介绍计算机中常见的文本文件和二进制文件,并探讨在Python中操作文件的步骤和相关函数/方法。通过学习本文,您将能够了解如何使用Python打开、读取、写入和关闭文件,以及如何按行读取文件内容。希望您能够通过本文提高您的Python文件操作能力,并且在实际

华为HCIA(二)

今天是第二天(一题一笔记)FTP(文件传输协议)使用的端口号是20和21控制层面用的是21DHCP(IP地址和子网掩码)服务器分配IP地址默认的租期是24小时Tnlnet协议(网络层)默认使用的服务器端口号是23完成链路认证后,STA要通过Association发起链路服务协商WLAN通过SSID来区分不同的网络基于M

【PickerView案例10-国旗选择界面02 Objective-C预言】

一、好了,我们继续来实现这个国旗选择界面:1.它的界面里面,是不是很简单,就一个UIPickerView,就完事儿了然后,显示的每一行内容呢,1)一个文字Label2)一个图片那大家应该有意识,它返回的应该是一个View,对吧,代理方法里面,有一个返回View的,viewForRow:viewForRowInCompo

R语言APRIORI关联规则、K-MEANS均值聚类分析中药专利复方治疗用药规律网络可视化...

全文链接:http://tecdat.cn/?p=30605应用关联规则、聚类方法等数据挖掘技术分析治疗的中药专利复方组方配伍规律(点击文末“阅读原文”获取完整代码数据)。方法检索治疗中药专利复方,排除外用中药及中西药物合用的复方。最近我们被要求撰写关于用药规律的研究报告,包括一些图形和统计输出。对入选的中药专利复方进

延长周末,获得高质量休息:工作与学习党的生活策略

🌷🍁博主猫头虎带您GotoNewWorld.✨🍁🦄博客首页——猫头虎的博客🎐🐳《面试题大全专栏》文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺🌊《IDEA开发秘籍专栏》学会IDEA常用操作,工作效率翻倍~💐🌊《100天精通Golang(基础入门篇)》学会Golang语言,畅玩云原生,走遍大

热文推荐