创建型模式--4.抽象工厂模式【弗兰奇一家】

2024-04-09 09:20

本文主要是介绍创建型模式--4.抽象工厂模式【弗兰奇一家】,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1. 奔向大海

在海贼世界中,位于水之都的弗兰奇一家是由铁人弗兰奇所领导的以拆船为职业的家族,当然了他们的逆向工程做的也很好,会拆船必然会造船。船是海贼们出海所必备的海上交通工具,它由很多的零件组成,从宏观上看它有这么几个组成部分:船体、动力系统、武器。
在这里插入图片描述

有一天我攒够了钱要出海,找到了弗兰奇一家,发现他们的老大跟着草帽路飞出海了,但是我还是选择相信他们的技术。下面是他们给我制定的造船方案,根据我的购买力提供了不同型号的海贼船,一共是三个级别,如下表:

基础型标准型旗舰型
船体木头钢铁合成金属
动力手动内燃机核能
武器速射炮激光

根据这个表,在造船的时候需要根据不同的型号选择相应的零部件,在设计程序的时候还需要保证遵循开放-封闭原则,即添加了新型号之后不需要修改原有代码,而是添加新的代码。

1.1 船体

因为要建造的这艘船是由多个部件组成的并且每个部件还有不同的品级可供选择,先说船体,关于船体材料的这个属性是可变的,所以还需要给它提供一个抽象类,这样在这个抽象类的子类中就可以更换不同的船体材料了:

// 船体
class ShipBody
{
public:virtual string getShipBody() = 0;virtual ~ShipBody() {}
};class WoodBody : public ShipBody
{
public:string getShipBody() override{return string("用<木材>制作轮船船体...");}
};class IronBody : public ShipBody
{
public:string getShipBody() override{return string("用<钢铁>制作轮船船体...");}
};class MetalBody : public ShipBody
{
public:string getShipBody() override{return string("用<合金>制作轮船船体...");}
};

这样,只要添加了新的造船材料,就给它添加一个对应的子类(父类是 ShipBody),在这个子类重写父类的虚函数getShipBody(),用这种材料把船体造出来就行了。

1.2 动力和武器

知道了如何处理船体部分,那么动力武器部分的处理思路也是一样的:

  • 可以给船提供不同的动力系统,因此这个属性是可变的,所以需要提供一个抽象类
  • 可以给船提供不同的武器系统,因此这个属性也是可变的,所以也需要提供一个抽象类

照葫芦画瓢把代码写一下:

// 动力
class Engine
{
public:virtual string getEngine() = 0;virtual ~Engine() {}
};class Human : public Engine
{
public:string getEngine() override{return string("使用<人力驱动>...");}
};class Diesel : public Engine
{
public:string getEngine() override{return string("使用<内燃机驱动>...");}
};class Nuclear : public Engine
{
public:string getEngine() override{return string("使用<核能驱动>...");}
};// 武器
class Weapon
{
public:virtual string getWeapon() = 0;virtual ~Weapon() {}
};class Gun : public Weapon
{
public:string getWeapon() override{return string("配备的武器是<枪>");}
};class Cannon : public Weapon
{
public:string getWeapon() override{return string("配备的武器是<自动机关炮>");}
};class Laser : public Weapon
{
public:string getWeapon() override{return string("配备的武器是<激光>");}
};

不论是动力还是武器系统都是需要提供一个抽象类,这样它们的子类就可以基于这个抽象基类进行专门定制,如果要对它们进行拓展也只需添加新的类,不需要修改原有代码。

1.3 一艘船

如果有了以上的零件,只需要在工厂中将它们装配到一起,这样就得到了一艘船,这是一艘什么型号的船取决于使用的是什么零件,所以只需要让这艘船对应一个类就可以了,这个类的定义如下:

// 轮船类
class Ship
{
public:Ship(ShipBody* body, Weapon* weapon, Engine* engine) :m_body(body), m_weapon(weapon), m_engine(engine) {}string getProperty(){string info = m_body->getShipBody() + m_weapon->getWeapon() + m_engine->getEngine();return info;}~Ship() {delete m_body;delete m_engine;delete m_weapon;}
private:ShipBody* m_body = nullptr;Weapon* m_weapon = nullptr;Engine* m_engine = nullptr;
};

这艘船使用的零件是通过构造函数参数传递进来的,并在类的内部对这些零件对象进行了保存,这样在释放船这个对象的时候就可以将相应的零件对象一并析构了。

另外,在Ship这个类中保存零件对象的时候使用的是它们的父类指针,这样就可以实现多态了。

2. 准备生产

万事俱备,只剩建厂了。造船厂要生产三种型号的船,那么也就是至少需要三条生产线,所以对应的工厂类也就不止一个,处理思路还是一样的,提供一个抽象的基类,然后在它的子类中完成各种型号的船的组装,每个子类对应的就是一条生产线

2.1 设计图纸

现在,关于抽象工厂模式的逻辑应该是比较清晰了,下面来看一下这个模式对应的UML类图:

在这里插入图片描述

在这个图中有四个抽象类,分别是:

  1. ShipBody 类:船体的抽象类
    有三个子类,在子类中通过不同的材料来建造船体
  2. Weapon 类:武器的抽象类
    有三个子类,在子类中给战船提供不同种类的武器
  3. Engine 类:动力系统抽象类
    有三个子类,在子类中给战船提供不同动力系统
  4. AbstractFactory 类:抽象工厂类
    在子工厂类中生产不同型号的战船
    ShipBodyWeaponEngine有依赖关系,在工厂函数中创建了它们的实例对象
    Ship 类有依赖关系,在工厂函数中创建了它的实例对象

关于Ship类它可以和ShipBodyWeaponEngine可以是聚合关系,也可以是组合关系:

  • 组合关系:析构Ship类对象的时候,也释放了ShipBody 、Weapon、Engine对象
  • 聚合关系:析构Ship类对象的时候,没有释放ShipBody 、Weapon、Engine对象
    在上面的Ship类的析构函数中做了释放操作,因此在UML中将它们之间描述为了组合关系。

在使用抽象工厂模式来处理实际问题的时候,由于实际需求不一样,我们画出的UML类图中有些类和类之间的关系可能也会有所不同,所以上图只适用于当前的业务场景,在处理其他需求的时候还需要具体问题具体分析。

2.2 开工

给上面的程序再添加相应的工厂类,就可以生产出我们需要的型号的船只了,示例代码如下:

#include <iostream>
#include <string>
using namespace std;// 船体
class ShipBody
{
public:virtual string getShipBody() = 0;virtual ~ShipBody() {}
};class WoodBody : public ShipBody
{
public:string getShipBody() override{return string("用<木材>制作轮船船体...");}
};class IronBody : public ShipBody
{
public:string getShipBody() override{return string("用<钢铁>制作轮船船体...");}
};class MetalBody : public ShipBody
{
public:string getShipBody() override{return string("用<合金>制作轮船船体...");}
};// 武器
class Weapon
{
public:virtual string getWeapon() = 0;virtual ~Weapon() {}
};class Gun : public Weapon
{
public:string getWeapon() override{return string("配备的武器是<枪>...");}
};class Cannon : public Weapon
{
public:string getWeapon() override{return string("配备的武器是<自动机关炮>...");}
};class Laser : public Weapon
{
public:string getWeapon() override{return string("配备的武器是<激光>...");}
};// 动力
class Engine
{
public:virtual string getEngine() = 0;virtual ~Engine() {}
};class Human : public Engine
{
public:string getEngine() override{return string("使用<人力驱动>...");}
};class Diesel : public Engine
{
public:string getEngine() override{return string("使用<内燃机驱动>...");}
};class Nuclear : public Engine
{
public:string getEngine() override{return string("使用<核能驱动>...");}
};// 轮船类
class Ship
{
public:Ship(ShipBody* body, Weapon* weapon, Engine* engine) :m_body(body), m_weapon(weapon), m_engine(engine) {}string getProperty(){string info = m_body->getShipBody() + m_weapon->getWeapon() + m_engine->getEngine();return info;}~Ship() {delete m_body;delete m_engine;delete m_weapon;}
private:ShipBody* m_body = nullptr;Weapon* m_weapon = nullptr;Engine* m_engine = nullptr;
};// 工厂类
class AbstractFactory
{
public:virtual Ship* createShip() = 0;virtual ~AbstractFactory() {}
};class BasicFactory : public AbstractFactory
{
public:Ship* createShip() override{Ship* ship = new Ship(new WoodBody, new Gun, new Human);cout << "<基础型>战船生产完毕, 可以下水啦..." << endl;return ship;}
};class StandardFactory : public AbstractFactory
{
public:Ship* createShip() override{Ship* ship = new Ship(new IronBody, new Cannon, new Diesel);cout << "<标准型>战船生产完毕, 可以下水啦..." << endl;return ship;}
};class UltimateFactory : public AbstractFactory
{
public:Ship* createShip() override{Ship* ship = new Ship(new MetalBody, new Laser, new Nuclear);cout << "<旗舰型>战船生产完毕, 可以下水啦..." << endl;return ship;}
};int main()
{AbstractFactory* factroy = new StandardFactory;Ship* ship = factroy->createShip();cout << ship->getProperty();delete ship;delete factroy;return 0;
}

main()函数中,要通过工厂类的工厂函数生产什么型号的战船,和用户的需求息息相关,所以这个选择也是用户通过客户端的操作界面做出的,在这个例子中,关于客户端的界面操作就直接忽略了。

抽象工厂模式适用于比较复杂的多变的业务场景,总体上就是给一系列功能相同但是属性会发生变化的组件(如:船体材料、武器系统、动力系统)添加一个抽象类,这样就可以非常方便地进行后续的拓展,再搭配工厂类就可以创建出我们需要的对象了。

关于简单工厂模式、工厂模式和抽象工厂模式的区别可以做如下总结:

  1. 简单工厂模式不能遵守开放-封闭原则,工厂和抽象工厂模式可以
  2. 简单工厂模式只有一个工厂类,工厂和抽象工厂有多个工厂类
  3. 工厂模式创建的产品对象相对简单,抽象工厂模式创建的产品对象相对复杂
    工厂模式创建的对象对应的类不需要提供抽象类【这产品类组件中没有可变因素】
    抽象工厂模式创建的对象对应的类有抽象的基类【这个产品类组件中有可变因素】

这篇关于创建型模式--4.抽象工厂模式【弗兰奇一家】的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/887760

相关文章

idea中创建新类时自动添加注释的实现

《idea中创建新类时自动添加注释的实现》在每次使用idea创建一个新类时,过了一段时间发现看不懂这个类是用来干嘛的,为了解决这个问题,我们可以设置在创建一个新类时自动添加注释,帮助我们理解这个类的用... 目录前言:详细操作:步骤一:点击上方的 文件(File),点击&nbmyHIgsp;设置(Setti

SpringBoot如何通过Map实现策略模式

《SpringBoot如何通过Map实现策略模式》策略模式是一种行为设计模式,它允许在运行时选择算法的行为,在Spring框架中,我们可以利用@Resource注解和Map集合来优雅地实现策略模式,这... 目录前言底层机制解析Spring的集合类型自动装配@Resource注解的行为实现原理使用直接使用M

Spring 中使用反射创建 Bean 实例的几种方式

《Spring中使用反射创建Bean实例的几种方式》文章介绍了在Spring框架中如何使用反射来创建Bean实例,包括使用Class.newInstance()、Constructor.newI... 目录1. 使用 Class.newInstance() (仅限无参构造函数):2. 使用 Construc

C#原型模式之如何通过克隆对象来优化创建过程

《C#原型模式之如何通过克隆对象来优化创建过程》原型模式是一种创建型设计模式,通过克隆现有对象来创建新对象,避免重复的创建成本和复杂的初始化过程,它适用于对象创建过程复杂、需要大量相似对象或避免重复初... 目录什么是原型模式?原型模式的工作原理C#中如何实现原型模式?1. 定义原型接口2. 实现原型接口3

大数据spark3.5安装部署之local模式详解

《大数据spark3.5安装部署之local模式详解》本文介绍了如何在本地模式下安装和配置Spark,并展示了如何使用SparkShell进行基本的数据处理操作,同时,还介绍了如何通过Spark-su... 目录下载上传解压配置jdk解压配置环境变量启动查看交互操作命令行提交应用spark,一个数据处理框架

Python中conda虚拟环境创建及使用小结

《Python中conda虚拟环境创建及使用小结》本文主要介绍了Python中conda虚拟环境创建及使用小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们... 目录0.前言1.Miniconda安装2.conda本地基本操作3.创建conda虚拟环境4.激活c

使用Python创建一个能够筛选文件的PDF合并工具

《使用Python创建一个能够筛选文件的PDF合并工具》这篇文章主要为大家详细介绍了如何使用Python创建一个能够筛选文件的PDF合并工具,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下... 目录背景主要功能全部代码代码解析1. 初始化 wx.Frame 窗口2. 创建工具栏3. 创建布局和界面控件4

Java中对象的创建和销毁过程详析

《Java中对象的创建和销毁过程详析》:本文主要介绍Java中对象的创建和销毁过程,对象的创建过程包括类加载检查、内存分配、初始化零值内存、设置对象头和执行init方法,对象的销毁过程由垃圾回收机... 目录前言对象的创建过程1. 类加载检查2China编程. 分配内存3. 初始化零值4. 设置对象头5. 执行

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后

Python创建Excel的4种方式小结

《Python创建Excel的4种方式小结》这篇文章主要为大家详细介绍了Python中创建Excel的4种常见方式,文中的示例代码简洁易懂,具有一定的参考价值,感兴趣的小伙伴可以学习一下... 目录库的安装代码1——pandas代码2——openpyxl代码3——xlsxwriterwww.cppcns.c