工厂模式分三种类型:简单工厂模式、工厂方法模式、抽象工厂模式。
1、开发需求
假设:实现一个项目,需要有多种通信方式,譬如UDP、组播、TCP、CPCI等,为了工作方便,几部分通信方式分别找了几个熟练的程序员进行开发。假设我提供他们的接口为:
//ICommunicate.h
#ifndef ICOMMUNICATE_H
#define ICOMMUNICATE_H
class ICommunicate
{
public:
virtual void start() = 0;
};
#endif
其中,UDP实现为:
//ComunicateUDP.h
#pragma once
#include "icommunicate.h"
#include <iostream>
using namespace std;
class ComunicateUDP :
public ICommunicate
{
public:
ComunicateUDP(void);
~ComunicateUDP(void);
virtual void start();
};
//ComunicateUDP.cpp
#include "StdAfx.h"
#include "ComunicateUDP.h"
ComunicateUDP::ComunicateUDP(void)
{
}
ComunicateUDP::~ComunicateUDP(void)
{
}
void ComunicateUDP::start(){
cout<<"udp start!"<<endl;
}
TCP实现为:
//CommunicateTCP.h
#pragma once
#include "icommunicate.h"
#include <iostream>
using namespace std;
class CommunicateTCP :
public ICommunicate
{
public:
CommunicateTCP(void);
~CommunicateTCP(void);
virtual void start();
};
//CommunicateTCP.cpp
#include "StdAfx.h"
#include "CommunicateTCP.h"
CommunicateTCP::CommunicateTCP(void)
{
}
CommunicateTCP::~CommunicateTCP(void)
{
}
void CommunicateTCP::start(){
cout<<"tcp start!"<<endl;
}
而我们如果需要使用他们的模块,可以在主程序中使用:
// test.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "ICommunicate.h"
#include "ComunicateUDP.h"
#include "CommunicateTCP.h"
int _tmain(int argc, _TCHAR* argv[])
{
ICommunicate * pCom1 = new ComunicateUDP();
ICommunicate * pCom2 = new CommunicateTCP();
pCom1->start();
pCom2->start();
delete pCom1;
delete pCom2;
pCom1 = NULL;
pCom2 = NULL;
return 0;
}
2、进一步考虑,使用简单工厂模式
2.1简单工厂模式的实现
上面的解决方案虽然可以实现功能,但是在使用不同的通信方式时,还存在一个问题:假如,需要使用这几种的通信方式时,还需要在主程序中调用对应的头文件、以及new一个他的实例,这样的话,会大大增加程序的耦合度。
事实上,开发者并不需要知道具体的类的名字、具体在哪个头文件中,而只需要一个通用的接口来实现不同的实例。
// CommunicateFactory.h
#pragma once
#include "ICommunicate.h"
#include "ComunicateUDP.h"
#include "CommunicateTCP.h"
class CommunicateFactory
{
public:
CommunicateFactory(void);
~CommunicateFactory(void);
ICommunicate *createCommunicate(string);
};
// CommunicateFactory.cpp
#include "StdAfx.h"
#include "CommunicateFactory.h"
CommunicateFactory::CommunicateFactory(void)
{
}
CommunicateFactory::~CommunicateFactory(void)
{
}
ICommunicate *CommunicateFactory::createCommunicate(string type)
{
ICommunicate * pCom = NULL;
if(type.compare("UDP") == 0){
pCom = new ComunicateUDP();
}
else if(type.compare("TCP") == 0){
pCom = new CommunicateTCP();
}
else{
// ... ...
}
return pCom;
}
通过一个工厂类,实现了对通信不同实例的封装,而主函数只需要一个接口类、一个工厂类就可以实现其需要的功能。
// test.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "ICommunicate.h"
#include "CommunicateFactory.h"
int _tmain(int argc, _TCHAR* argv[])
{
ICommunicate * pCom;
CommunicateFactory m_CommunicateFactory;
pCom = m_CommunicateFactory.createCommunicate("UDP");
pCom->start();
delete pCom;
pCom = NULL;
return 0;
}
由上,即可以实现了简单工厂模式,简单工厂不是GoF的23种标准设计模式,其实就是一个专门生产不同产品的类,这个类通过传入的参数进行区分要生产的东西!
2.2简单工厂模式的问题
当我们需要增加新的通信方式的时候,需要做的工作有:继承ICommunicate.h接口,然后写一个新的.cpp,并且修改简单工厂类,为其增加一个新的条件分支。后续维护过程中,需要不断完善修改简单工厂模式类。
3、工厂方法模式
工厂方法模式是对简单工厂模式的进一步抽象化,其好处是可以使系统在不修改原来代码的情况下引进新的产品,即满足开闭原则。工厂方法模式主要是将每一个具体产品写一个工厂,取代以前的一个工厂类,主要缺点就是增加了代码量。
其改写方法主要是:
首先,增加一个工厂方法接口:
// IFacory.h
#ifndef IFacory_H
#define IFacory_H
#include "ICommunicate.h"
class IFacory
{
public:
virtual ICommunicate* createCommunicate() = 0;
};
#endif
然后分别实现各个产品的工厂类:
//FacoryUDP.h
#pragma once
#include "ifacory.h"
#include "ComunicateUDP.h"
class FacoryUDP :
public IFacory
{
public:
FacoryUDP(void);
~FacoryUDP(void);
virtual ICommunicate* createCommunicate();
};
//FacoryUDP.cpp
#include "StdAfx.h"
#include "FacoryUDP.h"
FacoryUDP::FacoryUDP(void)
{
}
FacoryUDP::~FacoryUDP(void)
{
}
ICommunicate* FacoryUDP::createCommunicate()
{
return new ComunicateUDP();
}
//FacoryTCP.h
#pragma once
#include "ifacory.h"
#include "CommunicateTCP.h"
class FacoryTCP :
public IFacory
{
public:
FacoryTCP(void);
~FacoryTCP(void);
virtual ICommunicate* createCommunicate();
};
//FacoryTCP.cpp
#include "StdAfx.h"
#include "FacoryTCP.h"
FacoryTCP::FacoryTCP(void)
{
}
FacoryTCP::~FacoryTCP(void)
{
}
ICommunicate* FacoryTCP::createCommunicate()
{
return new CommunicateTCP();
}
最后在使用上进行修改:
// test.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "ICommunicate.h"
//#include "CommunicateFactory.h"
#include "IFacory.h"
#include "FacoryUDP.h"
#include "FacoryTCP.h"
int _tmain(int argc, _TCHAR* argv[])
{
ICommunicate * pCom;
//CommunicateFactory m_CommunicateFactory;
//pCom = m_CommunicateFactory.createCommunicate("UDP");
//pCom->start();
IFacory *iFy;
iFy = new FacoryUDP();
pCom = iFy->createCommunicate();
pCom->start();
delete pCom;
pCom = NULL;
return 0;
}
可以看到,如果需要增加新的产品,不需要在已有的基础上进行修改,只需要进行增加对应的工厂类即可,其在设计上完全符合“开放封闭”原则,但是也带来较大的代码量,增加了类的个数,增加了复杂程度。
4、抽象工厂模式
抽象工厂模式可以较好的实现“开放封闭”原则,有能够有效控制住代码的复杂度,不至于像工厂方法模式那样,每增加一个产品都要增加一个工厂。其表现更像是工厂方法+简单工厂模式的结合!
对于上面的项目,其改造过程为:
首先,定义抽象工厂接口,用来生产不同产品,生产不同产品,既可以通过一个接口传参数来区分,也可以通过不同的接口函数区分。
// IFactoryCommunicate.h
#ifndef IFactoryCommunicate_H
#define IFactoryCommunicate_H
#include "ICommunicate.h"
#include <iostream>
using namespace std;
class IFactoryCommunicate
{
public:
virtual ICommunicate* createCommunicate(string) = 0;
};
#endif
将通信方式定义成网络通信和串口通信两种大的抽象类,TCP、UDP都定义在网络通信工厂中,下面实现以下网络通信工厂函数:
//NetCommunicateFactory.h
#pragma once
#include "IFactoryCommuncate.h"
#include "ComunicateUDP.h"
#include "CommunicateTCP.h"
class NetCommunicateFactory :
public IFactoryCommunicate
{
public:
NetCommunicateFactory(void);
~NetCommunicateFactory(void);
virtual ICommunicate* createCommunicate(string);
};
//NetCommunicateFactory.cpp
#include "StdAfx.h"
#include "NetCommunicateFactory.h"
NetCommunicateFactory::NetCommunicateFactory(void)
{
}
NetCommunicateFactory::~NetCommunicateFactory(void)
{
}
ICommunicate *NetCommunicateFactory::createCommunicate(string type)
{
ICommunicate * pCom = NULL;
if(type.compare("UDP") == 0){
pCom = new ComunicateUDP();
}
else if(type.compare("TCP") == 0){
pCom = new CommunicateTCP();
}
else{
// ... ...
}
return pCom;
}
而使用的方式改为:
// test.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "ICommunicate.h"
#include "IFactoryCommuncate.h"
#include "NetCommunicateFactory.h"
int _tmain(int argc, _TCHAR* argv[])
{
ICommunicate * pCom;
IFactoryCommunicate *iFy;
iFy = new NetCommunicateFactory();
pCom = iFy->createCommunicate("UDP");
pCom->start();
delete pCom;
pCom = NULL;
return 0;
}
由此可以看到,当我们需要增加串口通信相关产品时,实现过程不需要更改网络通信工厂;而如果增加了网络通信产品,又不需要更改串口通信工厂,一定程度上满足了“开放封闭”原则。另外,也不需要每增加一个新的产品都增加对应的工厂,从一定程度上降低了代码的复杂度。