《C++设计模式》工厂模式
文章目录
- 1、简介
- 2、工厂模式的种类
- 2.1 简单工厂模式(Simple Factory Pattern):
- 2.2 工厂方法模式(Factory Method Pattern):
- 2.3 抽象工厂模式(Abstract Factory Pattern):
- 3、工厂模式的具体介绍
- 3.1 简单工厂模式
- 3.1.1 代码示例
- 3.1.2 组成部分
- 3.1.3 优缺点
- 3.1.4 应用场景
- 3.2 工厂方法模式
- 3.2.1 代码示例
- 3.2.2 组成部分
- 3.2.3优缺点
- 3.2.4应用场景
- 3.3 抽象工厂模式
- 3.3.1 代码示例
- 3.3.2 组成部分
- 3.3.3优缺点
- 3.3.4 应用场景
- 4、面试常问问题
- 4.1、工厂模式基本概念
- 4.2、工厂模式的应用场景
- 4.3、工厂模式的实现方式
- 4.4、工厂模式与其他设计模式的对比
- 4.5、工厂模式的优缺点
- 5、总结
- 6、参考文章
1、简介
工厂模式(Factory Pattern)是创建型设计模式的一种,它提供了一种创建对象的最佳方式。在工厂模式中,一个工厂类负责创建具有共同接口或基类的对象,但无需明确指定将要创建的对象的具体类。这种模式通过将对象的实例化推迟到子类来实现,从而使得代码更加灵活和可扩展。
2、工厂模式的种类
2.1 简单工厂模式(Simple Factory Pattern):
也称为静态工厂方法模式,它通过一个工厂类来创建对象,但这个工厂类通常是一个静态方法。
客户端直接调用静态方法来获取所需的对象,而无需知道具体类的实现。
2.2 工厂方法模式(Factory Method Pattern):
定义一个用于创建对象的接口,但让子类决定实例化哪一个类。
工厂方法使得一个类的实例化延迟到其子类。
在这个模式中,父类提供一个创建对象的接口(通常是抽象方法),而子类则实现这个接口来创建具体的对象。
2.3 抽象工厂模式(Abstract Factory Pattern):
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
抽象工厂模式通常包含多个工厂方法,每个方法对应一个产品类。
这个模式使得客户端能够使用抽象接口来创建一系列相关的对象,而无需指定它们的具体实现类。
3、工厂模式的具体介绍
3.1 简单工厂模式
3.1.1 代码示例
#include <iostream>
#include <memory> // 包含智能指针的头文件
using namespace std;
// 鞋子抽象类
class Shoes
{
public:
virtual ~Shoes() {}
virtual void Show() = 0;
};
// 耐克鞋子
class NiKeShoes : public Shoes
{
public:
void Show() override
{
std::cout << "Nike:Just do it" << std::endl;
}
};
// 阿迪达斯鞋子
class AdidasShoes : public Shoes
{
public:
void Show() override
{
std::cout << "Adidas: Impossible is nothing" << std::endl;
}
};
enum SHOES_TYPE
{
NIKE,
ADIDAS
};
// 总鞋厂
class ShoesFactory
{
public:
// 根据鞋子类型创建对应的鞋子对象,并返回智能指针
std::unique_ptr<Shoes> CreateShoes(SHOES_TYPE type)
{
switch (type)
{
case NIKE:
return std::make_unique<NiKeShoes>();
case ADIDAS:
return std::make_unique<AdidasShoes>();
default:
return nullptr; // 注意这里返回nullptr而不是NULL,这是C++11及以后版本的推荐做法
}
}
};
int main()
{
ShoesFactory shoesFactory;
// 从鞋工厂对象创建耐克鞋对象
auto pNikeShoes = shoesFactory.CreateShoes(NIKE);
if (pNikeShoes)
{
// 耐克球鞋广告喊起
pNikeShoes->Show();
}
// 从鞋工厂对象创建阿迪达斯鞋对象
auto pAdidasShoes = shoesFactory.CreateShoes(ADIDAS);
if (pAdidasShoes)
{
// 阿里达斯球鞋广告喊起
pAdidasShoes->Show();
}
return 0;
}
3.1.2 组成部分
工厂类(ShoesFactory):工厂模式的核心类,会定义一个用于创建指定的具体实例对象的接口。
抽象产品类(Shoes):是具体产品类的继承的父类或实现的接口。
具体产品类(NiKeShoes\AdidasShoes\LiNingShoes):工厂类所创建的对象就是此具体产品实例。
3.1.3 优缺点
优点:
(1)实现了对象创建和使用的分离:客户端无需了解对象的具体创建过程,只需要关心如何使用对象。
(2)代码结构简单:容易理解和实现。
缺点:
(1)不符合开闭原则:当需要增加新的产品对象时,需要修改工厂类的代码,导致系统的可扩展性差。
(2)大量使用if-else语句:在简单工厂模式中,通常需要根据输入参数的不同来创建不同的对象,这会导致工厂类中出现大量的if-else语句,使代码变得复杂且难以维护。
3.1.4 应用场景
(1)对象创建过程简单且数量较少的场景:
当系统中需要创建的对象种类较少,且创建过程相对简单时,可以使用简单工厂模式。通过工厂类提供的方法,客户端可以轻松地获取所需的对象实例,而无需关心对象的创建细节。
(2)客户端不需要知道对象创建过程的场景:
在某些情况下,客户端只需要使用对象,而不需要了解对象的创建过程。此时,可以使用简单工厂模式将对象的创建逻辑封装在工厂类中,使客户端代码更加简洁和清晰。
(3)需要对对象创建过程进行集中管理的场景:
如果系统中需要对对象的创建过程进行集中管理和控制,例如需要对创建过程进行优化或调整时,可以使用简单工厂模式。通过修改工厂类的代码,可以轻松地实现对对象创建过程的调整和优化,而无需修改客户端代码。
3.2 工厂方法模式
3.2.1 代码示例
#include <iostream>
#include <memory> // 包含智能指针的头文件
using namespace std;
// 鞋子抽象类
class Shoes
{
public:
virtual ~Shoes() {}
virtual void Show() = 0;
};
// 耐克鞋子
class NiKeShoes : public Shoes
{
public:
void Show() override
{
std::cout << "Niki:Just do it" << std::endl;
}
};
// 阿迪达斯鞋子
class AdidasShoes : public Shoes
{
public:
void Show() override
{
std::cout << "Adidas:Impossible is nothing" << std::endl;
}
};
// 总鞋厂(抽象工厂)
class ShoesFactory
{
public:
virtual std::unique_ptr<Shoes> CreateShoes() = 0;
virtual ~ShoesFactory() = default; // 使用默认析构函数
};
// 耐克生产者/生产链
class NiKeProducer : public ShoesFactory
{
public:
std::unique_ptr<Shoes> CreateShoes() override
{
return std::make_unique<NiKeShoes>();
}
};
// 阿迪达斯生产者/生产链
class AdidasProducer : public ShoesFactory
{
public:
std::unique_ptr<Shoes> CreateShoes() override
{
return std::make_unique<AdidasShoes>();
}
};
int main()
{
// ================ 生产耐克流程 ==================== //
// 鞋厂开设耐克生产线(使用智能指针管理)
std::unique_ptr<ShoesFactory> niKeProducer = std::make_unique<NiKeProducer>();
// 耐克生产线产出球鞋(智能指针自动管理内存)
auto nikeShoes = niKeProducer->CreateShoes();
// 耐克球鞋广告喊起
nikeShoes->Show();
// 无需手动释放nikeShoes和niKeProducer,它们会在作用域结束时自动释放
// ================ 生产阿迪达斯流程 ==================== //
// 鞋厂开设阿迪达斯生产线(使用智能指针管理)
std::unique_ptr<ShoesFactory> adidasProducer = std::make_unique<AdidasProducer>();
// 阿迪达斯生产线产出球鞋(智能指针自动管理内存)
auto adidasShoes = adidasProducer->CreateShoes();
// 阿迪达斯球鞋广告喊起
adidasShoes->Show();
// 同样,无需手动释放adidasShoes和adidasProducer
return 0;
}
3.2.2 组成部分
抽象工厂类厂(ShoesFactory):工厂方法模式的核心类,提供创建具体产品的接口,由具体工厂类实现。
具体工厂类(NiKeProducer\AdidasProducer\LiNingProducer):继承于抽象工厂,实现创建对应具体产品对象的方式。
抽象产品类(Shoes):它是具体产品继承的父类(基类)。
具体产品类(NiKeShoes\AdidasShoes\LiNingShoes):具体工厂所创建的对象,就是此类
3.2.3优缺点
优点:
(1)符合开闭原则:当需要增加新的产品对象时,只需要增加一个具体的工厂子类,无需修改原有代码,具有较好的可扩展性。
(2)降低耦合度:通过工厂方法模式,将对象的创建和使用分离,降低了客户端代码与具体产品类之间的耦合度。
缺点:
(1)工厂子类过多:当产品种类非常多时,会产生大量的工厂子类,导致代码结构复杂,增加系统的维护成本。
(2)增加了系统的复杂性:虽然工厂方法模式可以很好地解决简单工厂模式中的扩展性问题,但同时也增加了系统的复杂性,因为需要引入多个工厂子类来创建不同的产品对象。
3.2.4应用场景
(1)创建对象的任务由多个具体子工厂完成的场景:
当系统中存在多个具体子工厂,每个子工厂负责创建不同类型的对象时,可以使用工厂方法模式。通过定义抽象工厂接口和具体工厂类,可以灵活地选择所需的工厂来创建对象。
(2)客户端只需要知道产品接口而不需要知道具体产品实现的场景:
在某些情况下,客户端只需要知道产品的接口,而不需要知道具体的产品实现。此时,可以使用工厂方法模式将产品的创建逻辑封装在工厂类中,使客户端代码更加通用和可维护。
(3)需要扩展新产品种类且不希望修改客户端代码的场景:
如果系统中需要扩展新的产品种类,且不希望修改客户端代码,可以使用工厂方法模式。通过增加新的具体工厂类来实现新产品的创建,而无需修改原有的客户端代码。
3.3 抽象工厂模式
3.3.1 代码示例
#include <iostream>
#include <memory> // 包含智能指针的头文件
using namespace std;
// 基类 衣服
class Clothe
{
public:
virtual void Show() = 0;
virtual ~Clothe() {}
};
// 基类 鞋子
class Shoes
{
public:
virtual void Show() = 0;
virtual ~Shoes() {}
};
// 耐克衣服
class NiKeClothe : public Clothe
{
public:
void Show() override
{
cout << "我是耐克衣服,come on!" << endl;
}
};
// 耐克鞋子
class NiKeShoes : public Shoes
{
public:
void Show() override
{
cout << "我是耐克球鞋,come on!" << endl;
}
};
// 总厂
class Factory
{
public:
virtual std::unique_ptr<Shoes> CreateShoes() = 0;
virtual std::unique_ptr<Clothe> CreateClothe() = 0;
virtual ~Factory() {}
};
// 耐克生产者/生产链
class NiKeProducer : public Factory
{
public:
std::unique_ptr<Shoes> CreateShoes() override
{
return std::make_unique<NiKeShoes>();
}
std::unique_ptr<Clothe> CreateClothe() override
{
return std::make_unique<NiKeClothe>();
}
};
int main()
{
// ================ 生产耐克流程 ==================== //
// 鞋厂开设耐克生产线
std::unique_ptr<Factory> niKeProducer = std::make_unique<NiKeProducer>();
// 耐克生产线产出球鞋
auto nikeShoes = niKeProducer->CreateShoes();
// 耐克生产线产出衣服
auto nikeClothe = niKeProducer->CreateClothe();
// 耐克球鞋广告喊起
nikeShoes->Show();
// 耐克衣服广告喊起
nikeClothe->Show();
// 不需要手动释放资源,std::unique_ptr会自动管理
return 0;
}
3.3.2 组成部分
抽象工厂类厂(ShoesFactory):工厂方法模式的核心类,提供创建具体产品的接口,由具体工厂类实现。
具体工厂类(NiKeProducer):继承于抽象工厂,实现创建对应具体产品对象的方式。
抽象产品类(Shoes\Clothe):它是具体产品继承的父类(基类)。
具体产品类(NiKeShoes\NiKeClothe):具体工厂所创建的对象,就是此类。
3.3.3优缺点
优点:
(1)提供了更加灵活的代码结构:通过抽象工厂模式,可以创建一系列相关的产品对象,而不仅仅是单一的产品对象。这使得代码结构更加灵活,可以适应更复杂的业务需求。
(2)降低了客户端代码的修改成本:当需要增加新的产品对象系列时,只需要增加一个具体的工厂子类,无需修改原有代码,降低了客户端代码的修改成本。
(3)增强了系统的可扩展性:抽象工厂模式符合开闭原则,可以很好地应对未来可能出现的新需求。
缺点:
(1)实现复杂:抽象工厂模式的实现相对复杂,需要设计多个抽象类和具体类,增加了系统的复杂性。
(2)不适合简单的应用场景:对于简单的应用场景,使用抽象工厂模式可能会增加不必要的复杂性,导致代码可读性下降。
(3)对开闭原则的支持有局限性:虽然抽象工厂模式符合开闭原则,但当需要增加新的产品对象种类时,仍然需要修改抽象工厂和所有具体工厂的代码,这在一定程度上限制了其灵活性。
3.3.4 应用场景
(1)系统需要独立于具体产品创建、组合和表示的场景:
当系统需要独立于具体产品的创建、组合和表示时,可以使用抽象工厂模式。通过定义抽象工厂接口和具体工厂类,可以将产品的创建过程与系统的其他部分分离,从而实现系统的独立性和可扩展性。
(2)系统需要配置多个产品系列之一的场景:
如果系统中存在多个产品系列,且需要根据不同的需求选择其中一个产品系列进行配置时,可以使用抽象工厂模式。通过定义抽象工厂接口和具体工厂类,可以灵活地选择所需的产品系列来配置系统。
(3)需要强调一系列相关产品对象的设计以便进行联合使用的场景:
当系统中存在一系列相关的产品对象,并且这些对象需要联合使用时,可以使用抽象工厂模式。通过定义抽象工厂接口和具体工厂类,可以确保这些产品对象在设计和实现上保持一致性和协调性,从而方便它们的联合使用。
(4)提供一个产品类库而只想显示它们的接口而不是实现的场景:
如果系统提供了一个产品类库,并且只想向外部展示产品的接口而不是具体的实现细节时,可以使用抽象工厂模式。通过定义抽象工厂接口和具体工厂类,可以将产品的具体实现隐藏起来,只向外部提供产品的接口供其使用。
4、面试常问问题
4.1、工厂模式基本概念
(1)什么是工厂模式?
工厂模式是一种创建型设计模式,它提供了一种创建对象的最佳方式。在工厂模式中,一个工厂类负责创建具有共同接口或基类的对象,而无需指定它们的具体类。
(2)工厂模式有几种类型?
工厂模式通常包括简单工厂模式、工厂方法模式和抽象工厂模式三种类型。
4.2、工厂模式的应用场景
(1)简单工厂模式的应用场景是什么?
简单工厂模式适用于对象创建过程简单且数量较少的场景,或者客户端不需要知道对象创建过程的场景。
(2)工厂方法模式的应用场景是什么?
工厂方法模式适用于创建对象的任务由多个具体子工厂完成的场景,或者客户端只需要知道产品接口而不需要知道具体产品实现的场景。
(3)抽象工厂模式的应用场景是什么?
抽象工厂模式适用于系统需要独立于具体产品创建、组合和表示的场景,或者系统需要配置多个产品系列之一的场景。
4.3、工厂模式的实现方式
(1)如何实现简单工厂模式?
简单工厂模式通常通过一个工厂类来创建对象,该类包含一个静态方法,该方法根据传入的参数返回相应的对象实例。
(2)如何实现工厂方法模式?
工厂方法模式通过定义一个创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
(3)如何实现抽象工厂模式?
抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。客户端使用抽象工厂来创建具体工厂的子类,从而创建一系列相关对象。
4.4、工厂模式与其他设计模式的对比
(1)工厂模式与单例模式的区别是什么?
工厂模式用于创建对象,而单例模式用于确保一个类只有一个实例,并提供一个全局访问点。
(2)工厂模式与建造者模式的区别是什么?
工厂模式注重于对象的创建过程,而建造者模式则注重于复杂对象的构建过程。建造者模式通过构建一个复杂对象的各个部分来表示其创建过程,并通过一个导演类来管理这些部分的构建顺序。
(3)工厂模式与原型模式的区别是什么?
工厂模式通过实例化类来创建对象,而原型模式则通过复制现有对象来创建新对象。原型模式通过实现一个原型接口,允许对象通过复制自身来创建新的实例。
4.5、工厂模式的优缺点
(1)工厂模式的优点有哪些?
工厂模式将对象的创建过程封装在工厂类中,实现了对象的创建与使用的分离。
工厂模式提高了系统的可扩展性和灵活性,使得系统可以轻松地添加新产品或修改现有产品。
工厂模式有助于减少客户端代码对具体产品类的依赖,降低了系统的耦合度。
(2)工厂模式的缺点有哪些?
工厂模式可能会增加系统的复杂性,因为需要引入额外的工厂类和接口。
在某些情况下,工厂模式可能会导致性能下降,因为需要额外的步骤来创建对象。
5、总结
综上所述,三种工厂模式各有其独特的应用场景和优势。在实际应用中,应根据系统的具体需求和特点来选择合适的工厂模式,以实现代码的高内聚、低耦合和可扩展性。
6、参考文章
6.1 C++之工厂(factory)模式