设计模式(3)——工厂模式
文章目录
- 三、day3
- 1. 简单工厂模式
- 2. 工厂模式
- 3. 抽象工厂模式
三、day3
今天学习最常见的设计模式之一——工厂模式。工厂模式提供了一种创建对象的最佳方式,在创建对象的时候,不会对客户端暴露创建逻辑,并且通过使用一个共同的接口来创建新的对象。
工厂模式有3种不同的实现方式:
- 简单工厂模式:又叫做静态工厂方法模式。该模式是通过传⼊相关的类型来返回相应的类,这种方式比较单一,可扩展性相对较差。简单工厂模式看为工厂方法模式的一种特例,两者归为一类。
- 工厂方法模式:一个抽象产品类,可以派生出多个具体产品类。 一个抽象工厂类,可以派生出多个具体工厂类。每个具体工厂类只能创建一个具体产品类的实例。
- 抽象工厂模式:多个抽象产品类,每个抽象产品类可以派生出多个具体产品类。一个抽象工厂类,可以派生出多个具体工厂类。 每个具体工厂类可以创建多个具体产品类的实例。
1. 简单工厂模式
使用简单工厂的时候,通常不用创建简单工厂类的类实例,没有创建实例的必要。因此可以把简单工厂类实现成一个工具类,直接使用静态方法就可以了。也就是说简单工厂方法通常是静态的,所以也被称为静态工厂。如果要防止客户端无谓的创造简单工厂实例,还可以把简单工厂的构造方法私有化,并添加一个公共成员函数来创建我们需要的对象。
- 优点
- 实现简单,可扩展。
- 实现了对象创建与业务逻辑的分离。
- 客户端无需知道所创建的具体产品类和类名。
- 缺点
- 实例化对象的逻辑全部封装在一个工厂类里,每次需求变化都要单独修改工厂类(违反了开闭原则),而且出了异常可能没法正常工作。
- 不方便扩展子类。
假如我们想要生产如下产品:
// 人造恶魔果实· 绵羊形态
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()
两种功能,我们可以将这两个函数抽象化成一个基类 AbstractSmile
(AbstractSmile
类不仅要将两个功能函数 virtual
化,析构函数也需要加 virtual
),我们只需在三个产品类中继承 AbstractSmile
,并对两个功能函数 transform()
和使 ability()
进行重写。
在工厂类的工厂函数中,我们通过 switch-case
返回基类指针即可,基类指针指向我们想要指向的产品类,当我们通过基类指针调用派生类功能函数时,编译器通过动态联编和虚函数表协助我们判断调用哪一个函数。
简单工厂模式的 UML 图如下:
流程很简单,因为每个产品都有形态变化 transform()
和使用能力 ability()
两种功能,我们可以将这两个函数抽象化成一个基类 AbstractSmile
(AbstractSmile
类不仅要将两个功能函数 virtual
化,析构函数也需要加 virtual
),我们只需在三个产品类中继承 AbstractSmile
,并对两个功能函数 transform()
和使 ability()
进行重写。这便生成了三个产品类 SheepSmile
、LionSmile
、BatSmile
,可以将其看作产品的模具。
当我们想要生产特定产品,比如 sheep
时,我们将枚举变量 sheep
输入至工厂 SmileFactory
中,工厂 SmileFactory
会根据我们给的产品名称(枚举变量 sheep
),使用对应的模具生产产品并返回。
简单工厂模式的缺点:
尽管简单工厂模式能生成我们需要的产品,但是如果我们想要生成更多类型不同的产品,超出了枚举变量的范围,那么就需要在工厂函数中添加更多的case
,很明显这违背了封闭原则,也就意味着需要基于开放原则来解决这个问题。
2. 工厂模式
工厂模式很好的解决了简单工厂模式的缺点,简单工厂模式是只有一个工厂类,而工厂模式是有很多的工厂类:
- 一个基类,包含一个虚工厂函数,用于实现多态。
- 多个子类,重写父类的工厂函数。每个子工厂类负责生产一种恶魔果实,这相当于再次解耦,将工厂类的职责再次拆分、细化,如果要生产新品种的恶魔果实,那么只需要添加对应的工厂类,无需修改原有的代码。
工厂模式包含以下几个角色:
- 抽象产品(Product):定义了产品的接口,是所有具体产品类的共同父类。
- 具体产品(Concrete Product):实现了抽象产品接口的具体产品类,是工厂方法模式所创建的对象。
- 抽象工厂(Factory):定义了工厂方法的接口,负责创建抽象产品的对象。
- 具体工厂(Concrete Factory):实现了抽象工厂接口,具体工厂类根据具体业务逻辑,创建具体产品的对象。
工厂方法模式的工作流程如下:
- 客户端通过调用具体工厂类的工厂方法来创建产品对象。
- 具体工厂类根据客户端的请求,调用具体产品类的构造方法创建具体产品对象。
- 具体工厂类将创建的具体产品对象返回给客户端。
通过工厂方法模式,客户端与具体产品类解耦,客户端只需关心抽象产品和抽象工厂,具体产品的创建由具体工厂类来完成。这样,当需要引入新的产品时,只需创建一个新的具体产品类和对应的具体工厂类,而不需要修改客户端代码。
工厂方法模式适用于以下情况:
- 当一个类无法预知它需要创建的对象的具体类时,可以使用工厂方法模式。
- 当一个类希望将对象的创建责任委托给多个子类中的某一个时,可以使用工厂方法模式。
- 当一个类需要通过其子类来指定创建对象时,可以使用工厂方法模式。
总结来说,工厂方法模式通过定义一个创建对象的接口,将对象的实例化延迟到子类中去完成,实现了对象的创建与使用的解耦,提高了系统的可扩展性和灵活性。
工厂模式的代码如下:
#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类图如下所示:
上半部分和简单工厂模式一样,因为每个产品都有形态变化 transform()
和使用能力 ability()
两种功能,我们可以将这两个函数抽象化成一个基类 AbstractSmile
(AbstractSmile
类不仅要将两个功能函数 virtual
化,析构函数也需要加 virtual
),我们只需在三个产品类中继承 AbstractSmile
,并对两个功能函数 transform()
和使 ability()
进行重写。这便生成了三个产品类 SheepSmile
、LionSmile
、BatSmile
,可以将其看作产品的模具。
但不同于简单工厂模式,我们这里将工厂类抽象化为了一个接口类 AbstractFactory
,其包含两个虚函数,一个虚函数是 createSmile()
,用于返回 AbstractSmile
的指针,指针指向生成的产品(多态);另一个函数是析构函数。
当我们需要生成一个新产品时,我们只需要继承这个抽象工厂类 AbstractFactory
,然后添加专属于其自身的属性,最后调用重写的 createSmile()
函数,返回一个指向新产品的 AbstractSmile
指针即可,当我们通过 AbstractSmile
指针调用产品的方法时,多态确保我们能够准确指向这个产品的方法。
在我们创建工厂以及产品对象时,必须使用两个抽象类作为指针指向特定产品的工厂和产品模具,比如
AbstractSmile
和AbstractFactory
。
- 优点
符合开放-关闭原则(简称开-闭原则)将创建对象的逻辑与任务交给了工厂类。
- 缺点
每次新增产品,产品类都需要创建对应工厂类,增加了系统的开销。
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 - 博客园