【设计模式】工厂模式(Factory)
目录
一、问题导入
二、简单工厂(Simple Factory)
1.概念
2.代码实现
3.优劣
4.构成
三、工厂方法(Factory Method)
1.概念
2.代码实现
3.优劣
4.构成
5.使用场景(课件上面有,所以加上了)
四、抽象工厂(Abstract Factory)
1.概念导入
2.代码实现
3.优劣
4.构成
5.使用场景
一、问题导入
“拿来!” 当我们需要某样东西时,只要清晰说出要求,就能直接拿到对应的结果,而不用关心它是怎么被制作、从哪里来的。
就像去水果摊买水果,你只需要跟老板说 “要一个苹果”,他就会递给你一个苹果。你不需要知道这个苹果产自哪个果园、经历了哪些运输环节,甚至不用知道老板是从哪个箱子里拿的。
这种逻辑的基础实现,就是我们先从‘简单工厂’开始聊起的内容,它是更灵活的‘工厂方法’的基础。
二、简单工厂(Simple Factory)
1.概念
简单工厂(Simple Factory)是通过一个统一的工厂类,根据传入的参数(如产品类型)来创建对应的产品实例,无论苹果还是香蕉,都由这个工厂统一负责生产。
2.代码实现
如下代码所示:在该代码当中,我们设计了一个抽象产品类Fruit,然后设计了两个具体产品类Apple和Banana,最后在工厂类Factory当中提供根据传入水果的枚举获得对应Fruit的方法。
#pragma once
#include<iostream>namespace _SimpleFactory
{//产品枚举类enum class FruitType{Apple,Banana};//抽象产品class Fruit{public:Fruit() = default;~Fruit() = default;//抽象方法,定义所有水果的通用行为(如被使用时的操作)virtual void on_use() = 0;};//具体产品class Apple:public Fruit{public:Apple() = default;~Apple() = default;void on_use() override{std::cout << "创建了苹果" << std::endl;}};class Banana:public Fruit{public:Banana() = default;~Banana() = default;void on_use() override{std::cout << "创建了香蕉" << std::endl;}};//具体工厂class Factory{public:Factory() = default;~Factory() = default;
//根据传入的水果类型枚举,决定创建苹果还是香蕉实例,返回抽象产品指针(屏蔽具体类型)Fruit* create_fruit(FruitType type){switch (type){case FruitType::Apple:return new Apple();case FruitType::Banana:return new Banana();default:return nullptr;}}};}
main函数中使用:
#include<iostream>
#include"simple_factory.h"int main()
{//简单工厂._SimpleFactory::Factory *factory=new _SimpleFactory::Factory();_SimpleFactory::Fruit*apple=factory->create_fruit(_SimpleFactory::FruitType::Apple);_SimpleFactory::Fruit*banana=factory->create_fruit(_SimpleFactory::FruitType::Banana);apple->on_use();banana->on_use();return 0;
}
上述代码已经完成了对创建的内部细节的屏蔽,但是考虑我们想要再去添加一个梨子的具体产品,我们就需要去新添枚举类的内容,并修改工厂方法中的switch语句,这违背了开闭原则(OCP)
3.优劣
优势:
(1)进行了职责的划分
(将产品的创建职责集中到工厂类,客户端只需关注‘要什么’,无需关注‘怎么创建’,实现了创建与使用的分离)
(2)通过简单工厂模式可以减轻用户的记忆负担
劣势:
(1)系统难以扩展,与开闭原则(OCP)冲突
(开闭原则要求‘对扩展开放、对修改关闭’,而简单工厂新增产品时必须修改工厂类的判断逻辑(如 switch 分支),因此违反了这一原则)
(2)工厂逻辑可能过于复杂
4.构成
理解一个设计模式,就要清楚地知道该设计模式的构成(----WaWaJie)
简单工厂的类成分构成我们可以很清楚地看到:
(1)抽象产品:定义所有具体产品的公共接口(如on_use()
),是工厂返回的统一类型
(2)具体产品:实现抽象产品接口,是工厂实际创建的对象;
(3)具体工厂:提供静态或实例方法(如create_fruit
),根据参数判断并创建对应具体产品的实例。
三、工厂方法(Factory Method)
1.概念
工厂方法(Factory Method)将去解决简单工厂违背开闭原则(OCP)的问题,它通过抽象出一个工厂接口(抽象工厂),并为每一种具体产品配套一个对应的具体工厂类(如苹果对应苹果工厂、香蕉对应香蕉工厂),让具体工厂负责创建对应产品的实例。并定义一个用于创建对象的接口,让子类决定要实例化哪一个工厂类,并且工厂模式将其创建过程延迟到子类中进行。
2.代码实现
相较于简单工厂,工厂方法多设计了一层抽象的工厂类,并为每一个产品创建了对应的工厂,从而遵守了开闭原则(在进行扩展的时候,只需要创建一个新的产品类与产品工厂,而不需要修改已有的产品或工厂)。此外,我们将使用者单独抽出来,使用者User
通过依赖抽象工厂(Factory*
)而非具体工厂,进一步降低了与具体产品的耦合 —— 使用者无需知道自己使用的是苹果工厂还是香蕉工厂,只需调用工厂的create_fruit
方法即可,符合‘依赖倒置原则’(依赖抽象而非具体)
#pragma once
#include<iostream>namespace _FactoryMethod
{ //抽象产品class Fruit{public:Fruit() = default;~Fruit() = default;virtual void on_use() = 0;};//具体产品class Apple :public Fruit{public:Apple() = default;~Apple() = default;void on_use() override{std::cout << "创建了苹果" << std::endl;}};class Banana :public Fruit{public:Banana() = default;~Banana() = default;void on_use() override{std::cout << "创建了香蕉" << std::endl;}};//抽象工厂class Factory{public:Factory() = default;~Factory() = default;virtual Fruit* create_fruit() = 0;};//具体工厂class AppleFactory :public Factory{public:AppleFactory() = default;~AppleFactory() = default;Fruit* create_fruit() override{return new Apple();}};class BananaFactory :public Factory{public:BananaFactory() = default;~BananaFactory() = default;Fruit* create_fruit() override{return new Banana();}};//使用者class User{public:User(Factory* factory) { this->factory = factory; }~User() = default;void use(){Fruit* fruit = factory->create_fruit();fruit->on_use();}private:Factory* factory;};
}
main函数中的使用:
#include<iostream>
#include"factory_method.h"int main()
{//工厂方法_FactoryMethod::Factory*apple_factory=new _FactoryMethod::AppleFactory();_FactoryMethod::Factory*banana_factory=new _FactoryMethod::BananaFactory();_FactoryMethod::User*user=new _FactoryMethod::User(apple_factory);user->use();user=new _FactoryMethod::User(banana_factory);user->use();return 0;
}
3.优劣
优势:
(1)对客户端隐藏具体的类与内部细节
(“客户端仅需与抽象产品(Fruit
)和抽象工厂(Factory
)交互,无需知道具体产品(Apple
)和具体工厂(AppleFactory
)的存在,降低了耦合”)
(2)扩展性好
(结合开闭原则具体说明 “新增产品时,只需新增具体产品类和对应具体工厂类(如Pear
和PearFactory
),无需修改已有代码,严格遵守开闭原则”)
(3)遵循命名规范,有助于其他开发者识别代码结构
劣势:
需要更多的类,增加了代码的理解难度。
(当产品种类较多时,类的数量会成倍增加(产品数 = 具体产品类数 = 具体工厂类数),可能导致系统复杂度上升)
4.构成
工厂方法的类成分构成我们可以很清楚地看到:
(1)抽象产品:定义所有具体产品的公共接口(如on_use()
),是工厂返回的统一类型
(2)具体产品:实现抽象产品接口,是工厂实际创建的对象;
(3)抽象工厂:定义创建产品的接口(如纯虚函数create_fruit
),是所有具体工厂的基类,规定具体工厂必须实现的创建逻辑;
(4)具体工厂:继承自抽象工厂,实现create_fruit
方法,负责创建对应具体产品(如AppleFactory
创建Apple
)的实例
5.使用场景(课件上面有,所以加上了)
(1)创建一个对象,且要在不重复代码的前提下实现其复用。(工厂方法将对象创建逻辑封装在具体工厂中,多个客户端可通过复用工厂类重复创建同一类对象,避免创建代码在各处重复编写)
(2)创建一个对象需要访问不应包含在组合类中的信息或资源。(若对象创建依赖数据库连接、配置文件等敏感资源,可将这些资源的访问逻辑封装在工厂类中,组合类(如客户端)只需调用工厂,无需直接接触敏感信息,符合 “信息隐藏” 原则)
(3)对生成对象的生命周期管理必须集中化,以确保应用程序内行为的一致性(工厂方法可统一控制对象的创建时机、初始化参数甚至销毁逻辑,避免不同客户端创建同一类对象时因参数或流程不同导致的行为不一致)
四、抽象工厂(Abstract Factory)
1.概念导入
在游戏中,战士需要装备剑🗡和盾🛡,法师需要装备法杖🪄和魔法书📕,这是固定的。
那么我们就可以提供一个战士工厂与一个法师工厂,战士🤺工厂生成战士的一套装备(也就是剑🗡和盾🛡),法师🧙工厂生成法师的一套装备(也就是法杖🪄和魔法书📕)
那么,可以看到,一个工厂生成了一系列的产品,而抽象工厂(Abstract Factory)便是要完成这样一项工作。相较于只生成单一对应产品的工厂方法,抽象工厂有着更高的维度,他将生成一系列关联产品,从而减少了用户所需考虑的工厂数量。
2.代码实现
抽象工厂在代码的实现层面相较于工厂方法将会提供一个整体工厂(合并了组件工厂),其余的部分无差异。
(如果用工厂方法实现,需要为剑、盾、法杖、魔法书分别创建 4 个工厂,而抽象工厂只需 2 个工厂(战士 + 法师)即可管理整套装备,减少了工厂数量)
#pragma once
#include<iostream>namespace _AbstractFactory
{//抽象产品(武器和防具)class Weapon{public:Weapon() = default;~Weapon() = default;virtual void on_equip() = 0;};class Armor{public:Armor() = default;~Armor() = default;virtual void on_equip() = 0;};//具体产品(剑和盾,法杖与魔法书)class Sword :public Weapon{public:Sword() = default;~Sword() = default;void on_equip() override{std::cout << "装备了剑🗡" << std::endl;}};class Shield :public Armor{public:Shield() = default;~Shield() = default;void on_equip() override{std::cout << "装备了盾🛡" << std::endl;}};//法杖和魔法书class WizardStaff :public Weapon{public:WizardStaff() = default;~WizardStaff() = default;void on_equip() override{std::cout << "装备了法杖🪄" << std::endl;}};class WizardBook :public Armor{public:WizardBook() = default;~WizardBook() = default;void on_equip() override{std::cout << "装备了魔法书 📕" << std::endl;}};//抽象工厂class AbstractFactory{public:AbstractFactory() = default;~AbstractFactory() = default;virtual Weapon* create_weapon() = 0;virtual Armor* create_armor() = 0;};//具体工厂(战士和法师🧙)class WarriorFactory :public AbstractFactory{public:WarriorFactory() = default;~WarriorFactory() = default;Weapon* create_weapon() override{return new Sword();}Armor* create_armor() override{return new Shield();}};class WizardFactory :public AbstractFactory{public:WizardFactory() = default;~WizardFactory() = default;Weapon* create_weapon() override{return new WizardStaff();}Armor* create_armor() override{return new WizardBook();}};//角色class Character{public:Character(AbstractFactory* factory){this->weapon = factory->create_weapon();this->armor = factory->create_armor();}~Character(){delete weapon;delete armor;}void on_equip(){weapon->on_equip();armor->on_equip();}private:Weapon* weapon;Armor* armor;};void test(){//创建战士AbstractFactory* factory = new WarriorFactory();Character* character = new Character(factory);character->on_equip();//创建法师factory = new WizardFactory();character = new Character(factory);character->on_equip();}
}
3.优劣
优势:
(1)易于扩展到一个系列
(新增产品族(如刺客的匕首 + 皮甲)时,只需新增具体工厂(AssassinFactory
),无需修改现有代码,符合开闭原则)
(2)创建相关或相互依赖的对象族,类似于工具包(Kit)
(确保战士不会拿到法师的法杖(产品族内一致性))
(3)提供产品的类库,暴露接口而非实现
劣势:
(1)需要更多的类
(2)无法在两个维度上进行扩展
(当需要新增产品族(如 “刺客装备”)时较容易扩展(新增具体工厂),但新增产品种类(如在 “武器”“防具” 外新增 “饰品”)时,需修改抽象工厂接口及所有具体工厂,违背开闭原则,以此明确)
4.构成
抽象工厂的类成分构成我们可以很清楚地看到:(其实跟工厂方法没太大差别)
(1)抽象产品
(2)具体产品
(3)抽象工厂
(4)具体工厂
5.使用场景
(1)一个系统应当独立于其产品
(2)一个类无法预先知道他必须创建的对象的类
(3)一个系统必须只是用一组产品族中的一个
(4)一组相关的产品对象是为一起使用而设计的,并且需要强制执行该约束