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

抽象工厂这个名字比较难以帮助理解,可以把抽象工厂理解为“品牌工厂”或者“家族工厂”。

动机

  • 在软件系统中,经常面临着“一系列相互依赖的对象”的创建工作;同时,由于需求的变化,往往存在更多系列对象的创建工作。
  • 如何应对这种变化?如何绕过常规的创建方法(new),提供一种“封装机制”来避免客户程序和这种“多系列具体对象创建工作”的紧耦合?

举个栗子

我们有一个控制数据库连接和操作的类

class EmployeeDAO{

public:
    vector<EmployeeDAO> GetEmployees(){
        SqlConnection* connection = new SqlConnection();
        connection->ConnectionString("...");

        SqlCommand* command = new SqlCommand();
        command->CommandText("...");
        command->SetConnection(connection);

        SqlDataReader* reader = command->ExecuteReader();
        while(reader->Read()){

        }
        
    }
};

我们使用这个类来连接一个数据库,并使用一些SQL语句来对数据库进行操作。然后使用reader来读取SQL语句得到的数据。

由于可以使用的数据库种类很多,比如针对oracle的数据库,我们可能需要另外再一个类来实现不同的实际操作。

在学习了工厂方法之后,我们可以根据工厂模式将SqlConnection,SqlCommand,SqlDataReader改写成以下模式。

class IDBconnection {
public:
	virtual void ConnectionString(string) = 0;
};
class SqlConnection : public IDBconnection {
public:
	virtual void ConnectionString(string s) {
		//do connection
	}
};
class OracleConnection : public IDBconnection {
public:
	virtual void ConnectionString(string s) {
		//do connection
	}
};

class IDBconnectionFactory {
public:
	virtual IDBconnection* create() = 0;
};
class SqlConnectionFactory : public IDBconnectionFactory {
	IDBconnection* create() {
		return new SqlConnection();
	}
};
class OracleConnectionFactory : public IDBconnectionFactory {
	IDBconnection* create() {
		return new OracleConnection();
	}
};

class ISqlCommand {
public:
	virtual void CommandText(string) = 0;
	virtual void SetConnection(IDBconnection*) = 0;
	virtual IDataReader* ExecuteReader() = 0;
};
class SqlCommand : public ISqlCommand {
public:
	virtual void CommandText(string) {
		//...
	}
	virtual void SetConnection(IDBconnection*) {
		//...
	}
	virtual IDataReader* ExecuteReader() {
		return new SqlDataReader();
	}
};
class OracleCommand : public ISqlCommand {
public:
	virtual void CommandText(string) {
		//...
	}
	virtual void SetConnection(IDBconnection*) {
		//...
	}
	virtual IDataReader* ExecuteReader() {
		return new OracleDataReader();
	}
};

class ISqlCommandFactory {
public:
	virtual ISqlCommand* create() = 0;
};
class SqlCommandFactory : public ISqlCommandFactory {
	ISqlCommand* create() {
		return new SqlCommand();
	}
};
class OracleCommandFactory : public ISqlCommandFactory {
	ISqlCommand* create() {
		return new OracleCommand();
	}
};


class IDataReader {
public:
	virtual bool Read() = 0;
};
class SqlDataReader : public IDataReader {
public:
	virtual bool Read() {
		//...
		return true;
	}
};
class OracleDataReader : public IDataReader {
public:
	virtual bool Read() {
		//...
		return true;
	}
};


class EmployeeDAO {
private:
	IDBconnection* connection;
	ISqlCommand* command;
	IDataReader* reader;

public:

	vector<EmployeeDAO> GetEmployees(IDBconnectionFactory* connectionFactory, ISqlCommandFactory* commanFactory) {
		
		connection = connectionFactory->create();
		connection->ConnectionString("...");

		command = commanFactory->create();
		command->CommandText("...");
		command->SetConnection(connection);

		reader = command->ExecuteReader();
		while (reader->Read()) {

		}

	}
};

我们使用工厂模式在运行时创建EmployeeDAO的connection和command指针。

问题

我们使用单纯的工厂模式来创建不同的connection和command的子类来操作不同的数据库。这表面上看起来已经解决了这个问题。

但是,假如出现了这种情况,在调用GetEmployees时,我们传入的参数是,

GetEmployees(SqlConnectionFactory, OracleCommandFactory)

这样的代码当然可以通过编译,但是我们用Oracle数据库的命令其他的数据库,当然会导致一系列的错误。

重构

class IDBFactory {
public:
	virtual IDBconnection* create_connection() = 0;
	virtual ISqlCommand* create_command() = 0;
};

class SqlFactory : public IDBFactory {
public:
	virtual IDBconnection* create_connection() {
		return new SqlConnection();
	}
	virtual ISqlCommand* create_command() {
		return new SqlCommand();
	}
};

class OracleFactory : public IDBFactory {
public:
	virtual IDBconnection* create_connection() {
		return new OracleConnection();
	}
	virtual ISqlCommand* create_command() {
		return new OracleCommand();
	}
};


class EmployeeDAO {
private:
	IDBconnection* connection;
	ISqlCommand* command;
	IDataReader* reader;

public:

	vector<EmployeeDAO> GetEmployees(IDBFactory* factory) {
		
		connection = factory->create_connection();
		connection->ConnectionString("...");

		command = factory->create_command();
		command->CommandText("...");
		command->SetConnection(connection);

		reader = command->ExecuteReader();
		while (reader->Read()) {

		}

	}
};

将两个connection和command工厂合并,让它只能生产“同一种类的产品”。这样,我们既降低了耦合,又避免“产品种类”不同导致不能使用的情况。

结构

红色是稳定的,蓝色代表了一种变化,绿色代表了另一种变化。

可以对照上面的代码自己寻找一下图中的类和代码中的类的对应关系。

9.抽象工厂_哔哩哔哩_bilibili这里面,将ConreteFactory1,2其实是按sql和oracle分开的。视频中出现了一点小错误。

总结

  • 如果没有应对“多系列对象构建”的需求变化,则没有必要使用抽象工厂模式,这时候简单的工厂模式完全可以。
  • “系列对象”指在某一特定系列下的对象之间有相互依赖、或作用的关系。(不同数据库间的操作模块不能互相作用和依赖)
  • 抽象工厂模式主要应对“新系列”的需求变动。缺点在于难以应对“新对象”的需求变动。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Python可视化实战】钻石数据可视化

一、项目引言 1.背景和目标 钻石作为一种珍贵的宝石&#xff0c;其价格受到多种因素的影响。为了深入了解钻石价格的决定因素&#xff0c;我们收集了大量关于钻石的数据&#xff0c;并希望通过数据可视化来揭示钻石特征与价格之间的关系。 2.内容 收集钻石的各项特征数据&a…

【python高级用法】进程

一个简单的进程 # -*- coding: utf-8 -*-import multiprocessingdef foo(i):print (called function in process: %s %i)returnif __name__ __main__:Process_jobs []for i in range(5):p multiprocessing.Process(targetfoo, args(i,))Process_jobs.append(p)p.start()p.j…

Vue中的过滤器详解(应用场景和原理分析)

文章目录 一、是什么二、如何用定义filter小结&#xff1a; 三、应用场景四、原理分析小结&#xff1a; 参考文献 一、是什么 过滤器&#xff08;filter&#xff09;是输送介质管道上不可缺少的一种装置 大白话&#xff0c;就是把一些不必要的东西过滤掉 过滤器实质不改变原…

K-最近邻算法(KNN)是什么算法?

K-最近邻算法&#xff08;K-Nearest Neighbor&#xff0c;KNN&#xff09;是一种经典的有监督学习方法&#xff0c;也可以被归为懒惰学习&#xff08;Lazy Learning&#xff09;方法。它基于“物以类聚”的原理&#xff0c;假设样本之间的类别距离越近则它们越有可能是同一类别…

关于目标检测任务中,XML(voc格式)标注文件的可视化

1. 前言 最近在弄关于目标检测的任务&#xff0c;因为检测的图片和标签是分开的&#xff0c;可视化效果不明显&#xff0c;也不知道随便下载的数据集&#xff0c;标注信息对不对。网上看了好多代码&#xff0c;代码风格和本人平时不同&#xff0c;看起来麻烦&#xff0c;也不知…

项目使用PowerJob

新一代的定时任务框架——PowerJob 简介 PowerJob是基于java开发的企业级的分布式任务调度平台&#xff0c;与xxl-job一样&#xff0c;基于web页面实现任务调度配置与记录&#xff0c;使用简单&#xff0c;上手快速&#xff0c;其主要功能特性如下&#xff1a; 使用简单&…

ClickHouse基础介绍

目录 前言 1、什么是clickhouse 2、OLAP场景的关键特征 3、列式存储更适合于OLAP场景的原因 4、clickhouse的独特功能 5、clickhouse的缺点 6、性能 6.1、单个大查询的吞吐量 6.2、处理短查询的延迟时间 6.3、处理大量短查询的吞吐量 6.4、数据的写入性能 前言 11月…

RTSP/Onvif安防平台EasyNVR接入EasyNVS显示服务不存在的原因及解决办法

EasyNVS云管理平台具备汇聚与管理EasyGBS、EasyNVR等平台的能力&#xff0c;可以将接入的视频资源实现统一的视频能力输出&#xff0c;支持远程可视化运维等管理功能&#xff0c;还能解决设备现场没有固定公网IP却需要在公网直播的需求。 有用户在现场部署EasyNVR&#xff0c;…

how2heap-2.23-04-unsorted_bin_leak

#include<stdio.h> #include<malloc.h>int main() {char* a malloc(0x88);char* b malloc(0x8);free(a);long* c malloc(0x88);printf("%lx , %lx\n",c[0],c[1]);return 0; }unsorted bin leak原理&#xff1a;将chunk从unsorted bin申请回来时&#…

ssm基于web的素材网的设计与实现+vue论文

基于web的素材网站的设计与实现 摘要 当下&#xff0c;正处于信息化的时代&#xff0c;许多行业顺应时代的变化&#xff0c;结合使用计算机技术向数字化、信息化建设迈进。传统的素材信息管理模式&#xff0c;采用人工登记的方式保存相关数据&#xff0c;这种以人力为主的管理…

安卓逆向某脚本-stringFog 拆解

引言 有个autojs 脚本软件,挺好用的,我想看下这个软件怎么实现的,学习写人家怎么写的。 先用MT 重新打包下, 看下重打包之后是否还可以继续使用。 用MT 打开APK,然后选择查看 随便找一个dex ,编辑下

02、Kafka ------ 配置 Kafka 集群

目录 配置 Kafka 集群配置步骤启动各Kafka节点 配置 Kafka 集群 启动命令&#xff1a; 1、启动 zookeeper 服务器端 小黑窗输入命令&#xff1a; zkServer 2、启动 zookeeper 的命令行客户端工具 &#xff08;这个只是用来看连接的节点信息&#xff0c;不启动也没关系&#…

数据结构与算法(六)

文章目录 高频-体系学习班(六)41 四边形不等式技巧(上)41.1 非负数组切分成左右两部分累加和的最大值41.2 非负数组切分成左右两部分累加和的最大值的数组41.3 合并石子的得分41.4 画匠问题42 四边形不等式技巧(下)42.1 邮局选址问题42.2 丢棋子问题43 状态压缩的动态规划…

RabbitMQ安装与应用

文章目录 1. RabbitMQ1.1. 同步通讯与异步通讯1.2. 异步通讯的优缺点1.3. 几种MQ的对比1.4. docker安装运行RabbitMQ 流程1.5. RabbitMQ的几个概念1.6. 五种模型1.6.1. 基本消息队列 1.7. 基本使用1.7.1. 1建立连接时会出现以下界面![在这里插入图片描述](https://img-blog.csd…

MFC扩展库BCGControlBar Pro v34.0 - 网格、报表控件功能升级

BCGControlBar库拥有500多个经过全面设计、测试和充分记录的MFC扩展类。 我们的组件可以轻松地集成到您的应用程序中&#xff0c;并为您节省数百个开发和调试时间。 BCGControlBar专业版 v34.0已正式发布了&#xff0c;该版本包括新的主题任务对话框、图像效果、旋转圆形刻度、…

[电子榨菜]状态管理redux,以及react-redux

0.写在前面 很遗憾&#xff0c;最终还是没能入围2023年的博客评选。 不过不管怎么说&#xff0c;今年需要开个好头。 迫于成本压力吧&#xff0c;最终还是没能顺利离开这里。。。。。。 其实白天已经能放的下啦&#xff0c;我给自己买了喜欢的玩具&#xff0c;去了喜欢的漫…

MySQL:约束主键唯一键

表的约束&#xff1a;表中一定有约束&#xff0c;通过约束让插入表中的数据是符号预期的 约束的本质是通过技术手段&#xff0c;倒逼程序员插入正确的数据 Null约束 这里的Null表示在插入的时候&#xff0c;该属性能否为空&#xff0c;如果是NO&#xff0c;则插入时候必须有数…

《Effective C++》《Resource Management》

文章目录 13、term13:Use objects to manage resources14、term14:Think carefully about copying behavior in resource-managing classes15、term15:Provide access to raw resources in resource-managing classes法一&#xff1a; 使用智能指针的get进行显示转换法二&#…

第11课 实现桌面与摄像头叠加

在上一节&#xff0c;我们实现了桌面捕获功能&#xff0c;并成功把桌面图像和麦克风声音发送给对方。在实际应用中&#xff0c;有时候会需要把桌面与摄像头图像叠加在一起发送&#xff0c;这节课我们就来看下如何实现这一功能。 1.备份与修改 备份demo10并修改demo10为demo11…

Python数据分析从入门到进阶:分类算法

数据分析是处理和解释数据以发现有用信息和洞察的过程。其中&#xff0c;分类算法是数据分析领域的一个重要组成部分&#xff0c;它用于将数据分为不同的类别或组。 本文将介绍分类算法的基本概念和进阶技巧&#xff0c;以及如何在Python中应用这些算法&#xff0c;包括示例代…