创建型模式--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

相关文章

JAVA中整型数组、字符串数组、整型数和字符串 的创建与转换的方法

《JAVA中整型数组、字符串数组、整型数和字符串的创建与转换的方法》本文介绍了Java中字符串、字符数组和整型数组的创建方法,以及它们之间的转换方法,还详细讲解了字符串中的一些常用方法,如index... 目录一、字符串、字符数组和整型数组的创建1、字符串的创建方法1.1 通过引用字符数组来创建字符串1.2

手把手教你idea中创建一个javaweb(webapp)项目详细图文教程

《手把手教你idea中创建一个javaweb(webapp)项目详细图文教程》:本文主要介绍如何使用IntelliJIDEA创建一个Maven项目,并配置Tomcat服务器进行运行,过程包括创建... 1.启动idea2.创建项目模板点击项目-新建项目-选择maven,显示如下页面输入项目名称,选择

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

在JS中的设计模式的单例模式、策略模式、代理模式、原型模式浅讲

1. 单例模式(Singleton Pattern) 确保一个类只有一个实例,并提供一个全局访问点。 示例代码: class Singleton {constructor() {if (Singleton.instance) {return Singleton.instance;}Singleton.instance = this;this.data = [];}addData(value)

在cscode中通过maven创建java项目

在cscode中创建java项目 可以通过博客完成maven的导入 建立maven项目 使用快捷键 Ctrl + Shift + P 建立一个 Maven 项目 1 Ctrl + Shift + P 打开输入框2 输入 "> java create"3 选择 maven4 选择 No Archetype5 输入 域名6 输入项目名称7 建立一个文件目录存放项目,文件名一般为项目名8 确定

Java 创建图形用户界面(GUI)入门指南(Swing库 JFrame 类)概述

概述 基本概念 Java Swing 的架构 Java Swing 是一个为 Java 设计的 GUI 工具包,是 JAVA 基础类的一部分,基于 Java AWT 构建,提供了一系列轻量级、可定制的图形用户界面(GUI)组件。 与 AWT 相比,Swing 提供了许多比 AWT 更好的屏幕显示元素,更加灵活和可定制,具有更好的跨平台性能。 组件和容器 Java Swing 提供了许多

工厂ERP管理系统实现源码(JAVA)

工厂进销存管理系统是一个集采购管理、仓库管理、生产管理和销售管理于一体的综合解决方案。该系统旨在帮助企业优化流程、提高效率、降低成本,并实时掌握各环节的运营状况。 在采购管理方面,系统能够处理采购订单、供应商管理和采购入库等流程,确保采购过程的透明和高效。仓库管理方面,实现库存的精准管理,包括入库、出库、盘点等操作,确保库存数据的准确性和实时性。 生产管理模块则涵盖了生产计划制定、物料需求计划、

顺序表之创建,判满,插入,输出

文章目录 🍊自我介绍🍊创建一个空的顺序表,为结构体在堆区分配空间🍊插入数据🍊输出数据🍊判断顺序表是否满了,满了返回值1,否则返回0🍊main函数 你的点赞评论就是对博主最大的鼓励 当然喜欢的小伙伴可以:点赞+关注+评论+收藏(一键四连)哦~ 🍊自我介绍   Hello,大家好,我是小珑也要变强(也是小珑),我是易编程·终身成长社群的一名“创始团队·嘉宾”

Maven创建项目中的groupId, artifactId, 和 version的意思

文章目录 groupIdartifactIdversionname groupId 定义:groupId 是 Maven 项目坐标的第一个部分,它通常表示项目的组织或公司的域名反转写法。例如,如果你为公司 example.com 开发软件,groupId 可能是 com.example。作用:groupId 被用来组织和分组相关的 Maven artifacts,这样可以避免

模版方法模式template method

学习笔记,原文链接 https://refactoringguru.cn/design-patterns/template-method 超类中定义了一个算法的框架, 允许子类在不修改结构的情况下重写算法的特定步骤。 上层接口有默认实现的方法和子类需要自己实现的方法