设计模式之简单工厂模式

107 阅读4分钟

不可避免地创建对象

当你使用任何一门常用的编程语言进行编程时,你总是直接地或间接地在创建对象,它们都需要申请一块内存,在C语言里,你可以使用malloc函数来申请一块内存,在一些面向对象编程语言里(例如 C++ C# Java...),你可能需要使用new运算符,总的来说,无论是malloc还是new还是其它的一些创建对象的方式,我们都少不了重复地写一些创建对象的语句,比如:type object_name = new type()...,为了减少new(或其它方式,正文主要讲述new)带来的眼花缭乱的代码,我们可以使用工厂模式(本文主要讲述简单工厂模式)。

简单工厂模式

简介

简单工厂模式顾名思义,是工厂模式中较简单的设计模式。

实例

如果一家互联网公司有以下产品:

  1. 计算机
  2. 手机
  3. 手表

那么,如果我需要购买其中的一项产品,我只需要交钱拿货就好了。

如果将这个抽象成程序代码,就应该是这样的:

将互联网公司比作一个工厂(一个类),这个工厂专门生产产品,并且上面列出的产品都独立成一个类且继承Product(产品)类,那么工厂类只需要提供CreateProduct方法,我们就能获取到产品。

其中CreateProduct的返回值应该是Product类型(基类),在函数内部,我们根据对应的“需求”返回对应的产品,

例如,我们用key(字符串)这个参数获取需求,那么如果key为"计算机",则return new Computer();,如果为"手表",则return new Phone(),如果为"手表",则return new Watch(),当这家公司没有符合条件的产品时,返回null。


当然,这里我们不会使用这个例子,我们使用Shape(形状)来演示简单工厂模式

文件结构

image.png

你可能使用过GUI编程,无论是signals/slots还是消息机制,你可能使用过绘图。

如果要开发一个画图应用(一个Windows内置的画板程序),你需要根据用户选择的图形在指定位置进行绘制。

那,我们再深入底层,大部分的绘图模块可能已经将各种形状拆开了,但是如果你想自己重新编写一个绘图的引擎,你可能会写出类似上图结构的程序,即一个Shape基类和它的子类,再通过绘制的技术展现给用户。其实,Shape类是抽象的,我们为它提供一个Draw方法但是不能实现,因为我不知道Shape究竟是何Shape,所以可以将Draw方法设置成纯虚函数,当Shape类型的指针指向其子类对象地址时,Draw函数能够得到调用,因为在此期间发生了多态,这个例子没有考虑到各种形状的参数,先在我们回归主题-简单工厂模式。

画图应用是一个好例子,当用户点击到“绘制某个形状”的按钮时,我们将这个形状通过某种形式的字符串传递给某个函数,这样就能获取到形状并且绘制。某个函数其实就是工厂方法,我们将一个字符串当作我们的需求传入时,它会给我们期待的一个对象。

Shape.h 和 Square.h(其余头文件和源文件代码类似下面的模板)

#ifndef _SHAPE_H_
#define _SHAPE_H_class Shape
{
public:
    virtual void Draw() = 0;
    virtual void PrintInfo() = 0;
};
​
#endif // shape.h

#ifndef _SQUARE_H_
#define _SQUARE_H_#include "shape.h"class Square : public Shape {
public:
    Square();
    Square(const Square&) = default;
    Square(Square&&) = default;
    virtual void Draw() override;
    virtual void PrintInfo() override;
};
​
#endif // square.h

Square.cpp

#include <iostream>
#include "../include/square.h"
​
Square::Square()
    : Shape()
{
}
​
void Square::Draw()
{
    std::cout << "Draw(): 绘制一个正方形" << std::endl;
}
​
void Square::PrintInfo()
{
    std::cout << "PrintInfo(): 我是一个正方形" << std::endl;
}

这个例子并不是展示多态,而是工厂模式,我们编写一个工厂类如下:

shape_factory.h

#ifndef _SHAPE_FACTORY_H_
#define _SHAPE_FACTORY_H_#include "shape.h"
#include "square.h"
#include "triangle.h"
#include "circle.h"class ShapeFactory
{
public:
    static Shape* CreateShape(const char* name);
};
​
​
#endif // shape_factory.h

shape_factory.cpp

#include "../include/shape_factory.h"Shape* ShapeFactory::CreateShape(const char* name)
{
    if (name == "circle") {
        
        return new Circle();
        
    } else if (name == "triangle") {
        
        return new Triangle();
        
    } else if (name == "square") {
        
        return new Square();
        
    }
    return nullptr;
}

可以看到,我们对name进行了判断,以返回一个需要的对象。

main.cpp

#include "config.h"
#ifdef _TEST_FOR_SIMPLE_FACTORY_
#include <iostream>
#include <vector>
#include "../include/shape_factory.h"void DrawShape(Shape* shape) {
    
    shape->Draw();
    
}
​
void PrintShapeInfo(Shape* shape) {
    
    shape->PrintInfo();
    
}
​
void DrawAndPrintShapeInfo(Shape* shape) {
    
    DrawShape(shape);
    PrintShapeInfo(shape);
    
}
​
int main() {
​
    std::unique_ptr<Circle> circle { 
        dynamic_cast<Circle*>(ShapeFactory::CreateShape("circle")) };
    // ok
​
    std::unique_ptr<Triangle> triangle {
        dynamic_cast<Triangle*>(ShapeFactory::CreateShape("triangle")) };
    // ok
​
    std::unique_ptr<Square> square {
        dynamic_cast<Square*>(ShapeFactory::CreateShape("square")) };
    // ok
​
    std::unique_ptr<Triangle> triangle2 {
        dynamic_cast<Triangle*>(ShapeFactory::CreateShape("isoscelestriangle")) }; 
    // null!
    
    std::cout << (triangle2.get() == nullptr ? "true" : "false")<< std::endl; // true
    
    DrawAndPrintShapeInfo(circle.get());
    DrawAndPrintShapeInfo(triangle.get());
    DrawAndPrintShapeInfo(square.get());
    
    
    return 0;
}
#endif

可以看到,由于我们没有等腰三角形这个类(当前),所以返回null。

输出:

true
draw(): 绘制一个圆形
printInfo(): 我是一个圆形
Draw(): 绘制一个三角形
PrintInfo(): 我是一个三角形
Draw(): 绘制一个正方形
PrintInfo(): 我是一个正方形

通过上面的示例,我们可以通过一个字符串(或者其它)来获取一个对象,这就是简单工厂模式的原理了。