C++设计模式之抽象工厂模式(Abstract Factory)

news2024/12/23 0:19:31

文章目录

    • 定义
    • 前言
      • 问题
      • 解决方案
    • 结构
    • 适用场景
    • 实现方式
    • 优点
    • 缺点
    • 与其他模式的关系
    • 实例

[C++]22种设计模式的C++实现大纲

定义

抽象工厂是一种创建型设计模式,它能创建一系列相关的对象,而无需指定其具体类。

前言

问题

假设你正在开发一款家具商店模拟器。你的代码中包括一些类,用于表示:

● 一系列相关产品,例如椅子(Chair)、沙发(Sofa)和咖啡桌(CoffeeTable)
● 系列产品的不同变体,例如你可以使用现代(Modern)、维多利亚(Victorian)和装饰风艺术(ArtDeco)等风格生成这些产品

在这里插入图片描述

你需要设法单独生成每件家具对象,这样才能确保其风格一致。如果顾客收到的家具风格不一样,他们可不会开心。

此外, 你也不希望在添加新产品或新风格时修改已有代码。家具供应商对于产品目录的更新非常频繁,你不会想在每次更新时都去修改核心代码的。

解决方案

首先,抽象工厂模式建议为系列中的每件产品明确声明接口(例如椅子、沙发或咖啡桌)。然后,确保所有产品变体都继承这些接口。例如,所有风格的椅子都实现椅子接口;所有风格的咖啡桌都实现咖啡桌接口,以此类推。

在这里插入图片描述
接下来, 我们需要声明抽象工厂——包含系列中所有产品构造方法的接口。例如创建椅子(createChair)、创建沙发(createSofa)和 创建咖啡桌(createCoffeeTable)。这些方法必须返回抽象产品类型,即我们之前抽取的那些接口: 椅子,沙发和咖啡桌等等。

在这里插入图片描述
那么该如何处理产品变体呢? 对于系列产品的每个变体, 我们都将基于抽象工厂接口创建不同的工厂类。每个工厂类都只能返回特定类别的产品,例如, 现代家具工厂(ModernFurnitureFactory)只能创建现代椅(ModernChair)、现代沙发(ModernSofa)和现代咖啡桌(ModernCoffeeTable)对象。

客户端代码可以通过相应的抽象接口调用工厂和产品类。你无需修改实际客户端代码,就能更改传递给客户端的工厂类,也能更改客户端代码接收的产品变体。

假设客户端想要工厂创建一把椅子。客户端无需了解工厂类,也不用管工厂类创建出的椅子类型。无论是现代风格,还是维多利亚风格的椅子,对于客户端来说没有分别,它只需调用抽象椅子接口就可以了。这样一来,客户端只需知道椅子以某种方式实现了坐下(sitOn)方法就足够了。此外,无论工厂返回的是何种椅子变体,它都会和由同一工厂对象创建的沙发或咖啡桌风格一致。

最后一点说明:如果客户端仅接触抽象接口,那么谁来创建实际的工厂对象呢?一般情况下,应用程序会在初始化阶段创建具体工厂对象。而在此之前,应用程序必须根据配置文件或环境设定选择工厂类别。

结构

在这里插入图片描述

  1. 抽象产品(Abstract Product)为构成系列产品的一组不同但相关的产品声明接口。
  2. 具体产品(Concrete Product)是抽象产品的多种不同类型实现。所有变体(维多利亚/现代)都必须实现相应的抽象产品(椅子/沙发)。
  3. 抽象工厂(Abstract Factory)接口声明了一组创建各种抽象产品的方法。
  4. 具体工厂(Concrete Factory)实现抽象工厂的构建方法。每个具体工厂都对应特定产品变体,且仅创建此种产品变体。
  5. 尽管具体工厂会对具体产品进行初始化,其构建方法签名必须返回相应的抽象产品。这样,使用工厂类的客户端代码就不会与工厂创建的特定产品变体耦合。客户端(Client)只需通过抽象接口调用工厂和产品对象,就能与任何具体工厂/产品变体交互。

适用场景

● 如果代码需要与多个不同系列的相关产品交互,但是由于无法提前获取相关信息,或者出于对未来扩展性的考虑,你不希望代码基于产品的具体类进行构建,在这种情况下,你可以使用抽象工厂。

抽象工厂为你提供了一个接口,可用于创建每个系列产品的对象。只要代码通过该接口创建对象,那么你就不会生成与应用程序已生成的产品类型不一致的产品。

● 如果你有一个基于一组抽象方法的类,且其主要功能因此变得不明确,那么在这种情况下可以考虑使用抽象工厂模式。

在设计良好的程序中,每个类仅负责一件事。如果一个类与多种类型产品交互,就可以考虑将工厂方法抽取到独立的工厂类或具备完整功能的抽象工厂类中。

实现方式

  1. 以不同的产品类型与产品变体为维度绘制矩阵。
  2. 为所有产品声明抽象产品接口。然后让所有具体产品类实现这些接口。
  3. 声明抽象工厂接口,并且在接口中为所有抽象产品提供一组构建方法。
  4. 为每种产品变体实现一个具体工厂类。
  5. 在应用程序中开发初始化代码。该代码根据应用程序配置或当前环境,对特定具体工厂类进行初始化。然后将该工厂对象传递给所有需要创建产品的类。
  6. 找出代码中所有对产品构造函数的直接调用,将其替换为对工厂对象中相应构建方法的调用。

优点

● 你可以确保同一工厂生成的产品相互匹配。
● 你可以避免客户端和具体产品代码的耦合。
● 单一职责原则。你可以将产品生成代码抽取到同一位置,使得代码易于维护。
● 开闭原则。向应用程序中引入新产品变体时,你无需修改客户端代码。

缺点

由于采用该模式需要向应用中引入众多接口和类,代码可能会比之前更加复杂。

与其他模式的关系

● 在许多设计工作的初期都会使用工厂方法(较为简单,而且可以更方便地通过子类进行定制), 随后演化为使用抽象工厂、原型或生成器(更灵活但更加复杂)。
● 生成器重点关注如何分步生成复杂对象。抽象工厂专门用于生产一系列相关对象。抽象工厂会马上返回产品,生成器则允许你在获取产品前执行一些额外构造步骤。
● 抽象工厂模式通常基于一组工厂方法,但你也可以使用原型模式来生成这些类的方法。
● 当只需对客户端代码隐藏子系统创建对象的方式时,你可以使用抽象工厂来代替外观。
● 你可以将抽象工厂和桥接搭配使用。如果由桥接定义的抽象只能与特定实现合作,这一模式搭配就非常有用。在这种情况下,抽象工厂可以对这些关系进行封装,并且对客户端代码隐藏其复杂性。
● 抽象工厂、生成器和原型都可以用单例来实现。

实例

AbstractFactory.h:

#ifndef  ABSTRACT_FACTORY_H_
#define  ABSTRACT_FACTORY_H_

#include <memory>
#include "AbstractProduct.h"

// 抽象工厂类 生产电影和书籍类等
class Factory {
 public:
    virtual std::shared_ptr<Movie> productMovie() = 0;
    virtual std::shared_ptr<Book> productBook() = 0;
};

#endif  // ABSTRACT_FACTORY_H_

ConcreteFactory.h:

#ifndef CONCRETE_FACTORY_H_
#define CONCRETE_FACTORY_H_

#include <memory>
#include "AbstractFactory.h"
#include "ConcreteProduct.h"

// 具体工厂类 中国生产者
class ChineseProducer : public Factory {
 public:
    std::shared_ptr<Movie> productMovie() override {
        return std::make_shared<ChineseMovie>();
    }

    std::shared_ptr<Book> productBook() override {
        return std::make_shared<ChineseBook>();
    }
};

// 具体工厂类 日本生产者
class JapaneseProducer : public Factory {
 public:
    std::shared_ptr<Movie> productMovie() override {
        return std::make_shared<JapaneseMovie>();
    }

    std::shared_ptr<Book> productBook() override {
        return std::make_shared<JapaneseBook>();
    }
};

#endif  // CONCRETE_FACTORY_H_

AbstractProduct.h:

#ifndef  ABSTRACT_PRODUCT_H_
#define  ABSTRACT_PRODUCT_H_

#include <string>

// 抽象产品类 电影
class Movie {
 public:
    virtual std::string showMovieName() = 0;
};

// 抽象产品类 书籍
class Book {
 public:
    virtual std::string showBookName() = 0;
};

#endif  // ABSTRACT_PRODUCT_H_

ConcreteProduct.h:

#ifndef  CONCRETE_PRODUCT_H_
#define  CONCRETE_PRODUCT_H_

#include <iostream>
#include <string>
#include "AbstractProduct.h"

// 具体产品类 电影::国产电影
class ChineseMovie : public Movie {
    std::string showMovieName() override {
        return "《让子弹飞》";
    }
};

// 具体产品类 电影::日本电影
class JapaneseMovie : public Movie {
    std::string showMovieName() override {
        return "《千与千寻》";
    }
};

// 具体产品类 书籍::国产书籍
class ChineseBook : public Book {
    std::string showBookName() override {
        return "《三国演义》";
    }
};

// 具体产品类 书籍::日本书籍
class JapaneseBook : public Book {
    std::string showBookName() override {
        return "《白夜行》";
    }
};

#endif  // CONCRETE_PRODUCT_H_

main.cpp:

#include <iostream>
#include "AbstractFactory.h"
#include "ConcreteFactory.h"


int main() {
    std::shared_ptr<Factory> factory;

    // 这里假设从配置中读到的是Chinese(运行时决定的)
    std::string conf = "China";

    // 程序根据当前配置或环境选择创建者的类型
    if (conf == "China") {
        factory = std::make_shared<ChineseProducer>();
    } else if (conf == "Japan") {
        factory = std::make_shared<JapaneseProducer>();
    } else {
        std::cout << "error conf" << std::endl;
    }

    std::shared_ptr<Movie> movie;
    std::shared_ptr<Book> book;
    movie = factory->productMovie();
    book = factory->productBook();
    std::cout << "获取一部电影: " << movie->showMovieName() << std::endl;
    std::cout << "获取一本书: " << book->showBookName() << std::endl;
}

编译运行:

$g++ -g main.cpp -o abstractfactory -std=c++11
$./abstractfactory 
获取一部电影: 《让子弹飞》
获取一本书: 《三国演义》

[C++]22种设计模式的C++实现大纲

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/553863.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Multi-Head Attention和Transformer Decoder(GPT)详解

文章目录 一、Transformer的Attention1. Self-Attention2. Masked Self-Attention3. Multi-Head Attention 二、Transformer Decoder&#xff08;GPT&#xff09;1. GPT的网络结构2. GPT的计算原理 一、Transformer的Attention 1. Self-Attention 如前篇文章所述&#xff08;ht…

如何开发一个人人爱的组件?

组件&#xff0c;是前端最常打交道的东西&#xff0c;对于 React、Vue 等应用来说&#xff0c;万物皆组件毫不为过。 有些工作经验的同学都知道&#xff0c;组件其实也分等级的&#xff0c;有的组件可以被上万开发者复用&#xff0c;有些组件就只能在项目中运行&#xff0c;甚…

Springboot +spring security,配置多个数据源:验证不同用户表

一.简介 上篇文章写到&#xff0c;我们在配置jdbc和mybatis 来源&#xff0c;进行登录后&#xff0c;出现了如下错误! 后面解决方案是&#xff1a;屏蔽了其中一个来源&#xff0c;登陆成功&#xff0c;也分析了其原因。 但是&#xff0c;但是如果需要配置多个数据来源&#…

2023年认证杯SPSSPRO杯数学建模B题(第一阶段)考订文本全过程文档及程序

2023年认证杯SPSSPRO杯数学建模 B题 考订文本 原题再现&#xff1a; 古代文本在传抄过程中&#xff0c;往往会出现种种错误&#xff0c;以至于一部书可能流传下来多种版本。在文献学中&#xff0c;错误往往被总结成“讹”、“脱”、“衍”、“倒”等形式&#xff0c;也可能同…

cda 1级模拟题错题知识点总结

Sql truncate函数 格式&#xff1a;TRUNCATE(number, decimals) number: the number to be truncated decimals:the number of decimal places to truncate to 截断到的小数位数&#xff0c;如果为0则表示不保留小数 例如: select truncate(2.83,0) 结果为2 select truncate(…

解读kubernetes部署:配置docker私服密钥与SSL证书创建

为k8s配置docker私服密钥 为了kubernetes有权访问您的docker私服&#xff0c;需要在kubernetes的凭证中建立docker私服的密钥&#xff1a; kubectlcreatesecretdocker-registryaliyun-secret--docker-server--docker-username--docker-password--docker-email--namespacens-jav…

2.golang的变量、常量、数据类型、循环和条件判断

一、变量 变量&#xff08;Variable&#xff09;的功能是存储数据。Go语言中的每一个变量都有自己的类型&#xff0c;并且变量必须经过声明才能开始使用。 Go语言的变量声明格式为&#xff1a; var 变量名 变量类型 例如&#xff1a; var name string var age int var isOk b…

线上问题处理案例:出乎意料的数据库连接池 | 京东云技术团队

导读 本文是线上问题处理案例系列之一&#xff0c;旨在通过真实案例向读者介绍发现问题、定位问题、解决问题的方法。本文讲述了从垃圾回收耗时过长的表象&#xff0c;逐步定位到数据库连接池保活问题的全过程&#xff0c;并对其中用到的一些知识点进行了总结。 一、问题描述…

LabVIEWCompactRIO 开发指南29 数据通信

LabVIEWCompactRIO 开发指南29 数据通信 LabVIEW FPGA中的数据通信分为两类&#xff1a;进程间和目标间。进程间通信通常对应于FPGA目标上的两个或多个环路之间的数据共享。目标间数据通信是在FPGA目标和主机处理器之间共享数据。对于这两种情况&#xff0c;在决定使用哪种机…

扩散能垒计算在电池材料领域的革新应用

扩散能垒计算在电池材料领域的革新应用 随着能源需求的增长和环境意识的提高&#xff0c;电池技术成为解决可再生能源存储和移动电子设备需求的关键。电池材料的研究和开发变得日益重要&#xff0c;而扩散能垒计算作为一种先进的计算方法&#xff0c;为电池材料领域带来了革新的…

设计模式之【观察者模式】,MQ的单机实现雏形

文章目录 一、什么是观察者模式1、观察者模式应用场景2、观察者模式的四大角色3、观察者模式优缺点 二、实例1、观察者模式的一般写法2、微信公众号案例3、鼠标响应事件API案例 三、实现一个异步非阻塞框架1、EventBus2、使用MQ 四、源码中的观察者模式1、Observable/Observer2…

pygam第3课——画图小程序

前言&#xff1a;我们前两节课已经学习了&#xff0c;界面的设计、图片的加载、那么今天我们将继续学习pygame的基础知识&#xff0c;我们的今天学习的内容是&#xff1a;鼠标滑动时坐标的实时获取、鼠标的移动事件、鼠标的点击事件、图形绘制等。希望大家能 搭建界面&#xf…

firewalld防火墙(又到了可以看日落和晚霞的日子了)

文章目录 一、firewalld概述二、firewalld和iptables的关系三、firewalld区域的概念四、firewalld数据处理流程五、firewalld检查数据包源地址的规则六、firewalld防火墙的配置种类1.运行时配置2.永久配置 七、firewalld防火墙的配置方法八、使用命令配置firewalld防火墙1.获取…

Ventoy 多合一启动盘制作工具神器 - 将多个系统 Win/PE/Linux 镜像装在1个U盘里

最近很多操作系统都纷纷发布了新版本&#xff0c;比如 Windows 11、Ubuntu、Deepin、优麒麟、CentOS、Debian 等等&#xff0c;对喜欢玩系统的人来说绝对是盛宴。 不过一般用 Rufus 等工具&#xff0c;一个 U 盘往往只能制作成一个系统的启动盘/安装盘&#xff0c;想要增加另一…

零入门kubernetes网络实战-33->基于nat+brigde+veth pair形成的跨主机的内网通信方案

《零入门kubernetes网络实战》视频专栏地址 https://www.ixigua.com/7193641905282875942 本篇文章视频地址(稍后上传) 本文主要使用的技术是 nat技术Linux虚拟网桥虚拟网络设备veth pair来实现跨主机网桥的通信 1、测试环境介绍 两台centos虚拟机 # 查看操作系统版本 cat …

VIBRO-METER VM600 IRC4 智能继电器卡

额外的继电器&#xff0c;由来自MPC4和/或AMC8卡的多达86个输入的方程驱动&#xff0c;用于需要2oo3表决等更复杂的逻辑时8个继电器&#xff0c;可配置为8个SPDT或4个DPDT使用IRC4配置器软件进行完全软件配置继电器可配置为正常通电(NE)或正常断电(NDE)&#xff0c;具有可配置的…

小航助学GESP_C++一级模拟测试试卷(含题库答题软件账号)

GESP在线模拟训练系统请点击 电子学会-全国青少年编程等级考试真题Scratch一级&#xff08;2019年3月&#xff09;在线答题_程序猿下山的博客-CSDN博客_小航答题助手 答案:A 第1题人们在使用计算机时所提到的 Windows 通常指的是&#xff08;&#xff09;。 A、操作系统B、多…

Science Bulletin:张占军教授团队提出“额叶保持,颞叶损伤” 假说解析成功认知老化

步入老年后&#xff0c;各项认知能力会逐渐衰退&#xff0c;我们把这一过程称之为认知老化。认知老化的过程与速度因人而异&#xff0c;走向阿尔茨海默病&#xff08;AD&#xff09;等认知障碍疾病为结局的属于病理认知老化&#xff0c;也就是经历轻度认知障碍阶段&#xff0c;…

【分享】PowerPoint如何设置保护和加密?

想保护自己做好的PPT&#xff0c;通常用的方法就是给PPT加密。下面我们来看看PPT加密保护方式有几种&#xff0c;具体如何操作。 打开PPT&#xff0c;点击菜单【文件】&#xff0c;再依次点击【信息】-【保护演示文稿】&#xff0c;就可以看到设置密码保护的5个选项。 选项1&a…

小航助学2022年NOC初赛图形化(小低组)(含题库答题软件账号)

需要在线模拟训练的题库账号请点击 小航助学编程在线模拟试卷系统&#xff08;含题库答题软件账号&#xff09;_程序猿下山的博客-CSDN博客 单选题3.0分 删除编辑 答案:D 第1题如果想要从造型库中自选一个喜欢角色&#xff0c;可以点击哪个按钮呢&#xff1f; A、①B、②C…