概念
定义一个用于创建的接口,让子类决定实例化哪一个类,它使一个类的实例化延迟到子类。
个人理解
1. 适用范围
实际上在抽象工厂模式的实现中已经或多或少使用到了工厂方法的思想:客户只知道获得的抽象对象,但我们需要创建的是一个具体的对象。表面上客户是从抽象的工厂获得抽象的对象,但实际上我们是让具体工厂去实例化一个具体的对象,把它向上类型转换后(具体对象继承于抽象对象)返回给客户。
由此延申工厂方法的适用场景:
- 当一个类不知道它所必须创建的对象的类时。即这个类获得的是一个抽象对象,它看不到下一层的具体对象。
- 当一个类希望由它的子类来指定它所创建的对象时。这点是站在创建方说的,即延迟实例化。
- 当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化时。局部化的反义词是全局化,就是说,具体让哪个子类来代理创建这件事我不想让所有类知道,只需要干这件事的那个帮助子类知道就行了。
2. 代码实现(C++)
实现与抽象工厂是十分类似的,只是抽象工厂多了一个同一系列的限制,是为同一系列的对象组合来划分具体工厂,可以说如果你把抽象工厂中同一系列的对象组合放在一个类里变成一个大对象,那么它和工厂方法模式没有什么区别,工厂方法模式是为每个具体产品提供一个具体工厂(这是一般情况下,当然你想一个工厂代理多种对象的创建也是可以的,看你自己的需求)。
组成部分:
- Product 抽象产品。就是客户最后获得的对象。
- ConcreteProduct 具体产品。具体工厂创建的对象,客户不可见。
- Creator 抽象创建者(工厂)。提供创建接口 virtual Product* Create(); ,值得注意的是这个接口既可以声明为纯虚函数,也可以在里面写一些默认的创建逻辑。因为,客户使用是用的具体创建者ConcreteCreator,于是 一般是当具体创建者中的创建逻辑覆盖不到时再调用父类的Create() ,这样又把实例化延迟到了父类。
- ConcreteCreator 具体创建者(工厂)。一般情况下,创建的操作在这个类中实现。
2.1 产品部分
创建产品直接写构造函数,但是何时创建交给创建者下的子类来决定。
类图:
代码:
typedef struct ListNode
{
const char* str;
ListNode* next;
ListNode(const char* s)
{
str = s;
next = nullptr;
}
}ListNode;
class Product
{
public:
Product();
virtual void add();
ListNode* GetList() { return head; }
protected:
ListNode* head;
ListNode* tail;
int length;
};
class ConcreteProductA :public Product
{
public:
ConcreteProductA();
void add();
};
class ConcreteProductB :public Product
{
public:
ConcreteProductB();
void add();
};
Product::Product()
{
head = new ListNode("Default Product head");
tail = head;
length = 0;
}
void Product::add()
{
ListNode* p = new ListNode("Add Default Node");
tail->next = p;
tail = p;
length++;
}
ConcreteProductA::ConcreteProductA()
{
Product::Product();
head->str = "Product A head";
}
void ConcreteProductA::add()
{
Product::add();
tail->str = "Add Node A";
}
ConcreteProductB::ConcreteProductB()
{
Product::Product();
head->str = "Product B head";
}
void ConcreteProductB::add()
{
Product::add();
tail->str = "Add Node B";
}
2.2 创建者部分
一些高效的技巧:
- 模板类。由于上面的product类及其子类都是把构造函数作为接口供Creator类及其子类调用,也就是说我要创建一个 MyProduct 对象,只需要 return new MyProduct; 这样一句代码就可以。而C++中恰好有 template 模板 来自定义类型,那么我们只需要采用 模板类 的方式,就可以省去新写很多子类的功夫。
- 引入参数。上面有说到过,一个具体创建者不一定只管一种类型的product创建,可以根据需要的逻辑自定义这些产品的创建。最常见的就是 在创建函数中引入参数 ,我们就可以根据给到的参数选择一个类型的产品返回。值得一提的是,Creator 类中的带参数的创建接口也是可以默认写一些逻辑的,因此具体产品的实例化是可以根据逻辑由下向上层层延迟的。
上面两种方式的类图如下:
代码:
class Creator
{
public:
virtual Product* Create();
virtual Product* Create(int id);
};
template <class TheProduct>
class StandardCreator :public Creator
{
public:
virtual Product* Create();
};
class ConcreteCreatorByID :public Creator
{
public:
Product* Create(int id);
};
Product* Creator::Create()
{
return new Product;
}
Product* Creator::Create(int id)
{
/* 基础的根据参数返回,只有当子类中
遇到未定义的id才会到此函数继续执行 */
switch (id)
{
case 3:
return new ConcreteProductA;
break;
case 4:
return new ConcreteProductB;
break;
default:
return Create();
break;
}
}
Product* ConcreteCreatorByID::Create(int id)
{
switch (id)
{
case 1:
return new ConcreteProductA;
break;
case 2:
return new ConcreteProductB;
break;
default:
//id为1、2之外时,会调用父类的Create(id),因此3/4和1/2的效果应该是一样的
//由此可知,可以把部分创建延迟到父类去完成
return Creator::Create(id);
break;
}
}
template <class TheProduct>
Product* StandardCreator<TheProduct>::Create()
{
return new TheProduct;
}
2.3 客户使用
客户使用只需要新建一个具体创造者即可。
(DisplayList 是一个简单的可视化链表的函数)
- 使用模板类实例 ConcreteProductA 的代码如下:
//1. 通过模板类实现具体创建者,获取对应的产品
Creator* creator = new StandardCreator<ConcreteProductA>;
Product* product = creator->Create();
product->add();
ListNode* res = product->GetList();
DisplayList(res);
cout << endl;
- 使用带参数的创建函数获取对应的实例,代码如下:
//2. 在创建者中的工厂方法中引入参数,从而一个建造者可以控制多个产品的建造
creator = new ConcreteCreatorByID;
//id为1、2之外时,会调用父类的Create(id),因此3/4和1/2的效果应该是一样的
product = creator->Create(2);
product->add();
res = product->GetList();
DisplayList(res);
cout << endl;
2.4 运行结果
如图: