【学习笔记】C/C++ 设计模式 - 工厂模式(下)
介绍说明
这篇笔记承接《【学习笔记】C/C++ 设计模式 - 工厂模式(上)》文章,主要记录 “抽象工厂设计模式” 的学习笔记,上一次是以音频播放器来作为例子,主要是想体现出的是接口标准化的优势,但不适用于 “抽象工厂设计模式” 的示例,因此这里改为台式电脑作为例子。
上文说到工厂模式属于 “创建型设计模式” ,但其中的 “创建” 的优势并不明显,那么 “抽象工厂模式” 对 “创建” 具有很好的体现。原因看下面的举例说明。
举例说明
现实生活中,我们所购买的电脑大部分都是OEM/ODM产品,例如联想的电脑,没有一个组件是由联想自己设计生产,都是根据市场需求来选用各家的组件搭配而成。
台式电脑基本由主板、CPU、内存、硬盘、显示屏、键盘、鼠标、电源组成,每个组件都有各自的用处,也都有不同的厂商设计制造。如 主板 有华硕主板、技嘉主板,CPU 有 Intel 、有 Amd,内存有金士顿、三星,这些不同的品牌有的性价比高,有的性能高寿命长,各有各的特点,设计也都共同遵循行业标准,将这些有效的组合,就形成了不同配置不同价格的电脑了。消费者不需要任何硬件基础,根据自身实际所需选择其中一种配置的电脑就好,不会导致玩游戏的人买了只够日常办公的电脑,而只需日常办公的人却买了发烧级的游戏电脑,白白浪费钱。
我们想购买一台期望性价比高的台式电脑,通常都会请熟悉这些的朋友推荐多种方案的配置单,里面记录了要求使用什么样主板、什么样的CPU、什么样的内存,以及这些组件都在哪里购买,大致需要多少价钱,性能如何。而我们就会拿着这份配置单,去电脑城购买。抽象工厂就相当于你的朋友给你提供多种配置方案的配置单,业务逻辑就相当于去购买并且去使用这些组件来组装电脑。
即由 “抽象工厂模式” 决定如何选择这些组件,并提供相应组件的创建方法,而外部逻辑借助这些创建方法,来决定如何使用这些组件,并且不需要了解组件的具体实现。
应用场景
某个功能可以通过多个对象关联共同实现,而多个对象各自有不同的实现,且选择不同实现的对象,可以让功能产生变化从而适用于多种应用场景。
如上例,同样的是电脑,同样都是由CPU、内存、硬盘等组成(多个对象),但是把 CPU 由单核更换为多核、把内存由 1G 更换为 32G、把硬盘由机械硬盘改为固态硬盘(不同的实现),性能就完全不一样,同时价格也上涨很多,把原来只能用于普通办公环境的电脑,变成了可以玩大型游戏的电脑(适用于办公运行环境变为适用于可以玩游戏的环境)。
代码实现
编写步骤:
- 将电脑的接口抽象出来(Computer)
- 将电脑组成的各个组件接口抽象出来(MainBoard、Cpu、Memory、Disk...)
- 依次实现各个组件(ASUS\MSI、Intel\AMD...)
- 根据不同的需求从各个组件中选择合适的组件形成多份电脑硬件配置(联想电脑、小米电脑)
- 根据需要选择联想,或者小米的配置进行使用(参考单元测试)
电脑以及各个组件的抽象方法
首先把电脑的各个组件以及电脑抽象出来,统一接口标准:
#ifndef __COMPUTER_H
#define __COMPUTER_H
// 主板
class MainBoard
{
public:
virtual const char* getMakeName() = 0; // 制造商名称
virtual const char* getModelName() = 0; // 型号名称
};
// CPU
class Cpu
{
public:
virtual const char* getMakeName() = 0; // 制造商名称
virtual const char* getModelName() = 0; // 型号名称
virtual float getFrequency() = 0; // 获取运行频率
virtual int getCoreNumber() = 0; // 获取核心数
};
// 内存
class Memory
{
public:
virtual const char* getMakeName() = 0; // 制造商名称
virtual const char* getModelName() = 0; // 型号名称
virtual int getFrequency() = 0; // 获取工作频率
virtual int getCapacity() = 0; // 获取容量大小
};
// 硬盘
class Disk
{
public:
virtual const char* getMakeName() = 0; // 制造商名称
virtual const char* getModelName() = 0; // 型号名称
virtual int getSpeed() = 0; // 获取读写速度
virtual int getCapacity() = 0; // 获取容量大小
};
// 显示器
class Display
{
public:
virtual const char* getMakeName() = 0; // 制造商名称
virtual const char* getModelName() = 0; // 型号名称
virtual const char* getResolution() = 0; // 分辨率
};
// 键盘
class Keyboard
{
public:
virtual const char* getMakeName() = 0; // 制造商名称
virtual const char* getModelName() = 0; // 型号名称
};
// 鼠标
class Mouse
{
public:
virtual const char* getMakeName() = 0; // 制造商名称
virtual const char* getModelName() = 0; // 型号名称
};
// 计算机
class Computer
{
public:
Computer() {};
virtual ~Computer() {};
virtual const char* getMakeName() = 0; // 制造商名称
virtual const char* getModelName() = 0; // 型号名称
virtual MainBoard* createMainBoard() = 0;
virtual Cpu* createCpu() = 0;
virtual Memory* createMemory() = 0;
virtual Disk* createDisk() = 0;
virtual Display* createDisplay() = 0;
virtual Keyboard* createKeyboard() = 0;
virtual Mouse* createMouse() = 0;
};
#endif
各个部件的具体实现
限于篇幅,每个组件只提供一个具体实现,完整的代码参考附件。
这是实现具体的华硕主板 B360M:
#ifndef __MAINBOARD_ASUS_B360M_H
#define __MAINBOARD_ASUS_B360M_H
#include "../../Computer.h"
class MainBoardAsus_B360M : public MainBoard
{
public:
virtual const char* getMakeName() override; // 制造商名称
virtual const char* getModelName() override; // 型号名称
};
#endif
#include "MainBoardAsus_B360M.h"
const char* MainBoardAsus_B360M::getMakeName()
{
return "ASUS";
}
const char* MainBoardAsus_B360M::getModelName()
{
return "B360M";
}
实现具体的 AMD CPU 3990X:
#ifndef __CPU_AMD_3990X_H
#define __CPU_AMD_3990X_H
#include "../../Computer.h"
// CPU
class CpuAmd_3990X : public Cpu
{
public:
virtual const char* getMakeName() override; // 制造商名称
virtual const char* getModelName() override; // 型号名称
virtual float getFrequency() override; // 获取运行频率
virtual int getCoreNumber() override; // 获取核心数
};
#endif
#include "CpuAmd_3990X.h"
const char* CpuAmd_3990X::getMakeName()
{
return "AMD";
}
const char* CpuAmd_3990X::getModelName()
{
return "3990X";
}
float CpuAmd_3990X::getFrequency()
{
return 5.0f;
}
int CpuAmd_3990X::getCoreNumber()
{
return 64;
}
实现具体的内存 金士顿
#ifndef __MEMORY_KINGSTON_DDR42400_H
#define __MEMORY_KINGSTON_DDR42400_H
#include "../../Computer.h"
class MemoryKingstonDDR42400 : public Memory
{
public:
virtual const char* getMakeName() override; // 制造商名称
virtual const char* getModelName() override; // 型号名称
virtual float getFrequency() override; // 获取工作频率
virtual int getCapacity() override; // 获取容量大小
};
#endif
#include "MemoryKingston_DDR42400.h"
const char* MemoryKingstonDDR42400::getMakeName()
{
return "Kingston";
}
const char* MemoryKingstonDDR42400::getModelName()
{
return "DDR42400";
}
int MemoryKingstonDDR42400::getFrequency()
{
return 2133;
}
int MemoryKingstonDDR42400::getCapacity()
{
return 32;
}
实现具体的硬盘 西数:
#ifndef __DISK_WD_1000GB_H
#define __DISK_WD_1000GB_H
#include "../../Computer.h"
class DiskWD1000GB : public Disk
{
public:
virtual const char* getMakeName() override; // 制造商名称
virtual const char* getModelName() override; // 型号名称
virtual int getSpeed() override; // 获取读写速度
virtual int getCapacity() override; // 获取容量大小
};
#endif
#include "DiskWD_1000GB.h"
const char* DiskWD1000GB::getMakeName()
{
return "WesternDigital";
}
const char* DiskWD1000GB::getModelName()
{
return "HUS722T1TALA604";
}
int DiskWD1000GB::getSpeed()
{
return 300;
}
int DiskWD1000GB::getCapacity()
{
return 1000;
}
实现具体的三星显示屏:
#ifndef __DISPLAY_SAMSUNG_C32JG52QQC_H
#define __DISPLAY_SAMSUNG_C32JG52QQC_H
#include "../../Computer.h"
class DisplaySamsungC32JG52QQC : public Display
{
public:
virtual const char* getMakeName() override; // 制造商名称
virtual const char* getModelName() override; // 型号名称
virtual const char* getResolution() override; // 分辨率
};
#endif
#include "DisplaySamsung_C32JG52QQC.h"
const char* DisplaySamsungC32JG52QQC::getMakeName()
{
return "Samsung";
}
const char* DisplaySamsungC32JG52QQC::getModelName()
{
return "C32JG52QQC";
}
const char* DisplaySamsungC32JG52QQC::getResolution()
{
return "2560×1440";
}
实现具体的樱桃键盘:
#ifndef __KEYBOARD_CHERRY_MX80_H
#define __KEYBOARD_CHERRY_MX80_H
#include "../../Computer.h"
class KeyboardCherryMX80 : public Keyboard
{
public:
virtual const char* getMakeName() override; // 制造商名称
virtual const char* getModelName() override; // 型号名称
};
#endif
#include "KeyboardCherry_MX80.h"
const char* KeyboardCherryMX80::getMakeName()
{
return "Cherry";
}
const char* KeyboardCherryMX80::getModelName()
{
return "MX-Board 8.0";
}
实现具体的罗技鼠标:
#ifndef __MOUSE_LOGITECH_G502_H
#define __MOUSE_LOGITECH_G502_H
#include "../../Computer.h"
class MouseLogitechG502 : public Mouse
{
public:
virtual const char* getMakeName() override; // 制造商名称
virtual const char* getModelName() override; // 型号名称
};
#endif
#include "MouseLogitech_G502.h"
const char* MouseLogitechG502::getMakeName()
{
return "Logitech";
}
const char* MouseLogitechG502::getModelName()
{
return "G502";
}
配置完整的电脑
各个组件现在都已经有具体的实现了,与之前介绍的工厂模式没啥两样,都是根据定义好的标准接口去实现。接下来假设小米和联想根据市场需求各自提供一份电脑配置。
备注说明:考虑到篇幅问题,本文中只给出了小米所选择所有组件的具体实现代码,完整实现工厂可以看附件打包好的代码。
联想提供的电脑配置单以及对应组件的创建方法:
#ifndef __COMPUTER_LENOVO_H
#define __COMPUTER_LENOVO_H
#include <iostream>
#include "Computer.h"
class ComputerLenovo : public Computer
{
public:
ComputerLenovo() { printf("ComputerLenovo\n"); }
~ComputerLenovo() { printf("~ComputerLenovo\n"); }
virtual const char* getMakeName() override;
virtual const char* getModelName() override;
virtual MainBoard* createMainBoard() override;
virtual Cpu* createCpu() override;
virtual Memory* createMemory() override;
virtual Disk* createDisk() override;
virtual Display* createDisplay() override;
virtual Keyboard* createKeyboard() override;
virtual Mouse* createMouse() override;
};
#endif
#include "ComputerLenovo.h"
#include "MainBoard/Msi/MainBoardMsi_B450M.h"
#include "Cpu/Intel/CpuIntel_10980XE.h"
#include "Memory/Kingston/MemoryKingston_DDR42400.h"
#include "Disk/Seagate/DiskSeagate_500GB.h"
#include "Display/AOC/DisplayAoc_I2490VXH.h"
#include "Keyboard/Alienware/KeyboardAlienware_AW768.h"
#include "Mouse/Lenovo/MouseLenovo_M120.h"
const char* ComputerLenovo::getMakeName()
{
return "Lenove";
}
const char* ComputerLenovo::getModelName()
{
return "刃7000";
}
MainBoard* ComputerLenovo::createMainBoard()
{
return new MainBoardMsi_B450M();
}
Cpu* ComputerLenovo::createCpu()
{
return new CpuIntel_10980XE();
}
Memory* ComputerLenovo::createMemory()
{
return new MemoryKingstonDDR42400();
}
Disk* ComputerLenovo::createDisk()
{
return new DiskSeagate500GB();
}
Display* ComputerLenovo::createDisplay()
{
return new DisplayAocI2490VXH();
}
Keyboard* ComputerLenovo::createKeyboard()
{
return new KeyboardAlienwareAW768();
}
Mouse* ComputerLenovo::createMouse()
{
return new MouseLenovoM120();
}
小米提供的电脑配置单以及对应组件的创建方法:
#ifndef __COMPUTER_MI_H
#define __COMPUTER_MI_H
#include <iostream>
#include "Computer.h"
class ComputerMi : public Computer
{
public:
ComputerMi() { printf("ComputerMi\n"); }
~ComputerMi() { printf("~ComputerMi\n"); }
virtual const char* getMakeName() override;
virtual const char* getModelName() override;
virtual MainBoard* createMainBoard() override;
virtual Cpu* createCpu() override;
virtual Memory* createMemory() override;
virtual Disk* createDisk() override;
virtual Display* createDisplay() override;
virtual Keyboard* createKeyboard() override;
virtual Mouse* createMouse() override;
};
#endif
#include "ComputerMi.h"
#include "MainBoard/Asus/MainBoardAsus_B360M.h"
#include "Cpu/Amd/CpuAmd_3990X.h"
#include "Memory/Kingston/MemoryKingston_DDR42400.h"
#include "Disk/WD/DiskWD_1000GB.h"
#include "Display/Samsung/DisplaySamsung_C32JG52QQC.h"
#include "Keyboard/Cherry/KeyboardCherry_MX80.h"
#include "Mouse/Logitech/MouseLogitech_G502.h"
const char* ComputerMi::getMakeName()
{
return "XiaoMi";
}
const char* ComputerMi::getModelName()
{
return "Mi9890";
}
MainBoard* ComputerMi::createMainBoard()
{
return new MainBoardAsus_B360M();
}
Cpu* ComputerMi::createCpu()
{
return new CpuAmd_3990X();
}
Memory* ComputerMi::createMemory()
{
return new MemoryKingstonDDR42400();
}
Disk* ComputerMi::createDisk()
{
return new DiskWD1000GB();
}
Display* ComputerMi::createDisplay()
{
return new DisplaySamsungC32JG52QQC();
}
Keyboard* ComputerMi::createKeyboard()
{
return new KeyboardCherryMX80();
}
Mouse* ComputerMi::createMouse()
{
return new MouseLogitechG502();
}
单元测试
现在联想和小米都确定好了组件配置,下面就选择联想和小米的配置,来演示如何使用这些配置提供的组件:
#ifndef __TEST_COMPUTER_H
#define __TEST_COMPUTER_H
#include "Computer.h"
class TestComputer
{
private:
void ComputerInfoDump(Computer* pComputer);
public:
int test();
};
#endif
#include <iostream>
#include <iomanip>
#include "test_Computer.h"
#include "ComputerLenovo.h"
#include "ComputerMi.h"
#define WIDTH 10
using namespace std;
void TestComputer::ComputerInfoDump(Computer* pComputer)
{
MainBoard *pMainBoard = pComputer->createMainBoard();
Cpu *pCpu = pComputer->createCpu();
Memory *pMemory = pComputer->createMemory();
Disk *pDisk = pComputer->createDisk();
Display *pDisplay = pComputer->createDisplay();
Keyboard *pKeyboard = pComputer->createKeyboard();
Mouse *pMouse = pComputer->createMouse();
printf("--------------------------------------------------------------------------------------------------------\n");
printf("品牌商: %-10s 型号:%s\n", pComputer->getMakeName(), pComputer->getModelName());
printf("-------------------------------\n");
printf("主板信息 -> 制造商: %-16s 型号: %s\n", pMainBoard->getMakeName(), pMainBoard->getModelName());
printf("CPU 信息 -> 制造商: %-16s 型号: %-20s 频率: %-18.2f 核数: %d\n", pCpu->getMakeName(), pCpu->getModelName(), pCpu->getFrequency(), pCpu->getCoreNumber());
printf("内存信息 -> 制造商: %-16s 型号: %-20s 频率: %-18d 容量: %dGB\n", pMemory->getMakeName(), pMemory->getModelName(), pMemory->getFrequency(), pMemory->getCapacity());
printf("硬盘信息 -> 制造商: %-16s 型号: %-20s 速度: %-18d 容量: %dGB\n", pDisk->getMakeName(), pDisk->getModelName(), pDisk->getSpeed(), pDisk->getCapacity());
printf("显示屏 -> 制造商: %-16s 型号: %-20s 分辨率: %s\n", pDisplay->getMakeName(), pDisplay->getModelName(), pDisplay->getResolution());
printf("键盘信息 -> 制造商: %-16s 型号: %s\n", pKeyboard->getMakeName(), pKeyboard->getModelName());
printf("鼠标信息 -> 制造商: %-16s 型号: %s\n", pMouse->getMakeName(), pMouse->getModelName());
printf("--------------------------------------------------------------------------------------------------------\n");
return;
}
int TestComputer::test()
{
Computer* pComputer = nullptr;
// 选择联想电脑
printf("========================================================================================================\n");
pComputer = new ComputerLenovo();
ComputerInfoDump(pComputer);
delete pComputer;
printf("========================================================================================================\n\n\n");
// 选择小米电脑
printf("========================================================================================================\n");
pComputer = new ComputerMi();
ComputerInfoDump(pComputer);
delete pComputer;
printf("========================================================================================================\n\n\n");
return 0;
}
执行的结果:
优点缺点
优点
从单元测试代码当中,可以了解到,业务逻辑不需要关心电脑各个组件的选型以及其具体的实现细节,只需要根据使用场景来选择合适的电脑配置,从而使用里面已经确定好的组件。
无论是增加新的组件的实现(如再增加一个三星品牌的内存),还是新增加电脑(如小米再增加一个电脑或者华为也来增加一个电脑),只需要新增对于的接口实现,不需要改变原有的接口,即可灵活切换使用。
缺点
只要基类产生修改,所涉及到的子类都将会连锁反应,出现各种错误,需要修改所有的子类。
1. 如本文代码说实现的例子,其实台式电脑还有一个很重要的组件,那就是电源。这时候如果我想把电源加进去,那么就需要修改 Computer.h 文件,增加电源的抽象接口,这时候,还需要为所有已经实现的电脑配置,增加电源组件进去,并且需要改动主业务逻辑,调用电源相关的接口(因为没有电,电脑不能工作呀),改动非常大。
2. 如果修改某个组件,新增、修改或删除某个接口,都会影响该组件的所有实现,如在 MainBoard 中增加获取尺寸的接口:
也可以将纯虚函数改为虚函数,给出默认实现来规避这种问题,但不适用于所有的情况,也破坏只定义接口不具体实现的规则。
【学习笔记】C/C++ 设计模式 - 工厂模式(下)的更多相关文章
- C#设计模式学习笔记:(3)抽象工厂模式
本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/7596897.html,记录一下学习过程以备后续查用. 一.引言 接上一篇C#设计模式学习笔记:简单工厂模式( ...
- Java设计模式学习笔记,二:工厂模式
工厂模式,主要实现了创建者和调用者的分离. 分类:1.简单工厂模式:2.工厂方法模式:3.抽象工厂模式. 核心:实例化对象时,用工厂方法代替new操作. 一.简单工厂模式 也叫静态工厂模式,工厂类中实 ...
- 读书笔记之 - javascript 设计模式 - 工厂模式
一个类或者对象中,往往会包含别的对象.在创建这种对象的时候,你可能习惯于使用常规方式,即用 new 关键字和类构造函数. 这会导致相关的俩个类之间产生依赖. 工厂模式,就是消除这俩个类之间的依赖性的一 ...
- [ExtJS5学习笔记]第十一节 Extjs5MVVM模式下系统登录实例
本文地址:http://blog.csdn.net/sushengmiyan/article/details/38815923 实例代码下载地址: http://download.csdn.net/d ...
- .NET设计模式: 工厂模式
.NET设计模式: 工厂模式(转) 转自:http://www.cnblogs.com/bit-sand/archive/2008/01/25/1053207.html .NET设计模式(1): ...
- 【设计模式】Java设计模式 -工厂模式
[设计模式]Java设计模式 -工厂模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 一个有梦有戏的人 @怒放吧德德 分享学习心得,欢迎指正,大家一起学习成长! 目 ...
- jquery源码学习笔记三:jQuery工厂剖析
jquery源码学习笔记二:jQuery工厂 jquery源码学习笔记一:总体结构 上两篇说过,query的核心是一个jQuery工厂.其代码如下 function( window, noGlobal ...
- 并发编程学习笔记(9)----AQS的共享模式源码分析及CountDownLatch使用及原理
1. AQS共享模式 前面已经说过了AQS的原理及独享模式的源码分析,今天就来学习共享模式下的AQS的几个接口的源码. 首先还是从顶级接口acquireShared()方法入手: public fin ...
- Redis学习笔记八:集群模式
作者:Grey 原文地址:Redis学习笔记八:集群模式 前面提到的Redis学习笔记七:主从复制和哨兵只能解决Redis的单点压力大和单点故障问题,接下来要讲的Redis Cluster模式,主要是 ...
- 23种设计模式--工厂模式-Factory Pattern
一.工厂模式的介绍 工厂模式让我们相到的就是工厂,那么生活中的工厂是生产产品的,在代码中的工厂是生产实例的,在直白一点就是生产实例的类,代码中我们常用new关键字,那么这个new出来的实例 ...
随机推荐
- curl 下载地址中有特殊字符解决方案
curl 下载地址中有特殊字符解决方案 情况 使用 curl 下载 地址中带有 特殊字符的时候 比如下面这个地址.实际访问地址不正确,参数丢失问题 curl -o kspf.jpeg https:// ...
- mycat搭建
搭建mycat 一.准备工作 1.确保jdk已安装成功,并且jdk版本选用1.7以上版本 2.准备一台新的主机mysql_mycat放到master的前面做代理 mycat ip 192.168.23 ...
- chronyd为隔离网络设置时间同步
参考链接:https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/configuring_basic ...
- 秦皇岛2020CCPC补题
秦皇岛2020CCPC A,E,F,G,I,K A. A Greeting from Qinhuangdao 知识点:简单题 复杂度:\(O(logn)\) #include<bits/stdc ...
- vue-element Form表单验证没错却一直提示错误
在使用element-UI 的表单时,发生一个验证错误,已输入值但验证的时候却提示没有输入 修改前 <el-form-item>中的prop绑定的是cus_name,而item里面的控件绑 ...
- 多点DMALL × Apache Kyuubi:构建统一SQL Proxy探索实践
伴随着国家产业升级的推进和云原生技术成熟,多点 DMALL 大数据技术也经历了从存算一体到存算分离的架构调整变迁.本文将从引入 Kyuubi 实现统一 SQL Proxy 的角度讲述这一探索实践的历程 ...
- C#调用WPS转换文档到PDF的的实现代码。
1.WPS安装,最好用这个版本别的版本不清楚,安装Pro Plus2016版本. https://ep.wps.cn/product/wps-office-download.html 2.添加相关的引 ...
- Linux deb系统 nginx 配置解析php
如果你是root用户可以不加sudo 首先安装php php-fpm nginx sudo apt-get install php php-fpm nginx -y nginx 是一个轻量级的http ...
- Linux下用rm误删除文件的三种恢复方法
Linux下用rm误删除文件的三种恢复方法 对于rm,很多人都有惨痛的教训.我也遇到一次,一下午写的程序就被rm掉了,幸好只是一个文件,第二天很快又重新写了一遍.但是很多人可能就不像我这么幸运了.本文 ...
- Hadoop安装-分布式-Fully
Hadoop安装-分布式-Fully 〇.所需资料 一.配置 1.基础配置 (1)系统安装 (2)hostname主机名配置 (3)ip地址.dns.hosts映射文件配置 (4)关闭防火墙与seli ...