C++设计模式——工厂模式

1 简单工厂模式

1.1 什么是简单工厂模式?

之所以叫做简单工厂模式,是相对于其他两种工厂模式(工厂方法模式和抽象工厂模式)而言,它的实现代码更简单,理解起来更加清晰易懂。

1.2 简单工厂模式举例

在这里,我想用GUI背景下的内容来举例说明简单工厂模式:在开发一个用户界面过程中,我需要三种控件类型,分别是按钮、图像和文本。这些控件都有一些工厂的特点,比如都有位置属性(位于界面的哪个地方)、大小属性(长度和宽度)等等。因此创建一个Item(参考Qt)作为它们的父类,Button(按钮)、Image(图像)和Text(文本)都继承于它。下面是参考代码:

// Item父类
class Item {
public:
    Item(int width, int height) : m_width(width), m_height(height) {}
    virtual ~Item() {}
protected:
    int m_width;
    int m_height;
};

// Button子类
class Button : public Item {
public:
    Button(int width, int heght) : Item(width, heght) {
        cout << "用户界面创建了一个按钮" << endl;
    }
};

// Image子类
class Image : public Item {
public:
    Image(int width, int heght) : Item(width, heght) {
        cout << "用户界面创建了一个图像" << endl;
    }
};

// Text子类
class Text : public Item {
public:
    Text(int width, int heght) : Item(width, heght) {
        cout << "用户界面创建了一个文本" << endl;
    }
};
接下来,我们要创建一个工厂,它是专门用来生产这些控件,将这个工厂类命名为ItemFactory,下面是参考代码:
// ItemFactory工厂类
class ItemFactory {
public:
    Item* createItem(string itemType) {
        Item* newItem = nullptr;
        if (itemType == "button") {
            newItem = new Button(50, 50);
        } else if (itemType == "image") {
            newItem = new Image(100, 100);
        }else if (itemType == "text") {
            newItem = new Text(100, 30);
        }
        return newItem;
    }
};
通过上面的代码我们可以看到,createItem成员函数根据接收的不同字符串,来创建不同的控件。我们在主函数中可以这样使用这个简单工厂模式:
int main() {
    ItemFactory itemFac;
    Item* pI1 = itemFac.createItem("button"); // 创建了一个Button
    Item* pI2 = itemFac.createItem("image"); // 创建了一个Image
    Item* pI3 = itemFac.createItem("text"); // 创建了一个Text

    delete pI1;
    delete pI2;
    delete pI3;
}

1.3 简单工厂模式总结

我们使用简单工厂模式后,创建各种各样的控件,就不用和它们控件本身的类打交道(Button、Image、Text),而是通过ItemFactory的成员函数createFactory,将不同字符串标识传入进去,从而返回不同的控件,这其实就是一种封装变化。 但是简单工厂模式的缺点也很明显,那就是当我们引入新的控件类型时,例如想加入一个新的控件叫做Rectangle(矩形框)时,我们就需要在createItem函数中加入一个新的分支。这其实违反了面向对象程序设计的一个原则——开闭原则:即对扩展开放,对修改关闭。解释一下就是当我们需要增加新功能时,不应该修改已经存在的代码,而应该通过拓展代码(例如增加新类、增加新成员函数)来进行。

2 工厂方法模式

2.1 什么是工厂方法模式?

上一节我们已经了解过简单工厂模式,当我们需要增加一个新的控件时,需要修改原有的代码,这违反了开闭原则,而工厂方法模式的存在就是解决了简单工厂模式的这个问题。工厂方法模式新增加了一个工厂抽象类I_ParFactory,它是其它工厂类的基类。接下来我们会有I_ButtonFactory、I_ImageFactory、I_TextFactory,这些都继承自I_ParFactory。

2.2 工厂方法模式举例

这里沿用上面的例子,我们将代码进行改造,就变成了需要的工厂方法模式:

// I_ParFactory工厂父类
class I_ParFactory {
public:
    virtual Item* createItem() = 0;
    virtual ~I_ParFactory() {};
};

// I_ButtonFactory按钮工厂
class I_ButtonFactory {
public:
    virtual Item* createItem() {
        return new Button(50, 50);
    }
};

// I_ImageFactory按钮工厂
class I_ImageFactory {
public:
    virtual Item* createItem() {
        return new Image(100 100)
    }
};

// I_TextFactory按钮工厂
class I_TextFactory {
public:
    virtual Item* createItem() {
        return new Text(100, 30);
    }
};
拥有了这些控件工厂类之后,我们再创建一个全局函数Gbl_CreateItem来生成这些控件类,代码如下:
Item* Gbl_CreaateItem(I_ParFactory* factory) {
    return factory->createItem();
}

主函数中,我们按照下面的方式来创建控件:

int main() {
    I_ParFactory* button_fac = new I_ButtonFactory();
    Item* button = Gbl_CreateItem(button_fac)

    I_ParFactory* image_fac = new I_ImageFactory();
    Item* image = Gbl_CreateItem(image_fac)

    I_ParFactory* text_fac = new I_TextFactory();
    Item* text = Gbl_CreateItem(text_fac)
}

2.3 工厂方法模式总结

通过上面的代码和分析,我们可以观察到,每当创建一个新的控件时,都对应一个新的控件工厂类,感觉比简单工厂模式复杂了一些。如果这个时候需要新增加一个Rectangle(矩形框),我们首先得将Rectangle继承自Item类,并且得对应创建一个矩形工厂类,I_RectangleFactory继承自I_ParFactory类。这样子为了增加一个新的控件类型,必须得增加控件类型和控件工厂类型。但与此同时也是优势,因为它满足了开闭原则,不用去修改原有的代码了。

3 抽象工厂模式

3.1 什么是抽象工厂模式?

想象这样一种情况:前面我们只是开发Window平台的各种控件(按钮、图像和文本),现在我们需要开发iOS和Linux平台下的这些控件。我这个时候如果按照工厂模式的设计的话,那么需要9种控件子类,并且需要就个工厂子类。但如果有一个工厂子类能够生产不止一种具有相同规则的控件对象,那么就可以有效地减少所创建的工厂子类的数量,这就是抽象工厂模式的核心思想。

3.2 抽象工厂模式举例

我们还是沿用生产控件的例子,保留Item父类,重新引入9个控件子类,下面是参考代码:

// windos下的Button
class I_Button_win : public Item {
public:
    I_Button_win(int width, int height) : Item(width, height) {
        cout << "windos平台下的Button创建了" << endl;
    }
};

// windos下的Image
class I_Image_win : public Item {
public:
    I_Image_win(int width, int height) : Item(width, height) {
        cout << "windos平台下的Image创建了" << endl;
    }
};

// windos下的Text
class I_Text_win : public Item {
public:
    I_Text_win(int width, int height) : Item(width, height) {
        cout << "windos平台下的Text创建了" << endl;
    }
};

// iOS下的Button
class I_Button_iOS : public Item {
public:
    I_Button_iOS(int width, int height) : Item(width, height) {
        cout << "iOS平台下的Button创建了" << endl;
    }
};

// iOS下的Image
class I_Image_iOS : public Item {
public:
    I_Image_iOS(int width, int height) : Item(width, height) {
        cout << "iOS平台下的Image创建了" << endl;
    }
};

// iOS下的Text
class I_Text_iOS : public Item {
public:
    I_Text_iOS(int width, int height) : Item(width, height) {
        cout << "iOS平台下的Text创建了" << endl;
    }
};

// linux下的Button
class I_Button_linux : public Item {
public:
    I_Button_linux(int width, int height) : Item(width, height) {
        cout << "linux平台下的Button创建了" << endl;
    }
};

// linux下的Image
class I_Image_linux : public Item {
public:
    I_Image_linux(int width, int height) : Item(width, height) {
        cout << "linux平台下的Image创建了" << endl;
    }
};

// linux下的Text
class I_Text_linux : public Item {
public:
    I_Text_linux(int width, int height) : Item(width, height) {
        cout << "linux平台下的Text创建了" << endl;
    }
};

因为一个工厂是针对一个平台去生产的,所以一共需要一个工厂父类,三个工厂子类,下面是工厂父类和工厂子类的写法:

// windos平台下的工厂
class I_Factory_Windos : public I_ParFactory {
public:
    virtual Item* createItem_button() {
        return new I_Button_win(50, 50);
    }
    virtual Item* createItem_image() {
        return new I_Image_win(100, 100);
    }
    virtual Item* createItem_text() {
        return new I_Text_win(100, 30);
    }
};

// iOS平台下的工厂
class I_Factory_iOS : public I_ParFactory {
public:
    virtual Item* createItem_button() {
        return new I_Button_iOS(50, 50);
    }
    virtual Item* createItem_image() {
        return new I_Image_iOS(100, 100);
    }
    virtual Item* createItem_text() {
        return new I_Text_iOS(100, 30);
    }
};

// linux平台下的工厂
class I_Factory_linux : public I_ParFactory {
public:
    virtual Item* createItem_button() {
        return new I_Button_linux(50, 50);
    }
    virtual Item* createItem_image() {
        return new I_Image_linux(100, 100);
    }
    virtual Item* createItem_text() {
        return new I_Text_linux(100, 30);
    }
};

3.3 抽象工厂模式总结

抽象工厂模式的优点:相比于工厂方法模式而言,抽象工厂模式大大减少了工厂的数量,并且如果这个时候继续增加一个平台控件的开发,例如安卓平台。此时只需要在原有代码上,继续增加一个安卓控件工厂子类。这是符合开闭原则的。 抽象工厂模式的缺点:如果这个时候,我们需要增加一个新的控件(Rectangle),此时就只能修改工厂子类的代码来满足新控件的生产。这个时候就不符合开闭原则了。 因此,抽象工厂模式适合于产品种类固定的情况(控件的类型),避免在产品种类不稳定的情况下使用该模式。

你可能感兴趣的:(C++基础,c++,设计模式)