不得不说的创建型模式-抽象工厂模式
抽象工厂模式是一种创建型模式,它提供一个接口来创建一系列相关或相互依赖的对象,而不需要指定它们的具体类。这个接口被称为“抽象工厂”,它可以被不同的具体工厂实现来创建不同的产品族。
下面通过一个简单的示例来说明抽象工厂模式的底层原理和实际应用。
示例场景: 假设我们需要设计一个跨平台的图形用户界面(GUI)库,它需要支持Windows和Linux两个操作系统,同时还需要支持不同的界面主题(如浅色和深色主题)。为了支持不同的操作系统和主题,我们需要使用抽象工厂模式。
首先定义抽象工厂接口:
class AbstractGUIFactory {
public:virtual Button* createButton() = 0;virtual TextBox* createTextBox() = 0;virtual Label* createLabel() = 0;
};
然后定义具体工厂类来实现这个接口:
class WindowsGUIFactory : public AbstractGUIFactory {
public:Button* createButton() { return new WindowsButton(); }TextBox* createTextBox() { return new WindowsTextBox(); }Label* createLabel() { return new WindowsLabel(); }
};class LinuxGUIFactory : public AbstractGUIFactory {
public:Button* createButton() { return new LinuxButton(); }TextBox* createTextBox() { return new LinuxTextBox(); }Label* createLabel() { return new LinuxLabel(); }
};
接下来定义产品类:
class Button {
public:virtual void paint() = 0;
};class TextBox {
public:virtual void display() = 0;
};class Label {
public:virtual void draw() = 0;
};
最后定义具体产品类来实现这些产品接口:
class WindowsButton : public Button {
public:void paint() {// Windows风格的按钮绘制逻辑}
};class LinuxButton : public Button {
public:void paint() {// Linux风格的按钮绘制逻辑}
};class WindowsTextBox : public TextBox {
public:void display() {// Windows风格的文本框绘制逻辑}
};class LinuxTextBox : public TextBox {
public:void display() {// Linux风格的文本框绘制逻辑}
};class WindowsLabel : public Label {
public:void draw() {// Windows风格的标签绘制逻辑}
};class LinuxLabel : public Label {
public:void draw() {// Linux风格的标签绘制逻辑}
};
这样,我们就可以通过不同的具体工厂类来创建不同的产品族,比如使用WindowsGUIFactory来创建Windows风格的GUI,使用LinuxGUIFactory来创建Linux风格的GUI。在不同的操作系统和主题下,我们只需要改变具体工厂类的实现即可,而不需要修改已有的代码。
抽象工厂模式的主要优点是:
-
保证了产品族内部的一致性。由于抽象工厂只负责创建一系列相关的产品,因此它可以确保这些产品之间的兼容性和一致性。
-
使得切换产品族变得容易。由于具体工厂类实现了抽象工厂接口,因此我们可以通过切换具体工厂类来创建不同的产品族,从而使得切换产品族变得容易。
-
使得扩展产品族变得容易。如果需要添加新的产品族,只需要添加一个新的具体工厂类和一些新的产品类即可,而不需要修改已有的代码。
抽象工厂模式的主要缺点是:
-
不够灵活。由于抽象工厂只负责创建一系列相关的产品,因此在需要创建新的产品时,可能需要修改抽象工厂的接口和所有的具体工厂类,这样会导致系统变得不够灵活。
-
不够扩展。如果需要添加新的产品族或者新的产品等级结构,可能需要修改抽象工厂的接口和所有的具体工厂类,这样会导致系统变得不够扩展。
抽象工厂模式在GUI库、游戏引擎等需要创建一系列相关的产品的场景下得到了广泛的应用。在这些场景下,我们需要根据不同的操作系统、不同的设备和不同的用户需求来创建不同的产品,而抽象工厂模式提供了一种很好的解决方案。
具体来说,抽象工厂模式可以应用于以下场景:
-
操作系统界面:不同的操作系统有不同的用户界面,例如Windows和Mac OS X,它们的用户界面是不同的,因此我们可以使用抽象工厂模式来创建不同操作系统的用户界面。
-
游戏引擎:游戏引擎中需要创建很多不同的对象,例如纹理、音效、粒子效果等,而这些对象又需要根据不同的平台进行优化和适配,因此我们可以使用抽象工厂模式来创建不同平台下的游戏对象。
-
数据库访问:不同的数据库有不同的访问方式,例如MySQL和Oracle,它们的访问方式是不同的,因此我们可以使用抽象工厂模式来创建不同的数据库访问对象。
总之,抽象工厂模式提供了一种很好的解决方案,可以帮助我们创建一系列相关的产品,并确保这些产品之间的兼容性和一致性。但是,它也有一些缺点,可能会导致系统变得不够灵活和不够扩展,因此在应用抽象工厂模式时需要权衡利弊,选择合适的设计模式。
假设我们正在开发一个游戏,这个游戏需要支持不同的平台,例如Windows、Linux和Mac OS X,同时还需要支持不同的分辨率和不同的语言。为了实现这个功能,我们可以使用抽象工厂模式来创建不同平台下的游戏对象,例如游戏画面、音效、输入设备等。具体实现如下:
首先,我们需要定义抽象工厂接口,该接口包含了创建游戏对象的方法,例如创建游戏画面、音效和输入设备等:
class AbstractFactory {
public:virtual std::shared_ptr<GameScreen> CreateGameScreen() = 0;virtual std::shared_ptr<GameAudio> CreateGameAudio() = 0;virtual std::shared_ptr<GameInput> CreateGameInput() = 0;
};
然后,我们需要定义具体的工厂类,例如WindowsFactory、LinuxFactory和MacFactory,这些工厂类都实现了抽象工厂接口,并且分别创建了Windows、Linux和Mac OS X下的游戏对象:
class WindowsFactory : public AbstractFactory {
public:virtual std::shared_ptr<GameScreen> CreateGameScreen() override {return std::make_shared<WindowsGameScreen>();}virtual std::shared_ptr<GameAudio> CreateGameAudio() override {return std::make_shared<WindowsGameAudio>();}virtual std::shared_ptr<GameInput> CreateGameInput() override {return std::make_shared<WindowsGameInput>();}
};class LinuxFactory : public AbstractFactory {
public:virtual std::shared_ptr<GameScreen> CreateGameScreen() override {return std::make_shared<LinuxGameScreen>();}virtual std::shared_ptr<GameAudio> CreateGameAudio() override {return std::make_shared<LinuxGameAudio>();}virtual std::shared_ptr<GameInput> CreateGameInput() override {return std::make_shared<LinuxGameInput>();}
};class MacFactory : public AbstractFactory {
public:virtual std::shared_ptr<GameScreen> CreateGameScreen() override {return std::make_shared<MacGameScreen>();}virtual std::shared_ptr<GameAudio> CreateGameAudio() override {return std::make_shared<MacGameAudio>();}virtual std::shared_ptr<GameInput> CreateGameInput() override {return std::make_shared<MacGameInput>();}
};
最后,我们可以在游戏中使用具体工厂类来创建不同平台下的游戏对象,例如:
void Game::Init(const std::string& platform, int resolution, const std::string& language) {std::shared_ptr<AbstractFactory> factory;if (platform == "Windows") {factory = std::make_shared<WindowsFactory>();}else if (platform == "Linux") {factory = std::make_shared<LinuxFactory>();}else if (platform == "Mac OS X") {factory = std::make_shared<MacFactory>();}else {throw std::runtime_error("Unsupported platform");}m_gameScreen = factory->CreateGameScreen();m_gameAudio = factory->CreateGameAudio();m
上述代码中,我们通过传递平台、分辨率和语言等参数来创建不同平台下的游戏对象。具体来说,我们先根据平台类型创建相应的工厂对象,然后使用工厂对象来创建游戏对象,最后设置游戏对象的参数。通过这种方式,我们可以在游戏运行时根据需要动态地创建不同平台下的游戏对象,从而实现游戏的跨平台支持。
抽象工厂模式在实际应用中具有广泛的应用。例如,操作系统中的GUI工具包就是一个典型的抽象工厂模式的应用,不同的操作系统下使用不同的GUI工具包,而不同的GUI工具包又可以支持不同的控件和主题等。此外,在游戏开发、网络编程、图形图像处理等地方中,抽象工厂模式也有着重要的应用。