📜  设计模式 |设置 2(工厂方法)

📅  最后修改于: 2021-09-10 02:50:01             🧑  作者: Mango

工厂方法是一种创建设计模式,即与对象创建相关。在工厂模式中,我们创建对象而不将创建逻辑暴露给客户端,客户端使用相同的通用接口来创建新类型的对象。
这个想法是使用一个静态成员函数(静态工厂方法)来创建和返回实例,对用户隐藏类模块的细节。

工厂模式是创建对象的核心设计原则之一,允许客户端以与库的类层次结构没有紧密耦合的方式创建库的对象(如下所述)。

当我们谈论图书馆和客户时是什么意思?
库是由某些第三方提供的东西,它公开一些公共 API,客户端调用这些公共 API 以完成其任务。一个非常简单的例子可以是 Android 操作系统提供的不同类型的视图。

为什么是工厂模式?
让我们通过一个例子来理解它:

// A design without factory pattern
#include 
using namespace std;
  
// Library classes
class Vehicle {
public:
    virtual void printVehicle() = 0;
};
class TwoWheeler : public Vehicle {
public:
    void printVehicle()  {
        cout << "I am two wheeler" << endl;
    }
};
class FourWheeler : public Vehicle {
    public:
    void printVehicle()  {
        cout << "I am four wheeler" << endl;
    }
};
  
// Client (or user) class
class Client {
public:
    Client(int type)  {
  
        // Client explicitly creates classes according to type
        if (type == 1)
            pVehicle = new TwoWheeler();
        else if (type == 2)
            pVehicle = new FourWheeler();
        else
            pVehicle = NULL;
    }
  
    ~Client()   {
        if (pVehicle)
        {
            delete[] pVehicle;
            pVehicle = NULL;
        }
    }
  
    Vehicle* getVehicle() {
        return pVehicle;
    }
private:
    Vehicle *pVehicle;
};
  
// Driver program
int main() {
    Client *pClient = new Client(1);
    Vehicle * pVehicle = pClient->getVehicle();
    pVehicle->printVehicle();
    return 0;
}

输出:

I am two wheeler

上面的设计有什么问题?
正如您在上面的示例中必须观察到的那样,客户端在构造其对象期间根据某些输入创建 TwoWheeler 或 FourWheeler 对象。
比如说,图书馆引入了一个新类 ThreeWheeler 来合并三轮车。会发生什么?如果在条件阶梯中创建 ThreeWheeler 对象,客户端最终将链接一个新的 else。这反过来又需要重新编译客户端。因此,每次在库端进行新的更改时,Client 都需要在其末尾进行一些相应的更改并重新编译代码。听起来很糟糕?这是一种非常糟糕的设计实践。

如何避免问题?
答案是,创建一个静态(或工厂)方法。让我们看看下面的代码。

// C++ program to demonstrate factory method design pattern
#include 
using namespace std;
  
enum VehicleType {
    VT_TwoWheeler,    VT_ThreeWheeler,    VT_FourWheeler
};
  
// Library classes
class Vehicle {
public:
    virtual void printVehicle() = 0;
    static Vehicle* Create(VehicleType type);
};
class TwoWheeler : public Vehicle {
public:
    void printVehicle() {
        cout << "I am two wheeler" << endl;
    }
};
class ThreeWheeler : public Vehicle {
public:
    void printVehicle() {
        cout << "I am three wheeler" << endl;
    }
};
class FourWheeler : public Vehicle {
    public:
    void printVehicle() {
        cout << "I am four wheeler" << endl;
    }
};
  
// Factory method to create objects of different types.
// Change is required only in this function to create a new object type
Vehicle* Vehicle::Create(VehicleType type) {
    if (type == VT_TwoWheeler)
        return new TwoWheeler();
    else if (type == VT_ThreeWheeler)
        return new ThreeWheeler();
    else if (type == VT_FourWheeler)
        return new FourWheeler();
    else return NULL;
}
  
// Client class
class Client {
public:
  
    // Client doesn't explicitly create objects
    // but passes type to factory method "Create()"
    Client()
    {
        VehicleType type = VT_ThreeWheeler;
        pVehicle = Vehicle::Create(type);
    }
    ~Client() {
        if (pVehicle) {
            delete[] pVehicle;
            pVehicle = NULL;
        }
    }
    Vehicle* getVehicle()  {
        return pVehicle;
    }
  
private:
    Vehicle *pVehicle;
};
  
// Driver program
int main() {
    Client *pClient = new Client();
    Vehicle * pVehicle = pClient->getVehicle();
    pVehicle->printVehicle();
    return 0;
}

输出:

I am three wheeler

在上面的例子中,我们已经将用于创建对象的类型选择与客户端完全分离。该库现在负责根据输入决定要创建的对象类型。客户端只需要调用库的工厂 Create 方法并传递它想要的类型,而无需担心对象创建的实际实现。

感谢 Rumplestiltskin 提供上述解释。


工厂方法的其他例子:

  1. 比如说,在“绘图”系统中,根据用户的输入,可以绘制不同的图片,如正方形、矩形、圆形。在这里,我们可以使用工厂方法根据用户的输入创建实例。对于添加新类型的形状,无需更改客户端的代码。
  2. 另一个例子:在旅游网站,我们可以预订火车票,也可以预订巴士票和机票。在这种情况下,用户可以将其旅行类型指定为“公共汽车”、“火车”或“航班”。
    这里我们有一个带有静态成员函数“GetObject”的抽象类“AnyTravel”,它根据用户的旅行类型,将创建和返回“BusTravel”或“TrainTravel”对象。 “BusTravel”或“TrainTravel”具有乘客姓名、出发地、目的地参数等通用功能。

感谢 Abhijit Saha 提供了前两个示例。