简单工厂模式是最常用的一种创建型模式,通常所说的工厂模式一般是指工厂方法模式。本篇是是工厂方法模式的“小弟”,我们可以将其理解为工厂方法模式的预备知识,它不属于GoF23种设计模式,但在软件开发中却也应用地比较频繁。此外,工厂方法模式还有一位“大哥”—抽象工厂模式,会在后面进行介绍。
简单工厂模式(Simple Factory) 学习难度:★★☆☆☆ 使用频率:★★★☆☆
1 简单工厂模式概述
1.1 定义
简单工厂(Simple Factory)模式:定义一个工厂类,它可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类。因为在简单工厂模式中用于创建实例的方法是静态(static)方法,因此简单工厂模式又被称为静态工厂方法模式,它属于创建型模式。
简单工厂模式的要点在于:当你需要什么,只需要传入一个正确的参数,就可以获取你所需的对象,而无须知道其创建细节
1.2 通用类方法
简单工厂模式包含3个角色:
1. Factory - 工厂角色:该模式的核心,负责实现创建所有产品实例的内部逻辑,提供一个静态的工厂方法GetProduct(),返回抽象产品类型Product的实例。
2. Product - 抽象产品角色:所有产品类的父类,封装了各种产品对象的共有方法,它的引入将提高系统的灵活性,使得在工厂类中只需要定义一个通用的工厂方法,因为所有创建的具体产品对象都是其子类对象。
3. ConcreteProduct -具体产品角色:简单工厂模式的创建目标,所有被创建的对象都充当这个角色的某个具体类的实例。
在简单工厂模式中,客户端通过工厂类来创建一个产品类的实例,而无须直接使用new关键字来创建对象。(可以看出,它是工厂模式家族中最简单的一员)
2 从女蜗造人说起
女蜗采用黄图捏成人的形状,然后放入八卦炉中烧制,最后放置到大地上生长,工艺过程没有错,但是意外发生了:
- 第一次烤泥人,感觉应该熟了,往大地上一放,哇,没有熟!于是一个白人诞生了!
- 第二次烤泥人,上一次没烤熟,这次多烤了一会,放到世间一看,嘿,熟过头了,于是黑人诞生了!
- 第三次烤泥人,一边烧一边看,知道表皮微黄,嘿,真正好,于是黄种人诞生了
我们定义每个人都有两个方法:getColor(获取人的皮肤颜色)和talk(交谈),其简化结构图如下
抽象产品角色:Human接口
typedef struct _Human Human;
struct _Human
{
void (*getColor)(void);
void (*talk)(void);
};
具体产品角色:接口Human是对人类的总称,每个人种都至少有两种方法,黑色人种,白色人种,黄色人种,其代码清单
typedef struct _BlackHuman BlackHuman;
struct _BlackHuman
{
Human human;
void (*BlackGetColor)(void);
void (*BlackTalk)(void);
void (*BlackHumanDelete)(BlackHuman *pBlackHuman);
};
void BlackGetColor(void)
{
printf("black get color\n");
}
void BlackTalk(void)
{
printf("black talk\n");
}
void BlackHumanDelete(BlackHuman *pBlackHuman)
{
if(!pBlackHuman)
{
free(pBlackHuman);
pBlackHuman = NULL;
}
return;
}
BlackHuman *CreateBlackHuman(void)
{
BlackHuman *pBlackHuman = (BlackHuman *)malloc(sizeof(BlackHuman));
pBlackHuman->BlackGetColor = BlackGetColor;
pBlackHuman->BlackTalk = BlackTalk;
pBlackHuman->BlackHumanDelete = BlackHumanDelete;
pBlackHuman->human.getColor = pBlackHuman->BlackGetColor;
pBlackHuman->human.talk = pBlackHuman->BlackTalk;
return pBlackHuman;
}
黄种人,白种人方法如上,所有的人种定义完毕后,下一步就是定义一个八卦炉,然后烧制人类,女蜗下达的命令
给我一个黄种人,而不是给我一个会走,会跑,会说话,皮肤是黄色的人种
工厂角色:作为生产的管理者,只需要知道生产的是什么,不需要食物的具体信息,那么八卦炉生产人类的方法就可以抽象一个Human的接口
typedef struct _AbstractFactory AbstractFactory;
struct _AbstractFactory
{
Human *(*CreateHuman)(char *pString);
void (*DeleteHumanFactory)(AbstractFactory *pHumanFactory);
};
其抽象类实现过程
Human *CreateHuman(char *pString)
{
Human *pHuman = NULL;
if(0 == strcmp(pString, "Whiteman"))
{
pHuman = (Human *)CreateWhiteHuman();
}
else if(0 == strcmp(pString, "Blackman"))
{
pHuman = (Human *)CreateBlackHuman();
}
else if(0 == strcmp(pString, "Yellowman"))
{
pHuman = (Human *)CreateYellowHuman();
}
else
{
printf("not support\n");
}
return pHuman;
}
void DeleteHumanFactory(AbstractFactory *pHumanFactory)
{
if(NULL != pHumanFactory)
{
free(pHumanFactory);
pHumanFactory = NULL;
}
return;
}
AbstractFactory *CreateHumanFactory(void)
{
AbstractFactory *pAbstractFactory = (AbstractFactory *)malloc(sizeof(AbstractFactory));
if(!pAbstractFactory)
{
return NULL;
}
pAbstractFactory->CreateHuman = CreateHuman;
pAbstractFactory->DeleteHumanFactory = DeleteHumanFactory;
return pAbstractFactory;
}
客户端代码
int main(void)
{
Human *pwhiteHuman = NULL;
Human *pblackHuman = NULL;
AbstractFactory *AbstractFactory = CreateHumanFactory();
pwhiteHuman = AbstractFactory->CreateHuman("Whiteman");
pwhiteHuman->getColor();
pwhiteHuman->talk();
pwhiteHuman = AbstractFactory->CreateHuman("Blackman");
pwhiteHuman->getColor();
pwhiteHuman->talk();
}
3 简单工厂模式的应用
3.1 简单工厂模式的优点
- 良好的封装性,代码结构清晰,一个对象创建时有条件约束的,调用者需要一个具体的产品对象,只需要知道这个产品类名,不需要知道具体的实现细节,降低了代码耦合性
- 工厂模式具体良好的扩展性,在新增产品类的情况下,只要适当修改具体的工厂类和扩展一个工厂类
- 屏蔽产品类,产品如何实现,如何变化,调用者不需要关心,只需要关系产品接口,只要接口保持不变,系统中的上层模块就不要发生改变
- 工厂模式是典型的解耦框架
3.2 简单工厂模式的缺点
- 由于工厂类集中了所有产品的创建逻辑,职责过重,一旦不能正常工作,整个系统都要受影响
3.3 简单工厂模式使用场景
- 工厂模式是new一个对象替代品,在所有需要生产对象的地方都可以使用
- 需要灵活可扩展的框架时,可以考虑采用工厂模式
4 参考资料
秦晓波, 《设计模式之禅》
刘伟,《设计模式的艺术—软件开发人员内功修炼之道》