一、简单工厂模式
1.1、简单工厂模式思想
简单工厂模式的核心思想是通过一个专门的工厂类,根据传入的参数或条件来决定创建并返回哪一个具体的产品对象。这个工厂类包含了必要的逻辑来判断应该创建哪个产品的实例,并将对象的创建与使用分离,从而提高代码的可扩展性和可维护性。
1.2、简单工厂模式特点
- 封装性:简单工厂模式隐藏了对象创建的具体逻辑,客户端代码不需要知道对象是如何被创建出来的。
- 解耦:实现了创建对象的过程与实际的使用过程的分离,降低了代码之间的耦合度。
- 易于扩展(理论上):在理想情况下,当需要添加新的产品时,只需添加新的产品类和相应的工厂逻辑,无需修改原有代码。但实际操作中可能会因修改工厂类而违反开闭原则。
1.3、简单工厂模式优缺点
优点
-
实现简单:客户端不需要知道具体产品的创建细节,只需与工厂类交互。
-
降低耦合度:将对象的创建与使用分离,使得代码更加灵活和可维护。
缺点
-
违反开闭原则:当添加新产品时,通常需要修改工厂类的代码,这违反了开闭原则(对扩展开放,对修改关闭)。
-
不利于扩展:随着产品种类的增加,工厂类的代码可能会变得复杂且难以维护。
1.4、简单工厂模式案例
以下是一个简单的手机生产工厂模式的案例:
// 手机基类
class Phone {
public:
virtual string GetName() = 0;
};
// 华为手机类
class HWPhone : public Phone {
public:
string GetName() {
return "Huawei Phone";
}
};
// 苹果手机类
class IPhone : public Phone {
public:
string GetName() {
return "iPhone";
}
};
// 红米手机类
class RMPhone : public Phone {
public:
string GetName() {
return "Redmi Phone";
}
};
// 工厂类
class Factory {
public:
enum PHONE_TYPE {
HUAWEI, // 华为手机
IPHONE, // 苹果手机
REDMI // 红米手机
};
Phone* CreatePhone(PHONE_TYPE type) {
Phone* pPhone = nullptr;
switch (type) {
case PHONE_TYPE::HUAWEI:
pPhone = new HWPhone(); // 华为手机
break;
case PHONE_TYPE::IPHONE:
pPhone = new IPhone(); // 苹果手机
break;
case PHONE_TYPE::REDMI:
pPhone = new RMPhone(); // 红米手机
break;
default:
break;
}
return pPhone;
}
};
// main函数测试代码
int main() {
Factory* pFactory = new Factory();
Phone* phwPhone = pFactory->CreatePhone(Factory::HUAWEI);
std::cout << phwPhone->GetName(); // 输出"Huawei Phone"
delete pFactory;
delete phwPhone;
return 0;
}
1.5、简单工厂模式应用场景
- 产品种类相对较少且不会频繁变动的情况:由于简单工厂模式在添加新产品时需要修改工厂类,因此它适用于产品种类相对较少且不会频繁变动的场景。
- 需要封装对象创建细节的场景:当客户端代码不需要知道具体产品的创建细节时,可以使用简单工厂模式来隐藏这些细节。
- 小型项目或快速原型开发:在小型项目或快速原型开发中,简单工厂模式可以简化代码结构,提高开发效率。
总的来说,简单工厂模式在C++中是一种有用的设计模式,它通过封装对象创建的具体逻辑来降低代码之间的耦合度,并提高代码的可扩展性和可维护性。然而,它也存在一些缺点,如违反开闭原则和不利于扩展等。因此,在选择是否使用简单工厂模式时,需要根据具体的应用场景和需求进行权衡。
二、抽象工厂模式
2.1、抽象工厂模式思想
抽象工厂模式的核心思想是提供一个创建一系列相关或相互依赖的对象的接口,而无需指定它们具体的类。它通过将对象的创建与使用分离,使得客户端代码可以更加简洁和易于维护。同时,由于抽象工厂提供了统一的接口,我们可以在不改变客户端代码的情况下增加新的产品或者修改已有的产品。
2.2、抽象工厂模式特点
- 封装性:抽象工厂模式将产品对象的创建过程封装在工厂类中,客户端代码只需要通过工厂接口来创建产品对象,而不需要知道具体的实现细节。
- 解耦:实现了产品对象的创建与使用的分离,降低了客户端代码与产品对象之间的耦合度。
- 扩展性:通过增加新的具体工厂类和具体产品类,可以轻松地扩展新的产品族,而无需修改客户端代码。
- 抽象性:抽象工厂模式增加了系统的抽象层次,使得系统更加灵活和可扩展。
2.3、抽象工厂模式优缺点
优点
-
易于交换产品系列:由于具体工厂类在一个应用中只需要在初始化的时候出现一次,这使得改变一个应用的具体工厂变得十分容易。只需要改变具体工厂即可使用不同的产品配置。
-
增加新的产品系列容易:扩展新的产品族比较方便,符合开闭原则。
-
提高代码的可维护性和可靠性:通过将对象的创建与使用分离,使得代码更加清晰、易于维护,并可以避免对象的不正确创建和使用,从而提高代码的可靠性。
缺点
-
难以支持新种类的产品:一旦工厂类中定义好了创建产品的方法后,如果需要添加新产品就不容易,因为抽象工厂接口确定后不容易修改。
-
复杂度增加:随着产品族的增加,相关的具体工厂和产品类的数量也会增加,这会使系统更加复杂。
2.4、抽象工厂模式案例
以下是一个抽象工厂模式的简单案例,用于生产不同风格的沙发:
// 抽象产品类
class Sofa {
public:
virtual void style() = 0;
};
// 具体产品类:现代风沙发
class ModernSofa : public Sofa {
public:
void style() {
cout << "Modern style sofa" << endl;
}
};
// 具体产品类:法式风沙发
class FrenchSofa : public Sofa {
public:
void style() {
cout << "French style sofa" << endl;
}
};
// ... 其他具体产品类(如奶油风、中式风沙发等)
// 抽象工厂类
class SofaFactory {
public:
static Sofa* createSofa(string style) {
if (style == "Modern") {
return new ModernSofa();
} else if (style == "French") {
return new FrenchSofa();
} else {
// 可以添加更多产品族的判断逻辑
return NULL;
}
}
};
// main函数测试代码
int main() {
Sofa* modernSofa = SofaFactory::createSofa("Modern");
modernSofa->style();
delete modernSofa;
Sofa* frenchSofa = SofaFactory::createSofa("French");
frenchSofa->style();
delete frenchSofa;
// ... 可以添加更多测试代码
return 0;
}
注意:在实际应用中,抽象工厂模式通常会包含多个抽象产品类和多个具体产品类,以及一个或多个具体工厂类。上述案例为了简化说明,只展示了一个抽象产品类和两个具体产品类,以及一个静态工厂方法。
2.5、抽象工厂模式应用场景
- 系统需要独立于具体产品的创建、组成和表示:当系统应该对产品类的实现细节和创建过程无需了解时,可以使用抽象工厂模式来封装这些细节。
- 系统需要多个产品系列中的一个来配置:当系统需要动态地更改产品系列时,可以使用抽象工厂模式来提供不同产品系列的配置选项。
- 系统需要提供产品库的类,且只想显示它们的接口而不是实现:当系统需要提供一个产品库,但只希望暴露产品的接口而不是具体实现时,可以使用抽象工厂模式来隐藏具体产品的创建细节。
综上所述,抽象工厂模式在C++中是一种非常有用的设计模式,它可以帮助我们更好地组织和管理代码,提高代码的可维护性和可靠性。然而,它也存在一些缺点,如难以支持新种类的产品和增加系统复杂度等。因此,在选择是否使用抽象工厂模式时,需要根据具体的应用场景和需求进行权衡。
三、工厂方法模式
工厂方法模式(Factory Method Pattern)是C++中一种重要的设计模式,它属于创建型模式,旨在将对象的创建过程与使用过程分离,以提高代码的灵活性和可维护性。以下是对工厂方法模式的思想、特点、优缺点、案例以及应用场景的详细解析。
3.1、工厂方法模式思想
工厂方法模式的核心思想是定义一个用于创建对象的接口,但由子类决定要实例化的类是哪一个。这样,类的实例化被延迟到了其子类,从而实现了对象的创建和使用的分离。
3.2、工厂方法模式特点
- 封装性:工厂方法模式通过引入工厂类,将对象的创建逻辑封装在工厂类中,客户端代码只需要与工厂类交互,无需关心具体的对象创建细节。
- 开放封闭原则:当需要添加新的产品时,只需要增加一个新的具体产品类和对应的工厂子类,而不需要修改现有代码,符合软件设计的开放封闭原则。
- 代码复用:相同的创建逻辑可以复用,减少了代码重复。
- 解耦:工厂方法模式将对象的创建与使用分离,使得代码的生产和消费部分解耦,有助于遵循单一职责原则。
3.3、工厂方法模式优缺点
优点:
-
易于扩展:当需要添加新的产品类型时,只需增加新的产品类和相应的工厂子类,无需修改客户端代码。
-
降低耦合度:客户端代码与具体产品类解耦,提高了系统的可维护性和可扩展性。
-
隐藏实现细节:通过工厂方法,创建对象的细节被隐藏在工厂类中,客户端只需要知道具体的工厂及工厂接口,无需关心具体的创建过程。
缺点:
-
增加系统复杂度:引入工厂方法模式后,系统的抽象性和理解难度增加,同时代码量和复杂度也会相应增加。
-
类的数量增加:每个产品类都需要对应一个工厂子类,这可能导致类的数量过多,不利于系统的管理和维护。
3.4、工厂方法模式案例
以下是一个简单的C++工厂方法模式案例:
#include <iostream>
#include <memory>
// 抽象产品类
class Shape {
public:
virtual void draw() = 0;
virtual ~Shape() = default;
};
// 具体产品类:圆形
class Circle : public Shape {
public:
void draw() override {
std::cout << "draw a circle" << std::endl;
}
};
// 具体产品类:矩形
class Rectangle : public Shape {
public:
void draw() override {
std::cout << "draw a rectangle" << std::endl;
}
};
// 抽象工厂类
class ShapeFactory {
public:
virtual std::unique_ptr<Shape> createShape() = 0;
virtual ~ShapeFactory() = default;
};
// 具体工厂类:圆形工厂
class CircleFactory : public ShapeFactory {
public:
std::unique_ptr<Shape> createShape() override {
return std::make_unique<Circle>();
}
};
// 具体工厂类:矩形工厂
class RectangleFactory : public ShapeFactory {
public:
std::unique_ptr<Shape> createShape() override {
return std::make_unique<Rectangle>();
}
};
int main() {
ShapeFactory* factory = new CircleFactory();
std::unique_ptr<Shape> shape = factory->createShape();
shape->draw();
delete factory;
factory = new RectangleFactory();
shape = factory->createShape();
shape->draw();
delete factory;
return 0;
}
在这个案例中,Shape
是抽象产品类,Circle
和Rectangle
是具体产品类,ShapeFactory
是抽象工厂类,CircleFactory
和RectangleFactory
是具体工厂类。客户端代码通过工厂类来创建具体的产品对象,而无需知道具体产品的实现细节。
3.5 工厂方法模式应用场景
工厂方法模式在C++中有广泛的应用场景,包括但不限于:
- 创建复杂对象:当对象的创建过程比较复杂,需要多个步骤或条件判断时,可以使用工厂方法模式来封装创建逻辑。
- 解耦客户端和具体产品:工厂方法模式将对象的创建和使用分离,使得客户端代码不需要知道具体产品的实现细节,从而降低了客户端代码与具体产品之间的耦合度。
- 动态选择产品:当需要根据不同的条件或配置动态选择创建不同的产品时,可以使用工厂方法模式。
- 延迟实例化:在某些情况下,可能需要延迟对象的实例化,直到真正需要使用该对象时才进行创建。工厂方法模式可以帮助实现这种延迟实例化。
综上所述,工厂方法模式是一种强大而灵活的设计模式,在C++编程中有广泛的应用。然而,在使用时需要谨慎考虑其适用性和潜在的问题,以确保代码的正确性、可维护性和可扩展性。
四、三种模式的区别
C++中的简单工厂模式、抽象工厂模式和工厂方法模式都是创建型设计模式,它们的共同点和区别如下:
4.1 共同点
- 封装对象的创建:这三种模式都通过定义一个接口(或抽象类)来封装对象的创建过程,使得客户端代码不需要直接实例化对象,而是通过工厂类来获取对象实例。
- 解耦对象的创建与使用:它们都将对象的创建和使用进行解耦,使得客户端代码只需要依赖抽象接口或基类,而不需要关心具体对象的创建过程。
- 提高代码的可维护性和可扩展性:通过集中管理对象的创建过程,这三种模式都可以在不修改客户端代码的情况下轻松地扩展新的对象类型。
4.2 区别
-
结构复杂度:
- 简单工厂模式:结构相对简单,通过一个具体的工厂类来创建所有对象,但可能会导致工厂类过于臃肿,不符合单一职责原则。
- 工厂方法模式:结构相对复杂一些,每个具体产品都有一个对应的工厂类,通过不同的工厂子类来实例化不同的产品,增加了代码的可扩展性。
- 抽象工厂模式:结构最为复杂,它提供了一种创建一系列相关或相互依赖的对象的机制,而无需指定具体类。抽象工厂模式通常包含多个抽象方法和具体工厂类,用于创建一组相关的产品。
-
产品类型的支持:
- 简单工厂模式:通常用于创建数量不多且具有共同属性的相关类对象。
- 工厂方法模式:可以支持多个产品类型,每个产品类型都有一个对应的工厂类。
- 抽象工厂模式:支持创建一组相关或相互依赖的对象,这些对象通常是同一主题的,例如创建不同风格的沙发。
-
扩展性:
- 简单工厂模式:扩展性较差,因为每增加一个新的产品类型,都需要修改工厂类中的代码。
- 工厂方法模式:扩展性较好,因为每增加一个新的产品类型,只需要增加一个新的工厂类,而不需要修改现有的工厂类或客户端代码。
- 抽象工厂模式:扩展性也非常好,可以在不修改客户端代码的情况下增加新的产品族(即一组相关的产品)。
-
使用场景:
- 简单工厂模式:适用于创建数量不多且具有共同属性的相关类对象,例如创建一个简单的信息管理系统中的学生类、教师类等。
- 工厂方法模式:适用于需要创建多个产品类型,并且每个产品类型都需要独立扩展的场景,例如日志记录器、数据库访问等。
- 抽象工厂模式:适用于需要创建一组相关或相互依赖的对象,并且这些对象通常是同一主题的场景,例如创建不同风格的沙发、不同品牌的手机等。
综上所述,简单工厂模式、抽象工厂模式和工厂方法模式在C++中都有其独特的特点和使用场景。选择哪种模式取决于具体的需求和设计目标。
参考: