设计模式学习(二)工厂模式——抽象工厂模式

· 浏览次数 : 13

小编点评

本文主要讨论了抽象工厂模式在相机操作模块中的应用。通过使用工厂方法模式,可以为不同平台的相机创建相应的类,从而实现了代码的重用和扩展性。然而,当需要添加新的相机品牌时,需要对工厂类进行修改,这违反了开闭原则。为了解决这个问题,可以考虑使用抽象工厂模式对产品家族进行进一步的抽象,以便在添加新品牌相机时,只需要添加新的具体工厂类,而不需要修改现有的工厂类。 1. 抽象工厂模式定义了一个创建一系列相关或相互依赖的对象的接口,而无需指定它们具体的类。 2. 通过工厂方法模式,可以为不同平台的相机创建相应的类,例如LinuxBaslerCamera和WindowsBaslerCamera。 3. 当需要添加新的相机品牌时,只需要创建一个新的具体工厂类,而不需要修改现有的工厂类,从而遵循开闭原则。 4. 然而,如果客户端有多个,每个客户端都需要手动修改以选择正确的工厂类,这会导致效率降低。 5. 对于抽象工厂模式的改进方法,将在下一篇文章中讨论。

正文

背景

现在我需要开发一个相机操作模块,它可能在Windows下运行,也可能在Linux下运行。由于在厂家提供的SDK中,Windows下的SDK和Linux下的SDK是有区别的,因此对于一个品牌的相机,我们要创建两个类去封装这两个不同平台下的API。

我们先使用工厂方法模式去设计(以Basler相机为例),类图如下:

对应的代码(就不用智能指针了,要不然类图不好画):

class BaslerCamera
{
public:
    virtual ~BaslerCamera() = default;
    virtual bool OpenCamera() = 0;
};

class LinuxBaslerCamera : public BaslerCamera
{
public:
    ~LinuxBaslerCamera() override = default;
    bool OpenCamera() override
    {
        return true;
    }
};

class WindowsBaslerCamera : public BaslerCamera
{
public:
    ~WindowsBaslerCamera() override = default;
    bool OpenCamera() override
    {
        return true;
    }
};

class CameraFactory
{
public:
    virtual ~CameraFactory() = default;
    virtual BaslerCamera* CreateBaslerCamera() = 0;
};

class LinuxCameraFactory : public CameraFactory
{
public:
    BaslerCamera* CreateBaslerCamera() override
    {
        return new LinuxBaslerCamera();
    }
};

class WindowsCameraFactory : public CameraFactory
{
public:
    BaslerCamera* CreateBaslerCamera() override
    {
        return new WindowsBaslerCamera();
    }
};

//客户端
int main()
{
	//如果更换平台,客户端代码只需要修改这一处
    CameraFactory* cameraFactory = new LinuxCameraFactory();
    
    BaslerCamera* camera = cameraFactory->CreateBaslerCamera();
    
    camera->OpenCamera();
    
    return 0;
}

现在若新增了一个品牌的相机:Sick,那么按照工厂方法模式的设计思路,就会为其创建出对应的抽象工厂类和具体工厂类(具体代码略)。

但是进一步分析可以发现,对于这个模块,它要么在Windows下运行,要么在Linux下运行。即对于抽象产品BaslerCameraSickCamera,要么实例化LinuxBaslerCameraLinuxSickCamera,要么实例化WindowsBaslerCameraWindowsSickCamera

可以说不同的相机被划分在Linux相机和Window相机这两个产品族下,因此我们不需要为每一个品牌的相机都去实现一组对应的工厂类,而是只使用两个工厂WindowsCameraFactoryLinuxCameraFactory去管理各自对应平台下的相机的创建过程。

那么工厂类的代码就会变成这样:

class CameraFactory
{
public:
    virtual ~CameraFactory() = default;
    virtual BaslerCamera* CreateBaslerCamera() = 0;
    virtual SickCamera* CreateSickCamera() = 0;
};

class LinuxCameraFactory : public CameraFactory
{
public:
    BaslerCamera* CreateBaslerCamera() override
    {
        return new LinuxBaslerCamera();
    }

    SickCamera* CreateSickCamera() override
    {
        return new LinuxSickCamera();
    }
};

class WindowsCameraFactory : public CameraFactory
{
public:
    BaslerCamera* CreateBaslerCamera() override
    {
        return new WindowsBaslerCamera();
    }

    SickCamera* CreateSickCamera() override
    {
        return new WindowsSickCamera();
    }
};

这就引出了抽象工厂模式

抽象工厂模式

抽象工厂模式,提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类

AbstractProductAAbstractProductB是两个抽象产品,之所以为抽象,是因为他们可能有多种不同的实现,就刚才的例子来说,抽象产品就是BaslerCameraSickCameraProductA1ProductA2ProductB1ProductB2就是对两个抽象产品的具体分类的实现,对应例子中的LinuxBaslerCameraWindowsBaslerCameraLinuxSickCameraWindowsSickCamera

AbstractFactory是一个抽象工厂基类,对应例子中的CameraFactory,它里面应该包含产品族中每个产品创建的抽象方法。ConcreteFactory1ConcreteFactory2是具体工厂,对应例子中的LinuxCameraFactoryWindowsCameraFactory

对于客户端,通常是在代码中创建一个具体工厂的实例(这个实例就对应着一个产品族),使用这个工厂实例再创建产品族内具体的产品对象。

客户端代码如下:

int main()
{
    /*
    若在windows平台,只需将本句改为:
    CameraFactory* cameraFactory = new WindowsCameraFactory();
    */
    CameraFactory* camera_factory = new LinuxCameraFactory();
    
    BaslerCamera* basler_camera = camera_factory->CreateBaslerCamera();
    basler_camera->OpenCamera();

    SickCamera* sick_camera = camera_factory->CreateSickCamera();
    sick_camera->OpenCamera();

    return 0;
}

完整代码如下:

class BaslerCamera
{
public:
    virtual ~BaslerCamera() = default;
    virtual bool OpenCamera() = 0;
};

class LinuxBaslerCamera : public BaslerCamera
{
public:
    ~LinuxBaslerCamera() override = default;
    bool OpenCamera() override
    {
        return true;
    }
};

class WindowsBaslerCamera : public BaslerCamera
{
public:
    ~WindowsBaslerCamera() override = default;
    bool OpenCamera() override
    {
        return true;
    }
};

class SickCamera
{
public:
    virtual ~SickCamera() = default;
    virtual bool OpenCamera() = 0;
};

class LinuxSickCamera : public SickCamera
{
public:
    ~LinuxSickCamera() override = default;
    bool OpenCamera() override
    {
        return true;
    }
};

class WindowsSickCamera : public SickCamera
{
public:
    ~WindowsSickCamera() override = default;
    bool OpenCamera() override
    {
        return true;
    }
};


class CameraFactory
{
public:
    virtual ~CameraFactory() = default;
    virtual BaslerCamera* CreateBaslerCamera() = 0;
    virtual SickCamera* CreateSickCamera() = 0;
};

class LinuxCameraFactory : public CameraFactory
{
public:
    BaslerCamera* CreateBaslerCamera() override
    {
        return new LinuxBaslerCamera();
    }

    SickCamera* CreateSickCamera() override
    {
        return new LinuxSickCamera();
    }
};

class WindowsCameraFactory : public CameraFactory
{
public:
    BaslerCamera* CreateBaslerCamera() override
    {
        return new WindowsBaslerCamera();
    }

    SickCamera* CreateSickCamera() override
    {
        return new WindowsSickCamera();
    }
};

int main()
{
    //若在windows平台,只需将本句改为CameraFactory* cameraFactory = new WindowsCameraFactory();
    CameraFactory* camera_factory = new LinuxCameraFactory();
    
    BaslerCamera* basler_camera = camera_factory->CreateBaslerCamera();
    basler_camera->OpenCamera();

    SickCamera* sick_camera = camera_factory->CreateSickCamera();
    sick_camera->OpenCamera();

    return 0;
}

优点与缺点

优点:

  • 易于更改产品族:工厂的实例化过程在一个客户端只需要出现一次,修改方便

缺点:

  • 提供方违反开闭原则:如果现在在每个产品族内新增一个品牌相机(如Huaray),那么除了要增加HuarayCameraWindowsHuarayCameraLinuxHuarayCamera三个产品类之外(这是必要的),还要修改CameraFactoryLinuxCameraFactoryWindowsCameraFactory这三个工厂类,违反了开闭原则。
  • 客户端违法开闭原则:客户端在开始的时候都要CameraFactory* camera_factory = new LinuxCameraFactory();,若是要更换为Windows平台,则还需手动修改实例化的类型,违反了开闭原则。而且如果客户端不止一个,则每一个客户端都需要手动修改,效率低。

对于抽象工厂模式的改进方法,将在下一篇文章中讨论。

参考文章

1.《大话设计模式》

与设计模式学习(二)工厂模式——抽象工厂模式相似的内容:

设计模式学习(二)工厂模式——抽象工厂模式

目录背景抽象工厂模式优点与缺点参考文章 背景 现在我需要开发一个相机操作模块,它可能在Windows下运行,也可能在Linux下运行。由于在厂家提供的SDK中,Windows下的SDK和Linux下的SDK是有区别的,因此对于一个品牌的相机,我们要创建两个类去封装这两个不同平台下的API。 我们先使

设计模式学习(二)工厂模式——抽象工厂模式+注册表

目录前言使用简单工厂改进使用注册表改进参考文章 前言 在上一篇文章中我们提到了抽象工厂模式初版代码的一些缺点:①客户端违反开闭原则②提供方违反开闭原则。本文将针对这两点进行讨论 使用简单工厂改进 对于缺点①,我们可以使用简单工厂的思路来改进抽象工厂的初版代码。对于上一篇文章中的例子,我们去除Came

设计模式学习(三):工厂模式

设计模式学习(三):工厂模式 作者:Grey 原文地址: 博客园:设计模式学习(三):工厂模式 CSDN:设计模式学习(三):工厂模式 工厂模式 工厂模式是创建型模式,工厂模式分为:简单工厂,工厂方法和抽象工厂三种类型。 简单工厂 这个模式很简单,比如我们需要制造不同类型的鼠标,我们只需要创建一个鼠

设计模式之抽象工厂模式(学习笔记)

定义 抽象工厂模式是一种创建型设计模式,它提供一个接口,用于创建一系列相关或依赖的对象,而无需指定它们的具体类。抽象工厂模式将对象的创建过程抽象化,允许子类通过实现具体工厂类来定制对象的创建。 为什么使用抽象工厂模式 产品族的一致性 抽象工厂模式确保同一产品族中的对象之间的一致性。 部分遵循开闭原则

Spring框架中的设计模式(重点学习!!!)

# Spring中的设计模式 Spring框架中用到的设计模式有很多,以下是一些常见的设计模式: 1. 依赖注入(DI)和控制反转(IoC):这是Spring框架最核心的设计模式,它允许开发人员将对象之间的依赖关系从代码中抽离出来,由Spring容器负责管理和注入对象之间的依赖关系。 2. 工厂模式

设计模式学习(二)工厂模式——工厂方法模式+注册表

目录工厂方法模式的瑕疵注册表 工厂方法模式的瑕疵 在前一篇笔记中我们介绍了工厂方法模式,示例的类图如下: 考虑一种情况:现在要在程序运行时,根据外部资源,动态的实例化对象。也就是说在编译期我们无法知道要实例化的对象的类型。因此在实例化的过程中,就需要加以判断。 例如,在我的例子中,要根据连接到主机的

【23种设计模式】工厂方法模式(二)

## 前言 在讲述之工厂方法模式前,我们来先了解简单工厂模式,简单工厂模式是最简单的设计模式之一,它虽然不属于GoF的23种设计模式,但是应用也较为频繁,同时它也是学习其他创建型模式的基础。下面我们来先了解下简单工厂模式,然后针对它的缺点来引出工厂方法模式。 ## 简单工厂模式定义 **简单工厂模式

设计模式之简单工厂模式(学习笔记)

定义 简单工厂模式(Simple Factory Pattern)是一种创建型设计模式,它定义一个用于创建对象的接口,但由一个单独的类来实现实际创建的工作。简单工厂模式通过在一个类中集中管理对象的创建过程,可以减少客户端与具体类之间的耦合,使得代码结构更加清晰和易于维护。通过专门定义一个类来负责创建

设计模式之工厂模式(学习笔记)

定义 工厂方法模式是一种创建型设计模式,它定义了一个用于创建对象的接口,但由子类来决定实例化哪一个类。工厂方法使得类的实例化延迟到子类,这样可以让客户端在不需要知道具体类的情况下创建对象。工厂方法模式通过使用继承和多态性,允许子类来控制对象的创建方式,能够更好地应对对象创建的复杂性和变化性。 为什么

设计模式-C#实现简单工厂模式

前言 上一篇文章写了如何使用RabbitMQ做个简单的发送邮件项目,然后评论也是比较多,也是准备去学习一下如何确保RabbitMQ的消息可靠性,但是由于时间原因,先来说说设计模式中的简单工厂模式吧! 在了解简单工厂模式之前,我们要知道C#是一款面向对象的高级程序语言。它有3大特性,封装、继承、多态。