设计模式———什么是工厂模式?

目录

引言

工厂方法模式

别称

意图

问题

解决方案

应用场景

优缺点

运行代码

抽象工厂模式

别称

意图

问题

解决方案

区别

应用场景

优缺点

运行代码

总结 


引言

工厂模式可以分为三种主流的模式:简单工厂模式工厂方法模式抽象工厂模式

而其中简单工厂模式就是在工厂中对需要的生产产品进行区分,然后创造,类似于这样

//程序实例(简单工厂模式)
enum CTYPE {COREA, COREB};
class SingleCore
{
public:
virtual void Show() = 0;
};
//单核A
class SingleCoreA: public SingleCore
{
public:
void Show() { cout<<"SingleCore A"<<endl; }
};
//单核B
class SingleCoreB: public SingleCore
{
public:
void Show() { cout<<"SingleCore B"<<endl; }
};
//唯一的工厂,可以生产两种型号的处理器核,在内部判断
class Factory
{
public:
SingleCore* CreateSingleCore(enum CTYPE ctype)
{
if(ctype == COREA) //工厂内部判断
return new SingleCoreA(); //生产核A
else if(ctype == COREB)
return new SingleCoreB(); //生产核B
else
return NULL;
}
};

而我们下面要详细介绍的,是在设计模式中使用比较多的两个设计模式,分别是工厂方法模式和抽象方法模式。

工厂方法模式

58b8246848894abbad07e8c6520feae9.png

别称

虚拟构造函数、Virtual Constructor、Factory Method

意图

工厂方法模式是一种创造型模式,由父类去提供创建方法,由子类去决定实例化对象的类型

问题

假设我正在开发一款物流运输的管理应用,但一开始我只有卡车运输这个方式,当我需要扩展其他的运输方式,比如是汽车,或者是轮船运输时,我该这么扩展?因为我最初的代码只有卡车的运输方式如果需要扩展其他的方式,就需要把大部分代码重写,这是很繁琐的一件事

解决方案

这时我们可以使用工厂方法模式去设计一个管理模式,父类设置为Logistics(后勤),子类可以有RoadLogistic(公路后勤)SeaLogistic(海上后勤),一般情况下,我们如果想扩展不同的运输方式,可以通过new一个需要的运输方式,但在工厂方法中,建议是在需要特定运输方式的工厂通过new来构建对象,这种方式创建的对象我们一般称为“product”(产品)

比如我想要公路运输,我就在RoadLogistic中new一个运输对象,或者需要海上运输,我就在SeaLogistic中new一个运输对象其实咋一看,这种方式有什么意义呢?但我们需要明白,我们可以在这一个特定的类当中重写工厂的方法,从而改变需要创建产品的类型,甚至是扩展。

首先我们先得理清思路,Logistics类是一个父类,他是生产后勤方式的工厂,RoadLogistic和SeaLogistic是他的子类,而父类中又有一个Transport接口这个接口就是需要子类去重写的,实现的是不同的交付(deliver)方法。

举例来说,卡车(Truck)轮船(Ship)类都必须实现运输(Transport)接口, 该接口声明了一个名为(deliver)交付的方法。 每个类都将以不同的方式实现该方法:卡车走陆路交付货物,轮船走海路交付货物。公路运输(Road­Logistics)类中的工厂方法返回卡车对象,而海路运输(Sea­Logistics)类则返回轮船对象。这是一种层层递进的方法,当我再需要扩展新的运输类型的时候(比如空运),我只需要在添加airLogistic这个类并继承于Logistics类,并且对airLogistic这个类中飞机(airplane)的Transport接口进行实现,就可以添加不同的运输的方式了。

调用工厂方法的代码(通常被称为客户端代码)无需了解不同子类返回实际对象之间的差别。客户端将所有产品视为抽象的运输。客户端知道所有运输对象都提供交付方法,但是并不关心其具体实现方式。用一个通俗易懂的说法,我是客户端,我有一个函数是负责调用运输方式的,我这次需要的是空运,我只需要定义airLogistic的这个类,将这个类传入这个函数中,这个函数是以一个Logistics引用接受的,我里面的代码不需要更改,照常调用transport这个接口,里面运输方式就是我需要的空运的运输方式和交付方法。

应用场景

  • 在你无法预知对象需要实现多少个类别时,可以使用工厂方法模式。
  • 在你希望用户能扩展你软件库或框架的内部组件,可以使用工厂方法模式。
  • 在你希望复用现有对象来节省系统资源,而不是每次都重新创建对象,可以使用使用工厂方法模式。

优缺点

  • 你可以避免创建者和具体产品之间的紧密耦合。
  • 单一职责原则:你可以将产品创建代码放在程序的单一位置, 从而使得代码更容易维护。
  • 开闭原则:无需更改现有客户端代码,你就可以在程序中引入新的产品类型。
  • 应用工厂方法模式需要引入许多新的子类,代码可能会因此变得更复杂。

运行代码

#include <iostream>

//产品总类
class Product
{
public:
    virtual void show() = 0;
};

//产品A
class ProductA : public Product
{
    void show() override
    {
        std::cout << "ProductA" << std::endl;
    }
};
//产品B
class ProductB : public Product
{
    void show() override
    {
        std::cout << "ProductB" << std::endl;
    }
};

//工厂类
class Factory
{
public:
    virtual Product* CreateProduct() = 0;

    void SomeOperation() {
    // Call the factory method to create a Product object.
    Product* product = this->CreateProduct();
    // Now, use the product.
    std::cout << "Creator: The same creator's code has just worked with ";
    product->show();
    delete product;
  }

};

//具体的产品A类,只生产产品A
class ConcretProductA : public Factory
{
    ProductA* CreateProduct() override
    {
        return new ProductA;
    }
};

//具体的产品B类,只生产产品B
class ConcretProductB : public Factory
{
    ProductB* CreateProduct() override
    {
        return new ProductB;
    }
};

int main()
{
    Factory* factoryA = new ConcretProductA;
    Factory* factoryB = new ConcretProductB;
    factoryA->SomeOperation();
    factoryB->SomeOperation();
    return 0;
}

抽象工厂模式

422179051ac24926b553b2770dc6306e.png

别称

Abstract Factory

意图

抽象工厂模式是一种创建型设计模式,它能创建一系列相关的对象,而无需指定其具体类。

问题

假设你正在开发一款家具商店模拟器。你的代码中包括一些类,用于表示一系列的产品,这一系列的产品包括,桌子(table),椅子(chair),沙发(sofa),而系列又可以分为,现代风格(Modern),中式风格(Chinese),你需要去单独设计单个风格对应的家具,不然客户收到这套家具会不开心的。那我就要去设计这个风格对应的每个家具的对象,这样才能保证订阅的时候收到的是同一个风格的,但同时我也不想在有新风格家具的时候,重新去设计更新代码。

解决方案

首先,抽象工厂模式建议为系列中的每件产品明确声明接口(例如椅子、沙发或咖啡桌)。然后,确保所有产品变体都继承这些接口。例如,所有风格的椅子都实现椅子接口;所有风格的咖啡桌都实现咖啡桌接口,以此类推。

接下来,我们需要声明抽象工厂——包含系列中所有产品构造方法的接口。 例如 create­Chair创建椅子 、create­Sofa创建沙发和create­Coffee­Table创建咖啡桌。这些方法必须返回抽象产品类型,即我们之前抽取的那些接口:椅子,沙发和咖啡桌等等。那么该如何处理产品变体呢? 对于系列产品的每个变体, 我们都将基于 抽象工厂接口创建不同的工厂类。 每个工厂类都只能返回特定类别的产品, 例如,现代家具工厂Modern­Factory只能创建现代椅子Modern­Chair 、现代沙发Modern­Sofa和现代咖啡桌Modern­Coffee­Table对象。

客户端代码可以通过相应的抽象接口调用工厂和产品类。 你无需修改实际客户端代码,就能更改传递给客户端的工厂类,也能更改客户端代码接收的产品变体。假设客户端想要工厂创建一把椅子。 客户端无需了解工厂类, 也不用管工厂类创建出的椅子类型。 无论是现代风格, 还是维多利亚风格的椅子,对于客户端来说没有分别, 它只需调用抽象椅子接口就可以了。 这样一来, 客户端只需知道椅子以某种方式实现了 sit­On坐下方法就足够了。此外,无论工厂返回的是何种椅子变体,它都会和由同一工厂对象创建的沙发或咖啡桌风格一致。

区别

这时候我会想,上面我有工厂方法模式,我是不是可以定义一个父类家具工厂类,里面有桌子(table),椅子(chair),沙发(sofa)的接口,然后定义他的子类现代风格子类(Modern),中式风格子类(Chinese),再在里面去实现接口不就可以了吗。对啊,我当时学的时候也这样想,细想一下区别,工厂方法类是对单一的产品进行生产,上面的Logistics类中只是对Transport的不同方式进行重写,而他的子类RoadLogistic和SeaLogistic是具体不同的运输方式,里面是对Transport进行不同方式的重写,而现在我需要的是对多个不同种类的家具进行不同风格的匹配,中式桌子和中式椅子就是匹配中式风格家具,而我父类只需要去声明我要创建桌子和椅子还有沙发,对了,这是一对一和多对多的区别,抽象工厂模式和工厂方法模式的区别就是在于,我的主工厂只负责生产产品,而我的子工厂则负责他这个系列所生产的产品。比如我是一个交通工具工厂,我只要生产引擎,交通工具主体,但我的子工厂,有飞机,汽车,轮船的,他们就负责自己系列的交通工具设计,我是飞机工厂,就设计飞机引擎,飞机主体,或者我是汽车工厂。。。而客户需要的时候,只需要说,“我要一个飞机”,就可以给他一个配套的飞机主体和飞机引擎,而不是飞机主体加一个汽车引擎了。

应用场景

  1. 如果代码需要与多个不同系列的相关产品交互,但是由于无法提前获取相关信息,或者出于对未来扩展性的考虑,你不希望代码基于产品的具体类进行构建, 在这种情况下,你可以使用抽象工厂。
  2. 如果你有一个基于一组抽象方法的类,且其主要功能因此变得不明确,那么在这种情况下可以考虑使用抽象工厂模式。

优缺点

  • 你可以确保同一工厂生成的产品相互匹配。
  • 你可以避免客户端和具体产品代码的耦合。
  • 单一职责原则: 你可以将产品生成代码抽取到同一位置, 使得代码易于维护。
  • 开闭原则: 向应用程序中引入新产品变体时, 你无需修改客户端代码。
  • 由于采用该模式需要向应用中引入众多接口和类, 代码可能会比之前更加复杂。

运行代码

#include <iostream>

//抽象产品系列的,他可以分为不同种类
//比如说给抽象产品系列一个具体的产品系列是桌子
//那这个桌子的类就是有不同风格的桌子的子类
class Table
{
public:
    virtual ~Table(){};
    virtual void show() = 0;
};

class ModernTable : public Table
{
    void show() override
    {
        std::cout << "create ModernTable" << std::endl;
    }
};

class ChineseTable : public Table
{
    void show() override
    {
        std::cout << "create ChineseTable" << std::endl;
    }
};



class chair
{
public:
    virtual void show() = 0;
};

class Modernchair : public chair
{
    void show() override
    {
        std::cout << "create Modernchair" << std::endl;
    }
};

class Chinesechair : public chair
{
    void show() override
    {
        std::cout << "create Chinesechair" << std::endl;
    }
};



// 抽象工厂是控制不同工厂生产产品的工厂
// 中式工厂生产的产品是中式系列的家具
// 同理,现代工厂生产的产品是现代系列的家具

//声明:系列是指一个家具风格系列,种类是指具体的类别是什么,比如说现代风格系列的桌子,桌子就是种类
class AbstractFactory
{
public:
    virtual Table* createTable() = 0;
    virtual chair* createChair() = 0; 
};

// 具体生产中式家具的工厂
// 生产出来的产品是相同系列的不同种类的产品
class ChineseFactory : public AbstractFactory
{
    Table* createTable() override
    {
        return new ChineseTable();
    }
    chair* createChair() override
    {
        return new Chinesechair();
    }
};

// 具体生产现代家具的工厂
class ModernFactory : public AbstractFactory
{
    Table* createTable() override
    {
        return new ModernTable();
    }
    chair* createChair() override
    {
        return new Modernchair();
    }
};


void test(AbstractFactory& abstractFactory)
{
    Table* table = abstractFactory.createTable();
    chair* chair = abstractFactory.createChair();
    table->show();
    chair->show();
}


int main()
{
    ChineseFactory* chineseFactory = new ChineseFactory();
    test(*chineseFactory);
    delete chineseFactory;
    ModernFactory* modernFactory = new ModernFactory();
    test(*modernFactory);
    delete modernFactory;

    return 0;
}

总结 

什么时候用工厂方法模式,什么时候用抽象工厂模式,其实这个在上面也有提起过。

以实际开发的角度出发来看,针对一个UI组件,按钮在windows系统下的功能是按钮,在linux系统底下他也是按钮,他们的作用其实是相同的,但使用他们的系统不同,所以他的渲染不同,我们就可以使用工厂方法模式去创建一个按钮的工厂,根据不同系统的调用去返回不同系统下的点击事件和渲染样式

但在抽象工厂模式下,他是一个更大的板块,是一整个系列的组件,比如说windows有windows的组件,mac有mac的组件,我们不会想看到在windows系统上看到一个mac系统样式的图标,这就是乌龙啦,所以,抽象工厂模式就给出了这样的一个方案,主工厂负责声明一整套的组件,比如说这个抽象工厂就声明了创建按钮和对话框,而他的具体工厂就分为windows工厂和mac工厂,他们都要实现创建按钮和对话框这个接口,而他们创建的按钮和对话框这两个种类又被按钮父类和对话框父类去构建,主类提供一个或多个基础接口,而子类就可以分为windows按钮和mac按钮去分别实现接口(对话框同理),windows工厂创建windows的按钮和对话框,mac也一样,这就是抽象工厂模式,不仅仅局限于工厂方法模式的一对一创建,而是一对多或者多对多的关系,刚刚举的例子就是将抽象工厂具体化后的例子,其实设计模式往往都是现实设计模式的一种映射,是有迹可循的。

最后,感谢大家观看,希望看过有帮助的人可以点赞,有不同意见的可以在评论区下留言。大家一起参考学习,因为我也是在学习的路上,共勉。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值