简单工厂和工厂方法都集中封装了对象的创建过程,使得要更改对象时,不需做大的改动就能实现,降低了客户程序与产品对象的耦合。
//不使用工厂进行对象创建
class Computer
{
public:
virtual void show() = 0;
};
class DellComputer : public Computer
{
public:
void show()
{
std::cout << "This is Dell computer!" << endl;
}
};
class HaseeComputer: public Computer
{
public:
void show()
{
std::cout << "This is Haseecomputer!" << endl;
}
};
int main()
{
Computer *pComputer = NULL;
if(客户端需要HaseeComputer)
pComputer = new HaseeComputer;
else if(客户端需要DellComputer)
pComputer = new DellComputer;
else
...... //当有其他电脑品牌的类时就需要修改main函数中的代码,违背了对扩展开放,对修改关闭的原则
pComputer->show();
return 0;
}
使用简单工厂,增加新类SimpleFactory
class SimpleFactory
{
public:
Computer *CreateComputer(string computer)
{
if(computer == "HaseeComputer")
pComputer = new HaseeComputer;
else if(computer == "DellComputer")
pComputer = new DellComputer;
else
...... //当有其他电脑品牌的类时就需要修改main函数中的代码,违背了对扩展开放,对修改关闭的原则
}
}
使用简单工厂后,main函数则改为
int main()
{
SimpleFactory *pFactory = new SimpleFactory;
pFactory->CreateComputer("HaseeComuter");
return 0;
}
可以看出,使用简单工厂后的main函数,不需要执行具体对象创建的细节,也就是无需在主函数中做判断,取而代之的是定义一个简单工厂类SimpleFactory,并通过SimpleFactory中的CreateComputer函数返回一个对象,对象的类型通过传参的方式进入函数中。调用者只需要对参数进行修改即可实现创建不同对象,减少了代码修改程度。 但反观简单工厂类SimpleFactory可知,简单工厂与普通的创建对象方法的区别就是简单工厂把之前在main函数中的判断过程移到了其内部实现。这样虽然可以减少客户端的代码量,但是如果有新的电脑型号出现时,就需要更改SimpleFactory的内容,增加新的判断,破坏了对修改关闭的原则。 为了解决上述问题,引入工厂方法。 使用工厂方法解决上述问题,需要引入新的类。
class ComputerFactory //这个类相当于简单工厂类SimpleFactory,只不过将简单工厂中的实现延迟到了子类
{ //只把实现的接口定义出来
virtual Computer *CreateComputer() = 0; //定义为纯虚函数,在子类对该函数进行实现。
};
class DellComputerFactory
{
Computer *CreateComputer()
{
return new DellComputer;
}
};
class HaseeComputerFactory
{
Computer *CreateComputer()
{
return new HaseeComputer;
}
};
这时主函数的写法如下
int main()
{
ComputerFactory *pFactory = new HaseeComputerFactory; //根据需要定义对应工厂
Computer *pComputer = pFactory->CreateComputer();
pComputer->show(); //显示This is Haseecomputer!
return 0;
}
注意到此时如果需要添加新的电脑品牌,只需要添加一个对应的产品类和相应的产品工厂类即可,而无需对现有的类进行修改,实现了对拓展的开放和对修改的关闭。
抽象工厂模式 #
现在考虑这样一个问题,Dell 和 Hasee 两个厂商不可能只有电脑这一种类型的产品,假如现在增加了一个产品类型显示器,则需要增加一个抽象类 Monitor 和两个子类 DellMonitor 和 HaseeMonitor 。
class Monitor
{
public :
virtual void show() = 0;
};
class DellMontor : public Monitor
{
public:
void show()
{
std::cout << "This is a Dell Monitor" << endl;
}
};
class HaseeMontor : public Monitor
{
public:
void show()
{
std::cout << "This is a Hasee Monitor" << endl;
}
};
增加产品类之后,可将工厂方法中的三个工厂改为如下形式
class Factory
{
virtual Computer *CreateComputer() = 0;
virtual Monitor *CreateMonitor() = 0;
};
class DellComputerFactory
{
Computer *CreateComputer()
{
return new DellComputer;
}
Monitor *CreateMonitor()
{
return new DellMontor;
}
};
class HaseeComputerFactory
{
Computer *CreateComputer()
{
return new HaseeComputer;
}
Monitor *CreateMonitor()
{
return new HaseeMontor;
}
};
这样一个抽象工厂就产生了。类Factory作为抽象工厂的接口类,其子类产生具体的实例化对象。 通过上面三个类的定义可以发现,抽象工厂中每一个产品的创建过程都是一个工厂方法。 Computer和Monitor构成一个产品族。在抽象工厂中,一个实例化的工厂可以创建一个完整的产品族,产品族中的每个产品出于不同的产品结构。 产品等级结构:即产品的继承结构。例如Computer类和他的子类DellComputer及HaseeComputer构成了一个产品等级结构。而Computer和Monitor自然出于不同的产品等级结构。 产品族:处于不同产品等级结构的一族产品的合集。
这时在主函数中可以这样实现
int main()
{
Factory *pFactory = new DellFactory; //假设创建一个Dell系列产品
Computer *pComputer = pFactory->CreateComputer();
Monitor *pMonitor = pFactory->CreateMonitor();
pComputer->show(); //This is DellComputer!
pMonitor ->show(); //This is DellMonitor!
return 0;
}
从客户端代码可以看出,抽象工厂的好处是对同一产品族的产品进行了统一的设计。这里体现为对Dell厂商的产品进行了封闭的设计,保证了产品族中产品的统一。
如果在产品中新增一个继承父类的子类厂商Mac,那么需要增加一个MacFactory来创建MacComputer和MacMonitor。
抽象工厂也存在一定的缺点 #
当添加一个产品时,需要修改很多代码。在上个例子中,如果增加一个产品为Mouse,那么在增加新产品的同时还需要更改Factory及其子类的内容,增加CreateMouse的工厂方法。
从上面的分析可以看出:(1)增加一个产品的继承结构如Mac只需要增加新的MacFactory并对其实现即可,属于结构拓展。 (2)但如果增加一个新的产品如Mouse,则需要对现有的工厂进行修改,这破坏了开闭原则,这也是抽象工厂的缺点所在。
针对抽象工厂的缺点进行如下改进。