当前位置: 首页 > article >正文

设计模式(3)——工厂模式

文章目录

  • 三、day3
  • 1. 简单工厂模式
  • 2. 工厂模式
  • 3. 抽象工厂模式

三、day3

今天学习最常见的设计模式之一——工厂模式。工厂模式提供了一种创建对象的最佳方式,在创建对象的时候,不会对客户端暴露创建逻辑,并且通过使用一个共同的接口来创建新的对象。

工厂模式有3种不同的实现方式:

  • 简单工厂模式:又叫做静态工厂方法模式。该模式是通过传⼊相关的类型来返回相应的类,这种方式比较单一,可扩展性相对较差。简单工厂模式看为工厂方法模式的一种特例,两者归为一类。
  • 工厂方法模式:一个抽象产品类,可以派生出多个具体产品类。 一个抽象工厂类,可以派生出多个具体工厂类。每个具体工厂类只能创建一个具体产品类的实例。
  • 抽象工厂模式:多个抽象产品类,每个抽象产品类可以派生出多个具体产品类。一个抽象工厂类,可以派生出多个具体工厂类。 每个具体工厂类可以创建多个具体产品类的实例。

1. 简单工厂模式

使用简单工厂的时候,通常不用创建简单工厂类的类实例,没有创建实例的必要。因此可以把简单工厂类实现成一个工具类,直接使用静态方法就可以了。也就是说简单工厂方法通常是静态的,所以也被称为静态工厂。如果要防止客户端无谓的创造简单工厂实例,还可以把简单工厂的构造方法私有化,并添加一个公共成员函数来创建我们需要的对象。

  • 优点
  1. 实现简单,可扩展。
  2. 实现了对象创建与业务逻辑的分离。
  3. 客户端无需知道所创建的具体产品类和类名。
  • 缺点
  1. 实例化对象的逻辑全部封装在一个工厂类里,每次需求变化都要单独修改工厂类(违反了开闭原则),而且出了异常可能没法正常工作。
  2. 不方便扩展子类。

假如我们想要生产如下产品:

// 人造恶魔果实· 绵羊形态
class SheepSmile
{
public:
    void transform()
    {
        cout << "变成人兽 -- 山羊人形态..." << endl;
    }
    void ability()
    {
        cout << "将手臂变成绵羊角的招式 -- 巨羊角" << endl;
    }
};

// 人造恶魔果实· 狮子形态
class LionSmile
{
public:
    void transform()
    {
        cout << "变成人兽 -- 狮子人形态..." << endl;
    }
    void ability()
    {
        cout << "火遁· 豪火球之术..." << endl;
    }
};

// 人造恶魔果实· 蝙蝠形态
class BatSmile
{
public:
    void transform()
    {
        cout << "变成人兽 -- 蝙蝠人形态..." << endl;
    }
    void ability()
    {
        cout << "声纳引箭之万剑归宗..." << endl;
    }
};

无论哪一种产品,都有形态变化 transform() 和使用能力 ability() 两种功能。当然,这些产品在生产的过程中可能需要复杂的参数,但在这里这些类构造函数的参数暂时被省略。

如果想要生产这些产品,我们可以创建一个工厂类,然后给这个工厂添加一个工厂函数(通过这个函数来创建我们需要的对象,关于这个函数一般将其称之为工厂函数),我们通过枚举变量控制工厂类要生成哪一类产品,比如 Sheep,Lion或Bat。

enum class Type:char{SHEEP, LION, BAT};
// 恶魔果实工厂类
class SmileFactory
{
public:
    SmileFactory() {}
    ~SmileFactory() {}
    void* createSmile(Type type)
    {
        void* ptr = nullptr;
        switch (type)
        {
        case Type::SHEEP:
            ptr = new SheepSmile;
            break;
        case Type::LION:
            ptr = new LionSmile;
            break;
        case Type::BAT:
            ptr = new BatSmile;
            break;
        default:
            break;
        }
        return ptr;
    }
};

int main()
{
    SmileFactory* factory = new SmileFactory;
    BatSmile* batObj = (BatSmile*)factory->createSmile(Type::BAT);
    return 0;
}

我们创建了工厂类 SmileFactory,该类通过工厂函数 createSmile 接受枚举变量创建我们需要的对象实例,并返回 void* 类型的指针。因为每个一对象的指针类型都不同,我们这里统一返回 void* ,我们使用的时候强制转成实际类型即可。

我们也可以通过多态返回基类指针,我们可以直接通过基类指针调用函数,虚函数表会协助我们判断调用的是哪一个对象的虚函数。

#include <iostream>
using namespace std;

class AbstractSmile
{
public:
    virtual void transform() {}
    virtual void ability() {}
    virtual ~AbstractSmile() {}
};
// 人造恶魔果实· 绵羊形态
class SheepSmile : public AbstractSmile
{
public:
    void transform() override
    {
        cout << "变成人兽 -- 山羊人形态..." << endl;
    }
    void ability() override
    {
        cout << "将手臂变成绵羊角的招式 -- 巨羊角" << endl;
    }
};

// 人造恶魔果实· 狮子形态
class LionSmile : public AbstractSmile
{
public:
    void transform() override
    {
        cout << "变成人兽 -- 狮子人形态..." << endl;
    }
    void ability() override
    {
        cout << "火遁· 豪火球之术..." << endl;
    }
};

class BatSmile : public AbstractSmile
{
public:
    void transform() override
    {
        cout << "变成人兽 -- 蝙蝠人形态..." << endl;
    }
    void ability() override
    {
        cout << "声纳引箭之万剑归宗..." << endl;
    }
};

// 恶魔果实工厂类
enum class Type:char{SHEEP, LION, BAT};
class SmileFactory
{
public:
    SmileFactory() {}
    ~SmileFactory() {}
    AbstractSmile* createSmile(Type type)
    {
        AbstractSmile* ptr = nullptr;
        switch (type)
        {
        case Type::SHEEP:
            ptr = new SheepSmile;
            break;
        case Type::LION:
            ptr = new LionSmile;
            break;
        case Type::BAT:
            ptr = new BatSmile;
            break;
        default:
            break;
        }
        return ptr;
    }
};

int main()
{
    SmileFactory* factory = new SmileFactory;
    AbstractSmile* obj = factory->createSmile(Type::BAT);
    obj->transform();
    obj->ability();
    return 0;
}

很简单,因为每个产品都有形态变化 transform() 和使用能力 ability() 两种功能,我们可以将这两个函数抽象化成一个基类 AbstractSmileAbstractSmile 类不仅要将两个功能函数 virtual 化,析构函数也需要加 virtual),我们只需在三个产品类中继承 AbstractSmile,并对两个功能函数 transform() 和使 ability() 进行重写。

在工厂类的工厂函数中,我们通过 switch-case 返回基类指针即可,基类指针指向我们想要指向的产品类,当我们通过基类指针调用派生类功能函数时,编译器通过动态联编和虚函数表协助我们判断调用哪一个函数。

简单工厂模式的 UML 图如下:

在这里插入图片描述

图片来源:https://subingwen.cn/design-patterns/simple-factory/#3-%E5%A6%82%E4%BD%95%E7%94%9F%E4%BA%A7

流程很简单,因为每个产品都有形态变化 transform() 和使用能力 ability() 两种功能,我们可以将这两个函数抽象化成一个基类 AbstractSmileAbstractSmile 类不仅要将两个功能函数 virtual 化,析构函数也需要加 virtual),我们只需在三个产品类中继承 AbstractSmile,并对两个功能函数 transform() 和使 ability() 进行重写。这便生成了三个产品类 SheepSmileLionSmileBatSmile,可以将其看作产品的模具。

当我们想要生产特定产品,比如 sheep 时,我们将枚举变量 sheep 输入至工厂 SmileFactory 中,工厂 SmileFactory 会根据我们给的产品名称(枚举变量 sheep ),使用对应的模具生产产品并返回。

简单工厂模式的缺点

尽管简单工厂模式能生成我们需要的产品,但是如果我们想要生成更多类型不同的产品,超出了枚举变量的范围,那么就需要在工厂函数中添加更多的case,很明显这违背了封闭原则,也就意味着需要基于开放原则来解决这个问题。

2. 工厂模式

工厂模式很好的解决了简单工厂模式的缺点,简单工厂模式是只有一个工厂类,而工厂模式是有很多的工厂类:

  • 一个基类,包含一个虚工厂函数,用于实现多态。
  • 多个子类,重写父类的工厂函数。每个子工厂类负责生产一种恶魔果实,这相当于再次解耦,将工厂类的职责再次拆分、细化,如果要生产新品种的恶魔果实,那么只需要添加对应的工厂类,无需修改原有的代码。

工厂模式包含以下几个角色

  1. 抽象产品(Product):定义了产品的接口,是所有具体产品类的共同父类。
  2. 具体产品(Concrete Product):实现了抽象产品接口的具体产品类,是工厂方法模式所创建的对象。
  3. 抽象工厂(Factory):定义了工厂方法的接口,负责创建抽象产品的对象。
  4. 具体工厂(Concrete Factory):实现了抽象工厂接口,具体工厂类根据具体业务逻辑,创建具体产品的对象。

工厂方法模式的工作流程如下:

  1. 客户端通过调用具体工厂类的工厂方法来创建产品对象。
  2. 具体工厂类根据客户端的请求,调用具体产品类的构造方法创建具体产品对象。
  3. 具体工厂类将创建的具体产品对象返回给客户端。

通过工厂方法模式,客户端与具体产品类解耦,客户端只需关心抽象产品和抽象工厂,具体产品的创建由具体工厂类来完成。这样,当需要引入新的产品时,只需创建一个新的具体产品类和对应的具体工厂类,而不需要修改客户端代码。

工厂方法模式适用于以下情况:

  1. 当一个类无法预知它需要创建的对象的具体类时,可以使用工厂方法模式。
  2. 当一个类希望将对象的创建责任委托给多个子类中的某一个时,可以使用工厂方法模式。
  3. 当一个类需要通过其子类来指定创建对象时,可以使用工厂方法模式。

总结来说,工厂方法模式通过定义一个创建对象的接口,将对象的实例化延迟到子类中去完成,实现了对象的创建与使用的解耦,提高了系统的可扩展性和灵活性。

工厂模式的代码如下:

#include <iostream>
using namespace std;

class AbstractSmile
{
public:
    virtual void transform() = 0;
    virtual void ability() = 0;
    virtual ~AbstractSmile() {}
};
// 人造恶魔果实· 绵羊形态
class SheepSmile : public AbstractSmile
{
public:
    void transform() override
    {
        cout << "变成人兽 -- 山羊人形态..." << endl;
    }
    void ability() override
    {
        cout << "将手臂变成绵羊角的招式 -- 巨羊角" << endl;
    }
};

// 人造恶魔果实· 狮子形态
class LionSmile : public AbstractSmile
{
public:
    void transform() override
    {
        cout << "变成人兽 -- 狮子人形态..." << endl;
    }
    void ability() override
    {
        cout << "火遁· 豪火球之术..." << endl;
    }
};

class BatSmile : public AbstractSmile
{
public:
    void transform() override
    {
        cout << "变成人兽 -- 蝙蝠人形态..." << endl;
    }
    void ability() override
    {
        cout << "声纳引箭之万剑归宗..." << endl;
    }
};

// 恶魔果实工厂类
class AbstractFactory
{
public:
    virtual AbstractSmile* createSmile() = 0;
    virtual ~AbstractFactory() {}
};

class SheepFactory : public AbstractFactory
{
public:
    AbstractSmile* createSmile() override
    {
        return new SheepSmile;
    }
    ~SheepFactory()
    {
        cout << "释放 SheepFactory 类相关的内存资源" << endl;
    }
};

class LionFactory : public AbstractFactory
{
public:
    // 工厂函数
    AbstractSmile* createSmile() override
    {
        return new LionSmile;
    }
    ~LionFactory()
    {
        cout << "释放 LionFactory 类相关的内存资源" << endl;
    }

};

class BatFactory : public AbstractFactory
{
public:
    // 工厂函数
    AbstractSmile* createSmile() override
    {
        return new BatSmile;
    }
    ~BatFactory()
    {
        cout << "释放 BatFactory 类相关的内存资源" << endl;
    }
};

int main()
{
    AbstractFactory* factory = new BatFactory;
    AbstractSmile* obj = factory->createSmile();
    obj->transform();
    obj->ability();
    return 0;
}

工厂模式的UML类图如下所示:

在这里插入图片描述

图片来源:https://subingwen.cn/design-patterns/simple-factory/#3-%E5%A6%82%E4%BD%95%E7%94%9F%E4%BA%A7

上半部分和简单工厂模式一样,因为每个产品都有形态变化 transform() 和使用能力 ability() 两种功能,我们可以将这两个函数抽象化成一个基类 AbstractSmileAbstractSmile 类不仅要将两个功能函数 virtual 化,析构函数也需要加 virtual),我们只需在三个产品类中继承 AbstractSmile,并对两个功能函数 transform() 和使 ability() 进行重写。这便生成了三个产品类 SheepSmileLionSmileBatSmile,可以将其看作产品的模具。

但不同于简单工厂模式,我们这里将工厂类抽象化为了一个接口类 AbstractFactory,其包含两个虚函数,一个虚函数是 createSmile(),用于返回 AbstractSmile 的指针,指针指向生成的产品(多态);另一个函数是析构函数。

当我们需要生成一个新产品时,我们只需要继承这个抽象工厂类 AbstractFactory,然后添加专属于其自身的属性,最后调用重写的 createSmile() 函数,返回一个指向新产品的 AbstractSmile 指针即可,当我们通过 AbstractSmile 指针调用产品的方法时,多态确保我们能够准确指向这个产品的方法。

在我们创建工厂以及产品对象时,必须使用两个抽象类作为指针指向特定产品的工厂和产品模具,比如 AbstractSmileAbstractFactory

  • 优点

符合开放-关闭原则(简称开-闭原则)将创建对象的逻辑与任务交给了工厂类。

  • 缺点

每次新增产品,产品类都需要创建对应工厂类,增加了系统的开销。

3. 抽象工厂模式

抽象工厂模式主要是来解决产品簇问题。

产品簇是什么呢?通俗点来说,造船厂只生产船,但船包括了船体、武器系统、动力系统,这又分为了三个部门,每一个部门又负责很多子产品,这些一系列的产品就是产品簇。如下图:
在这里插入图片描述

在简单工厂模式下,假设武器系统部门现在只有产品1和产品2,如果想要新添加一个产品3,那么需要从工厂函数中添加更多的case,根本的更新工厂,很明显这违背了封闭原则,也就意味着需要基于开放原则来解决这个问题。如下图:

在这里插入图片描述

如果我们想要增加炮弹作为新产品,那么必须将武器部门重组,这样才能有专门负责炮弹的小组进行产品的更新、生产,很明显这违背了开放-封闭(类、模块、函数等可以扩展,但是不可以修改)原则。

在工厂模式下,如果要增加一个炮弹产品,相当于新增了两个类,虽然看起来效率和简单工厂模式差不多,但是执行了开发-封闭原则。如下图:

在这里插入图片描述

我们并没有重组武器系统部门,只是添加了两个类,一个是炮弹模具一个是炮弹的属性类,这样我们只是实现了拓展,并没有进行修改。

而如果我们现在想要新增加一个部门,比如动力系统部门,那么相当于增加了8个类,如下图:
在这里插入图片描述

如果我们使用抽象工厂模式,产品簇越多,效果越明显,节省的资源越多,如下图:
在这里插入图片描述

示例代码:

#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;
}

参考:

简单工厂模式 - 人造恶魔果实工厂1 | 爱编程的大丙

工厂模式 - 人造恶魔果实工厂2 | 爱编程的大丙

抽象工厂模式 - 弗兰奇一家 | 爱编程的大丙

「设计模式」工厂模式 - 知乎

设计模式之工厂模式 - TechNomad - 博客园


http://www.kler.cn/a/474321.html

相关文章:

  • 计算机网络——网络层—IP数据报与分片
  • 在JavaScript开发中,如何判断对象自身为空?
  • iOS - AutoreleasePool
  • (二十八)Flask之wtforms库【上手使用篇】
  • Docker Compose 启动 Harbor 并指定网络
  • git撤回提交、删除远端某版本、合并指定版本的更改
  • Vscode 如何使用GitHub Copilot
  • SSL 证书格式和证书文件扩展名:完整指南
  • 大中小厂技术面试流程
  • vue(2,3), react (16及以上)开发者工具资源
  • 计算机网络之---数据传输与比特流
  • 嵌入式系统 (5.嵌入式Linux网络应用开发)
  • 使用TCP协议实现智能聊天机器人
  • Cline+DeepSeek-v3-软件工程的浪潮:从切图仔到AI辅助编程
  • 【Leetcode 热题 100】20. 有效的括号
  • markdown语法中的表格快速转换成word中的表格。
  • 【网页自动化】篡改猴入门教程
  • C#标识符和关键字
  • 2025.01.15docker
  • Pytorch单、多GPU和CPU训练模型保存和加载
  • C++二十三种设计模式之观察者模式
  • 强化学习入门谈
  • Linux C/C++编程-UDP套接字编程示例
  • 微软人工智能研究院推出OLA-VLM:一种以视觉为中心的方法来优化多模态大型语言模型
  • Redis Stream
  • Git指令