介绍说明

这篇笔记承接《【学习笔记】C/C++ 设计模式 - 工厂模式(上)》文章,主要记录 “抽象工厂设计模式” 的学习笔记,上一次是以音频播放器来作为例子,主要是想体现出的是接口标准化的优势,但不适用于 “抽象工厂设计模式” 的示例,因此这里改为台式电脑作为例子。

上文说到工厂模式属于 “创建型设计模式” ,但其中的 “创建”  的优势并不明显,那么 “抽象工厂模式” 对 “创建” 具有很好的体现。原因看下面的举例说明。

举例说明

现实生活中,我们所购买的电脑大部分都是OEM/ODM产品,例如联想的电脑,没有一个组件是由联想自己设计生产,都是根据市场需求来选用各家的组件搭配而成。

台式电脑基本由主板、CPU、内存、硬盘、显示屏、键盘、鼠标、电源组成,每个组件都有各自的用处,也都有不同的厂商设计制造。如 主板 有华硕主板、技嘉主板,CPU 有 Intel 、有 Amd,内存有金士顿、三星,这些不同的品牌有的性价比高,有的性能高寿命长,各有各的特点,设计也都共同遵循行业标准,将这些有效的组合,就形成了不同配置不同价格的电脑了。消费者不需要任何硬件基础,根据自身实际所需选择其中一种配置的电脑就好,不会导致玩游戏的人买了只够日常办公的电脑,而只需日常办公的人却买了发烧级的游戏电脑,白白浪费钱。

我们想购买一台期望性价比高的台式电脑,通常都会请熟悉这些的朋友推荐多种方案的配置单,里面记录了要求使用什么样主板、什么样的CPU、什么样的内存,以及这些组件都在哪里购买,大致需要多少价钱,性能如何。而我们就会拿着这份配置单,去电脑城购买。抽象工厂就相当于你的朋友给你提供多种配置方案的配置单,业务逻辑就相当于去购买并且去使用这些组件来组装电脑。

即由 “抽象工厂模式” 决定如何选择这些组件,并提供相应组件的创建方法,而外部逻辑借助这些创建方法,来决定如何使用这些组件,并且不需要了解组件的具体实现。

应用场景

某个功能可以通过多个对象关联共同实现,而多个对象各自有不同的实现,且选择不同实现的对象,可以让功能产生变化从而适用于多种应用场景。

如上例,同样的是电脑,同样都是由CPU、内存、硬盘等组成(多个对象),但是把 CPU 由单核更换为多核、把内存由 1G 更换为 32G、把硬盘由机械硬盘改为固态硬盘(不同的实现),性能就完全不一样,同时价格也上涨很多,把原来只能用于普通办公环境的电脑,变成了可以玩大型游戏的电脑(适用于办公运行环境变为适用于可以玩游戏的环境)。

代码实现

编写步骤:

  1. 将电脑的接口抽象出来(Computer)
  2. 将电脑组成的各个组件接口抽象出来(MainBoard、Cpu、Memory、Disk...)
  3. 依次实现各个组件(ASUS\MSI、Intel\AMD...)
  4. 根据不同的需求从各个组件中选择合适的组件形成多份电脑硬件配置(联想电脑、小米电脑)
  5. 根据需要选择联想,或者小米的配置进行使用(参考单元测试)

电脑以及各个组件的抽象方法

首先把电脑的各个组件以及电脑抽象出来,统一接口标准:

#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++ 设计模式 - 工厂模式(下)的更多相关文章

  1. C#设计模式学习笔记:(3)抽象工厂模式

    本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/7596897.html,记录一下学习过程以备后续查用. 一.引言 接上一篇C#设计模式学习笔记:简单工厂模式( ...

  2. Java设计模式学习笔记,二:工厂模式

    工厂模式,主要实现了创建者和调用者的分离. 分类:1.简单工厂模式:2.工厂方法模式:3.抽象工厂模式. 核心:实例化对象时,用工厂方法代替new操作. 一.简单工厂模式 也叫静态工厂模式,工厂类中实 ...

  3. 读书笔记之 - javascript 设计模式 - 工厂模式

    一个类或者对象中,往往会包含别的对象.在创建这种对象的时候,你可能习惯于使用常规方式,即用 new 关键字和类构造函数. 这会导致相关的俩个类之间产生依赖. 工厂模式,就是消除这俩个类之间的依赖性的一 ...

  4. [ExtJS5学习笔记]第十一节 Extjs5MVVM模式下系统登录实例

    本文地址:http://blog.csdn.net/sushengmiyan/article/details/38815923 实例代码下载地址: http://download.csdn.net/d ...

  5. .NET设计模式: 工厂模式

    .NET设计模式: 工厂模式(转) 转自:http://www.cnblogs.com/bit-sand/archive/2008/01/25/1053207.html   .NET设计模式(1): ...

  6. 【设计模式】Java设计模式 -工厂模式

    [设计模式]Java设计模式 -工厂模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 一个有梦有戏的人 @怒放吧德德 分享学习心得,欢迎指正,大家一起学习成长! 目 ...

  7. jquery源码学习笔记三:jQuery工厂剖析

    jquery源码学习笔记二:jQuery工厂 jquery源码学习笔记一:总体结构 上两篇说过,query的核心是一个jQuery工厂.其代码如下 function( window, noGlobal ...

  8. 并发编程学习笔记(9)----AQS的共享模式源码分析及CountDownLatch使用及原理

    1. AQS共享模式 前面已经说过了AQS的原理及独享模式的源码分析,今天就来学习共享模式下的AQS的几个接口的源码. 首先还是从顶级接口acquireShared()方法入手: public fin ...

  9. Redis学习笔记八:集群模式

    作者:Grey 原文地址:Redis学习笔记八:集群模式 前面提到的Redis学习笔记七:主从复制和哨兵只能解决Redis的单点压力大和单点故障问题,接下来要讲的Redis Cluster模式,主要是 ...

  10. 23种设计模式--工厂模式-Factory Pattern

    一.工厂模式的介绍       工厂模式让我们相到的就是工厂,那么生活中的工厂是生产产品的,在代码中的工厂是生产实例的,在直白一点就是生产实例的类,代码中我们常用new关键字,那么这个new出来的实例 ...

随机推荐

  1. C. 连锁商店(状压dp)

    C. 连锁商店 time limit per test 1 second memory limit per test 512 megabytes input standard input output ...

  2. 实现将机器A上的程序包复制到机器B并更新的脚本

    一.前言 之前有写过如何在单台服务器上执行脚本自动更新程序包,但平时测试过程中相信大部分公司都是需要测试人员在服务器A上进行功能测试,测试通过后再将程序包更新到服务器B上进行安全测试或者性能测试:今天 ...

  3. Java项目有可能做到所有的代码逻辑均可热部署吗?

    前言 首先我们明确下什么叫做热部署,热部署是在不重启java虚拟机的前提下,自动更新class的行为,从而更新整个运行时的逻辑. 在java开发领域,热部署一直是一个难以解决的问题,java虚拟机理论 ...

  4. K8s如何启用cgroup2支持?

    什么是 cgroup ️Reference: control groups(控制组),通常被称为cgroup,是Linux内核的一项功能.它允许将进程组织成分层的组,然后限制和监控各种资源的使用. 内 ...

  5. Containerd 如何配置 Proxy?

    前言 在某些 air gap 场景中,往往需要离线或使用代理 (Proxy), 例如: 需要通过 Proxy pull 容器镜像: Docker Hub: docker.io Quay: quay.i ...

  6. Linux内存泄露案例分析和内存管理分享

    作者:李遵举 一.问题 近期我们运维同事接到线上LB(负载均衡)服务内存报警,运维同事反馈说LB集群有部分机器的内存使用率超过80%,有的甚至超过90%,而且内存使用率还再不停的增长.接到内存报警的消 ...

  7. ThinkPhp5 自定义异常处理类

    在项目的开发过程中异常抛出尤为重要不仅能够做出友好提示帮助掩盖我们伟大的程序员们尴尬的瞬间,还能做到提示开发人员代码白编写的错误,下面进行自定义异常抛出类,纯属个人理解,希望大家指正 首先在框架中我们 ...

  8. 使用VMware安装Linux(CentOS)操作系统

    使用VMware安装CentOS 6.4 环境:Windows7 , VMware Workstation10, CentOS6.4 为什么选择CentOS ? 主流: 目前的Linux操作系统主要应 ...

  9. Crane如何做到利用率提升3倍稳定性还不受损?

    作为云平台用户,我们都希望购买的服务器物尽其用,能够达到最大利用率.然而要达到理论上的节点负载目标是很的,计算节点总是存在一些装箱碎片和低负载导致的闲置资源.下图展示了某个生产系统的CPU资源现状,从 ...

  10. Linux NTP工具的基本使用

    NTP 时间同步 NTP(Network Time Protocol)协议,网络时间协议.利用ntp协议可以实现网络中的计算机时间同步. 实现NTP协议的工具: ntpdate:只能同步一次时间 nt ...