本文通过一个例子简单介绍简单工厂模式、工厂模式和抽象工厂模式。
1.简单工厂(静态)
假如我想换个手机,换什么手机呢?可以考虑苹果或者华为手机,那我们用简单工厂模式来实现这个功能:
我们关注的产品是手机,那生产手机的工厂有苹果,有华为,用户也不确定选哪种怎么办?这里用简单工厂模式:定义两个枚举类型,然后写一个工厂类,根据用户传入的枚举类型生产对应品牌的手机:
手机基类:
#pragma once
class MyPhone
{
public:MyPhone(double price);~MyPhone();double getPrice() { return m_price; }
private:double m_price;
};
#include "MyPhone.h"MyPhone::MyPhone(double price) :m_price(price)
{
}
MyPhone::~MyPhone()
{
}
苹果手机:
#pragma once
#include "MyPhone.h"
class Phone_Apple:public MyPhone
{
public:Phone_Apple(double price);~Phone_Apple();
};
#include "Phone_Apple.h"
#include "stdio.h"
Phone_Apple::Phone_Apple(double price):MyPhone(price)
{printf("This is iPhone!");
}
Phone_Apple::~Phone_Apple()
{
}
华为手机:
#pragma once
#include "MyPhone.h"class Phone_Huawei :public MyPhone
{
public:Phone_Huawei(double price);~Phone_Huawei();
};
#include "Phone_Huawei.h"
#include "stdio.h"
Phone_Huawei::Phone_Huawei(double price) :MyPhone(price)
{printf("This is HUAWEI!");
}
Phone_Huawei::~Phone_Huawei()
{
}
简单工厂类:
#pragma once
#include "MyPhone.h"
enum phoneType
{APPLE_Phone,HUAWEI_Phone
};
class SimpleFactory
{
public:SimpleFactory();~SimpleFactory();MyPhone* createPhone(phoneType type);
};
#include "SimpleFactory.h"
#include "Phone_Apple.h"
#include "Phone_Huawei.h"
SimpleFactory::SimpleFactory()
{}
SimpleFactory::~SimpleFactory()
{
}
MyPhone* SimpleFactory::createPhone(phoneType type)
{MyPhone* phone = nullptr;switch (type){case APPLE_Phone:phone = new Phone_Apple(6000);break;case HUAWEI_Phone:phone = new Phone_Huawei(5600);break;}return phone;
}
用户根据自定义类型修改创建:
SimpleFactory* factory = new SimpleFactory();//用户指定要苹果MyPhone* phone = factory->createPhone(APPLE_Phone);cout << "Price: " << phone->getPrice() << endl;delete phone;delete factory;
2.工厂模式
这里的工厂类似于一个手机销售店,你需要什么手机就提供什么手机,没有问题,如果用户增加了需求,想要添加三星、OPPO等,怎么办?
- 可以分别继承Myphone类,分别实现不同产品;
- 在SimpleFactory类中添加对应类别;
- 在createPhone方法中添加对应类别(还要引入对应的新的类和头文件);
- 可通过配置文件等方式准备好已有的类型供用户选择调用;
很明显每次都要对原文件修改,如何避免呢?这里进入工厂模式:将生产手机的工厂抽象出来,然后将具体的生产厂家继承自这个抽象工厂,分别创建自己的产品:
抽象工厂类:
#pragma once
#include "MyPhone.h"
class IFactory
{
public:IFactory();~IFactory();virtual MyPhone* createPhone() = 0;
};
华为工厂:
#pragma once
#include "IFactory.h"
#include "MyPhone.h"
class HuaweiFactory : public IFactory
{
public:HuaweiFactory();~HuaweiFactory();virtual MyPhone* createPhone();
};
#include "HuaweiFactory.h"
#include "Phone_Huawei.h"
HuaweiFactory::HuaweiFactory()
{
}
HuaweiFactory::~HuaweiFactory()
{
}
MyPhone* HuaweiFactory::createPhone()
{MyPhone* phone = new Phone_Huawei(5600);return phone;
}
苹果工厂:
#pragma once
#include "IFactory.h"
#include "MyPhone.h"
class IphoneFactory : public IFactory
{
public:IphoneFactory();~IphoneFactory();virtual MyPhone* createPhone();
};
#include "IphoneFactory.h"
#include "Phone_Apple.h"
IphoneFactory::IphoneFactory()
{
}
IphoneFactory::~IphoneFactory()
{
}
MyPhone* IphoneFactory::createPhone()
{MyPhone* phone = new Phone_Apple(6000);return phone;
}
//3.2 工厂模式IFactory* factory = new IphoneFactory();MyPhone* phone = factory->createPhone();cout << "Price: " << phone->getPrice() << endl;IFactory* factory1 = new HuaweiFactory();MyPhone* phone1 = factory1->createPhone();cout << "Price: " << phone1->getPrice() << endl;delete phone;delete phone1;delete factory;delete factory1;
3.抽象工厂模式
这时,如果用户需要增加产品类型,比如华为的平板、苹果的平板怎么办?这就要引入抽象工厂模式了。
- 将产品类进行抽象(这里可能会涉及多种产品);
- 写出具体的产品类;
- 将工厂类进行抽象;
- 具体工厂生产对应的产品(一个工厂可以生产自己品牌的多种产品)
在原来代码基础上进行修改:
产品抽象类:
#pragma once
class MyPhone
{
public:MyPhone(double price);~MyPhone();double getPrice() { return m_price; }
private:double m_price;
};class MyPad
{
public:MyPad(double price);~MyPad();double getPrice() { return m_price; }
private:double m_price;
};
苹果产品类:
#pragma once
#include "MyPhone.h"
class Phone_Apple:public MyPhone
{
public:Phone_Apple(double price);~Phone_Apple();
};
class Pad_Apple :public MyPad
{
public:Pad_Apple(double price);~Pad_Apple();
};
华为产品类:
#pragma once
#include "MyPhone.h"class Phone_Huawei :public MyPhone
{
public:Phone_Huawei(double price);~Phone_Huawei();
};
class Pad_Huawei :public MyPad
{
public:Pad_Huawei(double price);~Pad_Huawei();
};
抽象工厂类:
#pragma once
#include "MyPhone.h"
class IFactory
{
public:IFactory();~IFactory();virtual MyPhone* createPhone() = 0;virtual MyPad* createPad() = 0;
}
具体工厂类:
#pragma once
#include "IFactory.h"
#include "MyPhone.h"
#include "Phone_Apple.h"
class IphoneFactory : public IFactory
{
public:IphoneFactory();~IphoneFactory();virtual MyPhone* createPhone(){MyPhone* phone = new Phone_Apple(6000);return phone;}virtual MyPad* createPad() {MyPad* phone = new Pad_Apple(4900);return phone;}
};
#pragma once
#include "IFactory.h"
#include "MyPhone.h"
#include "Phone_Huawei.h"
class HuaweiFactory : public IFactory
{
public:HuaweiFactory();~HuaweiFactory();virtual MyPhone* createPhone() {MyPhone* phone = new Phone_Huawei(5600);return phone;}virtual MyPad* createPad() {MyPad* phone = new Pad_Huawei(4600);return phone;}
};
调用:
//3.3 抽象工厂模式IFactory* factory = new HuaweiFactory();MyPad* pad = factory->createPad();cout << "Price: " << pad->getPrice() << endl;MyPhone* phone = factory->createPhone();cout << "Price: " << phone->getPrice() << endl;delete pad;delete phone;delete factory;
4.总结对比
三种方法使用选择:
分别理解三种模式并不困难,关键就在于根据实际问题实际情况选用不同的模式,先总结如下思考方式,供参考:
- 当产品类型固定时可选择简单工厂模式(这里可以认为是零维的,即不用考虑其他维度);
- 当产品涉及到不同厂商时,将工厂抽象化,不同厂商实现大厂的接口(这里可以认为是一维的,除了产品自身还需考虑厂商问题);
- 当涉及到不同厂商的不同产品时,将工厂和产品分别抽象化(这里可以认为是二维的,考虑产品的种类以及厂商问题);
总之要尽量满足OOP七大原则:
- 开闭原则: 一个软件的实体应当对扩展开放,对修改关闭
- 依赖倒转原则: 要针对接口编程,不要针对实现编程
- 迪米特法则:只与你直接的朋友通信,而避免和陌生人通信
三种方式的对比:
简单(静态)工厂模式:
用来生产同一等级结构中的任意产品(对于增加新的产品,需要修改已有代码;
工厂方法模式:
用来生产同一等级结构中的固定产品(支持增加任意产品)
优点:
- 你可以避免创建者和具体产品之间的紧密耦合。
- 单一职责原则。你可以将产品创建代码放在程序的单一位置,从而使得代码更容易维护。
- 开闭原则。无需更改现有客户端代码,你就可以在程序中引入新的产品类型。
缺点:
- 应用工厂方法模式需要引入许多新的子类,代码可能会因此变得更复杂。最好的情况是将该模式引入创建者类的现有层次结构中。
抽象工厂模式:
围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。
优点:
- 应用工厂方法模式需要引入许多新的子类,代码可能会因此变得更复杂。最好的情况是将该模式引入创建者类的现有层次结构中。
缺点:
- 由于采用该模式需要向应用中引入众多接口和类,代码可能会比之前更加复杂。
参考文献:【精选】设计模式之工厂模式(简单工厂、工厂方法、抽象工厂)_简单工厂模式 抽象工厂模式-CSDN博客1. 工厂方法模式(Factory Method) (yuque.com)2. 抽象工厂模式(Abstract Factory) (yuque.com)