实战设计模式之抽象工厂模式

news/2024/12/24 10:17:17/

概述

        前一篇文章中提到的工厂方法模式允许子类决定具体要创建的对象类型,但它一次只创建一个对象。抽象工厂模式则更加复杂,它关注的是创建一系列相关的对象。这些对象通常构成了一个完整的“家族”,并且在不同的实现中保持一致性和兼容性。

        跨平台的图形用户界面库(GUI,比如:QT、wxWidgets)是运用抽象工厂模式的一个典型例子:这些库需要为不同的操作系统(比如:Windows、macOS、Linux等)提供一致的控件集,包括按钮、文本框、菜单、多选框等。通过抽象工厂模式,我们可以编写一次代码,而无需关心底层的操作系统差异,只需选择合适的工厂即可创建正确的控件集。

基本原理

        抽象工厂模式的基本原理是:提供一种创建一系列相关或依赖对象的接口,而无需指定它们具体的类。这种模式强调的是一个“家族”或一组具有相关性的对象,而不是单一的对象。通过抽象工厂模式,客户端代码可以与这些对象的接口进行交互,而不必关心具体的产品是如何创建和组合在一起的。抽象工厂模式包括如下五个核心组件。

        1、抽象工厂。声明了一组用于创建一系列相关产品的方法,这些方法返回的是抽象产品类型,而非具体的产品实例。

        2、具体工厂。实现了抽象工厂中定义的方法,以创建具体的产品对象。每个具体工厂,通常对应于一组特定的产品变体。

        3、抽象产品。为每种类型的产品定义了一个接口,所有具体产品都必须实现这个接口。

        4、具体产品。实现了抽象产品接口的具体类,代表了实际的对象。

        5、客户端。只依赖于抽象工厂和抽象产品接口,而不是具体的工厂或产品类。这样,可以确保客户端代码与具体实现解耦。

        基于上面的核心组件,抽象工厂模式的实现主要有以下六个步骤。

        1、定义抽象产品接口。首先,定义一组抽象产品接口。每个接口代表一类产品,并声明了该类产品必须提供的方法和行为。

        2、创建具体产品类。然后,为每种类型的产品创建具体的产品类,确保它们实现了第一步中定义的抽象产品接口,每个具体产品类代表一个特定的产品实现。

        3、定义抽象工厂接口。定义一个抽象工厂接口,它声明了一组用于创建各种产品的工厂方法。这些方法返回的是第一步中定义的抽象产品类型,而不是具体的产品实例。

        4、实现具体工厂类。为每个产品系列或主题创建具体工厂类,这些类实现了第三步中定义的抽象工厂接口,每个具体工厂负责创建属于同一家族的一系列产品。

        5、编写客户端代码。最后,在客户端代码中只依赖于抽象工厂和抽象产品接口进行编程。通过选择合适的具体工厂,客户端可以创建一系列相互关联的对象,而无需知道具体的实现细节。这种方式保证了系统的灵活性和可扩展性,同时保持了不同对象之间的一致性和兼容性。

        6、扩展系统。当需要添加新的产品系列时,只需引入新的具体工厂和相应的产品类即可。这种方法不仅简化了对象创建的过程,还提高了代码的维护性和适应变化的能力。

实战解析

        在下面的实战代码中,我们使用抽象工厂模式创建了Windows和macOS操作系统下的GUI库。

        首先,我们声明了CButton和CTextBox这两个抽象产品接口。它们声明了所有具体产品必须实现的Paint函数,这确保了所有具体产品的行为一致性。

        CWinButton、CWinTextBox、CMacButton和CMacTextBox是具体的产品类,分别实现了CButton和CTextBox接口中的Paint方法,这些类代表了特定于Windows和macOS的GUI控件。

        接下来,我们声明了抽象工厂类CGUIFactory。它声明了创建按钮和文本框的方法,但没有提供具体的实现。这使得客户端代码可以与任何实现了这个接口的具体工厂一起工作,而不关心具体的产品是如何创建的。

        CWinFactory和CMacFactory是具体工厂类,它们实现了CGUIFactory接口中定义的创建方法,分别负责创建Windows风格和macOS风格的控件。

        最后,在main函数中,我们创建了一个CWinFactory实例,并调用了CreateGUI函数来创建和使用Windows风格的控件。紧接着,同样的过程重复了一次,这次使用的是CMacFactory来创建和使用macOS风格的控件。

#include <iostream>using namespace std;// 抽象产品:按钮基类
class CButton
{
public:virtual ~CButton() {}virtual void Paint() const = 0;
};// 抽象产品:文本框基类
class CTextBox
{
public:virtual ~CTextBox() {}virtual void Paint() const = 0;
};// Windows具体产品:按钮
class CWinButton : public CButton
{
public:void Paint() const {cout << "Paint a Windows Button" << endl;}
};// Windows具体产品:文本框
class CWinTextBox : public CTextBox
{
public:void Paint() const{cout << "Paint a Windows TextBox" << endl;}
};// macOS具体产品:按钮
class CMacButton : public CButton
{
public:void Paint() const{cout << "Paint a macOS Button" << endl;}
};// macOS具体产品:文本框
class CMacTextBox : public CTextBox
{
public:void Paint() const{cout << "Paint a macOS TextBox" << endl;}
};// 抽象工厂接口
class CGUIFactory
{
public:virtual ~CGUIFactory() {}virtual CButton* CreateButton() const = 0;virtual CTextBox* CreateTextBox() const = 0;
};// Windows具体工厂
class CWinFactory : public CGUIFactory
{
public:CButton* CreateButton() const{return new CWinButton();}CTextBox* CreateTextBox() const{return new CWinTextBox();}
};// macOS具体工厂
class CMacFactory : public CGUIFactory
{
public:CButton* CreateButton() const{return new CMacButton();}CTextBox* CreateTextBox() const{return new CMacTextBox();}
};// 创建特定操作系统的具体产品
void CreateGUI(CGUIFactory& factory)
{CButton* pBtn = factory.CreateButton();pBtn->Paint();delete pBtn;CTextBox* pTextBox = factory.CreateTextBox();pTextBox->Paint();delete pTextBox;
}int main()
{// 客户端代码cout << "Using Windows GUI:" << endl;CWinFactory winFactory;CreateGUI(winFactory);cout << "Using macOS GUI:" << endl;CMacFactory macFactory;CreateGUI(macFactory);return 0;
}

总结

        抽象工厂模式可有效降低耦合度,客户端代码只需要处理抽象接口,避免了对具体类的直接引用。还可以在不修改现有代码的情况下添加新产品系列,只需创建新的具体工厂和相应的产品类即可。通过抽象工厂模式,确保了一组相关的产品一起使用,并且可以根据需要轻松切换整个产品系列。

        凡事有利亦有弊,抽象工厂模式也在无形中增加了系统的复杂性。由于引入了额外的接口和类,可能会使系统变得更加复杂。另外,抽象工厂模式难以支持新种类的产品。如果需要添加一个新的产品种类,则所有的工厂类都需要进行相应的修改。


http://www.ppmy.cn/news/1557713.html

相关文章

基于自定义注解与 AOP 切面实现接口日志全面数据库存储

基于自定义注解与 AOP 切面实现接口日志全面数据库存储 一、引言 在当今复杂的软件系统开发与运维过程中&#xff0c;详细且精准地记录接口的各项信息对于系统性能监测、问题排查、安全审计以及业务分析都有着极为关键的意义。本文将深入讲解如何运用自定义注解与 AOP&#x…

C# Winform简单的俄罗斯方块小游戏源码2

文章目录 一、设计来源俄罗斯方块小游戏讲解1.1 主界面1.2 游戏界面1.3 游戏结束界面1.4 游戏积分界面 二、效果和源码2.1 动态效果2.2 源代码 源码下载更多优质源码分享 作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/article/details/…

STM32基于标准库如何查看时钟主频,100%简单

基于原有的工程写入两行代码&#xff0c;见下图 RCC_ClocksTypeDef get_rcc_clock; RCC_GetClocksFreq(&get_rcc_clock); 进入我们的仿真加入断点&#xff0c;然后在watch1观察变量值数据&#xff0c;然后在计算器计算就能得出&#xff0c;如上图。 但是这样看的PAB1上…

搭建简易版本的git私有仓库--运用git和gitea

诉求 在公司和在家会进行个人java项目的编写&#xff0c;同一项目便于集中管理&#xff0c;同步版本&#xff0c;打算搭建私有的Git服务供个人学习和练习使用。 基本情况 Windows系统且未安装过Git 一、下载安装git git下载地址 下载完毕直接安装&#xff1a; 最后一步去…

Springboot(五十五)SpringBoot整合ELK实现日志可视化

首先我们先来解析一下,什么是ELK:E:elasticsearch、L:logstash、K:kibana 关于他们的部署,请移步 《docker(十五)docker-compose部署elasticsearch配置账号密码登录》 要先部署elasticsearch,在部署logstash,部署logstash需要用到es的账号和密码。 《docker(二十…

PostgreSQL 的历史

title: PostgreSQL 的历史 date: 2024/12/23 updated: 2024/12/23 author: cmdragon excerpt: PostgreSQL 是一款功能强大且广泛使用的开源关系型数据库管理系统。其历史可以追溯到1986年,当时由加州大学伯克利分校的一个研究团队开发。文章将深入探讨 PostgreSQL 的起源、…

django中cookie与session的使用

一、cookie cookie由服务器生成 &#xff0c;存储在浏览器中的键值对数据,具有不安全性&#xff0c;对应敏感数据应该加密储存在服务端每个域名的cookie相互独立浏览器访问域名为A的url地址&#xff0c;会把A域名下的cookie一起传递到服务器cookie可以设置过期时间 django中设…

多模态去噪信息收集

系列博客目录 文章目录 系列博客目录1、Here are some key papers related to multimodal denoising that may be relevant to your research:2、being 搜 Multimodal denoisingMulti-modal deep convolutional dictionary learning for image denoising 1、Here are some key …