设计模式学习(二)工厂模式——简单工厂模式
前言
工厂模式是一种常用的设计模式,属于创建型模式之一。它的主要目的是为了解耦组件之间的依赖关系。通过使用工厂模式,系统中的具体类的实例化过程可以被抽象出来,从而使得系统更加模块化,增强了系统的可维护性和可扩展性。
工厂模式可以分为三种类型:简单工厂模式,工厂方法模式和抽象工厂模式。本文先讨论简单工厂模式。
背景
在工业相机领域,多个品牌如Basler、Sick、Huaray等,各自提供了控制相机的API。本文中我们按照提供方和使用方两个角色来进行分析,以便更好地理解工厂模式的优点:
- 提供方:负责封装不同品牌相机的API,创建统一的接口。目的是简化相机控制的复杂性,使得使用方可以更容易地集成和操作这些相机。
- 使用方(客户端):利用提供方开发的代码,根据自己的具体需求,实例化选定的相机品牌,并执行所需的操作
一般方法
在一般方法中:
①提供方封装了三个不同品牌的相机类,其中包含了他们各自的相机操作函数
②使用方(即main函数),根据外部传入的相机品牌名称,来实例化具体相机对象,然后对其进行一系列操作。
代码如下:
class BaslerCameraDevice
{
public:
~BaslerCameraDevice();
bool Init(){}
bool OpenDevice(){}
};
class HuarayCameraDevice
{
public:
~HuarayCameraDevice();
bool Init(){}
bool OpenDevice(){}
};
class SickCameraDevice
{
public:
~SickCameraDevice();
bool Init(){}
bool OpenDevice(){}
};
int main(int argc, char* argv[])
{
if (argc < 2)
{
return -1;
}
std::string vendor_name = argv[1];
if("Basler" == vendor_name)
{
std::shared_ptr<BaslerCameraDevice> camera = std::make_shared<BaslerCameraDevice>();
camera->Init();
camera->OpenDevice();
}
else if("Huaray" == vendor_name)
{
std::shared_ptr<HuarayCameraDevice> camera = std::make_shared<HuarayCameraDevice>();
camera->Init();
camera->OpenDevice();
}
else if("Sick" == vendor_name)
{
std::shared_ptr<SickCameraDevice> camera = std::make_shared<SickCameraDevice>();
camera->Init();
camera->OpenDevice();
}
}
上述代码虽然比较直观,容易理解,但不利于维护和扩展。
比如现在需要新增一个品牌的相机,那么除了在提供方中增加一个对新品牌相机的封装(这是必要的)之外,在使用方的代码中还要添加新的条件分支来支持对新品牌相机的操作,这违反了设计原则中的开放封闭原则。
为了避免上述情况,提供方可以将相机实例化的过程封装到一个类中,这个类的职责就是根据传入的参数来实例化具体的相机对象,这样使用方就无需关心相机对象的创建过程了,在有新的相机品牌加入时,使用方的代码也不会变动。
按照这个思路,就有了简单工厂模式,而这个负责创建相机对象的类就是简单工厂类。
简单工厂模式
简介
简单工厂模式是工厂模式中的一个便捷实现,是通过一个单一的静态方法来创建不同类型的对象,根据传入参数的不同,可以返回不同类的实例。
示例
抽象相机类(接口类):CameraDevice
具体相机类:BaslerCameraDevice,SickCameraDevice,HuarayCameraDevice
简单工厂类:CameraDeviceFactory
UML类图如下:
代码如下:
class CameraDevice
{
public:
CameraDevice();
virtual ~CameraDevice() = 0;
virtual bool Init();
virtual bool OpenDevice() = 0;
};
class BaslerCameraDevice : public CameraDevice
{
public:
~BaslerCameraDevice() override;
bool Init() override;
bool OpenDevice() override;
};
class HuarayCameraDevice : public CameraDevice
{
public:
~HuarayCameraDevice() override;
bool Init() override;
bool OpenDevice() override;
};
class SickCameraDevice : public CameraDevice
{
public:
~SickCameraDevice() override;
bool Init() override;
bool OpenDevice() override;
};
class CameraDeviceFactory
{
public:
static std::shared_ptr<CameraDevice> CreateCamera(std::string vendor_name)
{
if (vendor_name == "Basler")
{
return std::make_shared<BaslerCameraDevice>();
}
else if (vendor_name == "Huaray")
{
return std::make_shared<HuarayCameraDevice>();
}
else if (vendor_name == "Sick")
{
return std::make_shared<SickCameraDevice>();
}
return nullptr;
}
};
int main(int argc, char* argv[])
{
if (argc < 2)
{
return -1;
}
std::string vendor_name = argv[1];
auto camera = CameraDeviceFactory::CreateCamera(vendor_name);
if (camera == nullptr)
{
return -1;
}
camera->Init();
camera->OpenDevice();
}
优点
- 调用简单:将对象的创建逻辑集中到了简单工厂类中,客户端只需要与工厂类进行交互,而无需关注具体对象的创建过程,降低了客户端代码的复杂性。
- 解耦:客户端代码与具体对象解耦,可以通过工厂类创建不同的对象,而无需修改客户端代码,提高了代码的灵活性和可扩展性。
缺点
- 违反开闭原则:对于提供方,每新增具体产品,都需要修改工厂类的内部代码,更新创建逻辑,违反了开闭原则。
- 扩展困难:若产品创建的过程复杂,当产品种类增多可能导致工厂类的代码过于臃肿,不宜与扩展和维护。
使用场景
- 产品类型相对固定:当系统中的对象种类不多且不经常变化时,使用简单工厂模式非常合适。因为在这种情况下,向工厂类添加新的产品类型并不频繁,也就不会经常违反开闭原则。
- 隐藏对象创建细节:当客户端只是需要产品的接口而不关心具体实现时,简单工厂可以隐藏对象的创建细节,客户端只需通过工厂接口请求其需要的对象即可,而无需关心对象是如何被创建、组装的。
- 对象创建过程简单:当需要创建的对象逻辑比较简单,不涉及复杂的条件判断或算法时,可以使用简单工厂模式