这篇笔记承接《【学习笔记】C/C++ 设计模式 - 工厂模式(上)》文章,主要记录 “抽象工厂设计模式” 的学习笔记,上一次是以音频播放器来作为例子,主要是想体现出的是接口标准化的优势,但不适用于 “抽象工厂设计模式” 的示例,因此这里改为台式电脑作为例子。
上文说到工厂模式属于 “创建型设计模式” ,但其中的 “创建” 的优势并不明显,那么 “抽象工厂模式” 对 “创建” 具有很好的体现。原因看下面的举例说明。
现实生活中,我们所购买的电脑大部分都是OEM/ODM产品,例如联想的电脑,没有一个组件是由联想自己设计生产,都是根据市场需求来选用各家的组件搭配而成。
台式电脑基本由主板、CPU、内存、硬盘、显示屏、键盘、鼠标、电源组成,每个组件都有各自的用处,也都有不同的厂商设计制造。如 主板 有华硕主板、技嘉主板,CPU 有 Intel 、有 Amd,内存有金士顿、三星,这些不同的品牌有的性价比高,有的性能高寿命长,各有各的特点,设计也都共同遵循行业标准,将这些有效的组合,就形成了不同配置不同价格的电脑了。消费者不需要任何硬件基础,根据自身实际所需选择其中一种配置的电脑就好,不会导致玩游戏的人买了只够日常办公的电脑,而只需日常办公的人却买了发烧级的游戏电脑,白白浪费钱。
我们想购买一台期望性价比高的台式电脑,通常都会请熟悉这些的朋友推荐多种方案的配置单,里面记录了要求使用什么样主板、什么样的CPU、什么样的内存,以及这些组件都在哪里购买,大致需要多少价钱,性能如何。而我们就会拿着这份配置单,去电脑城购买。抽象工厂就相当于你的朋友给你提供多种配置方案的配置单,业务逻辑就相当于去购买并且去使用这些组件来组装电脑。
即由 “抽象工厂模式” 决定如何选择这些组件,并提供相应组件的创建方法,而外部逻辑借助这些创建方法,来决定如何使用这些组件,并且不需要了解组件的具体实现。
某个功能可以通过多个对象关联共同实现,而多个对象各自有不同的实现,且选择不同实现的对象,可以让功能产生变化从而适用于多种应用场景。
如上例,同样的是电脑,同样都是由CPU、内存、硬盘等组成(多个对象),但是把 CPU 由单核更换为多核、把内存由 1G 更换为 32G、把硬盘由机械硬盘改为固态硬盘(不同的实现),性能就完全不一样,同时价格也上涨很多,把原来只能用于普通办公环境的电脑,变成了可以玩大型游戏的电脑(适用于办公运行环境变为适用于可以玩游戏的环境)。
首先把电脑的各个组件以及电脑抽象出来,统一接口标准:
#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 中增加获取尺寸的接口:
也可以将纯虚函数改为虚函数,给出默认实现来规避这种问题,但不适用于所有的情况,也破坏只定义接口不具体实现的规则。
手机扫一扫
移动阅读更方便
你可能感兴趣的文章