抽象工厂模式(Abstract Factory):
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
也就是说上面我有两个项目A和B,然后每个项目有两个子类1,2。如果是按照简单工厂或者是工厂方法的话,应该是一个大接口出来,然后细分四个子类,然后再在工厂里根据需求实例化哪个类(简单工厂),或者是直接定义工厂抽象接口,然后再分别实现一个工厂子类来负责实现具体的实体(工厂方法),而抽象工厂则的思路则是既然A和B都有方法1和2,那么我们就可以直接在工厂接口里把1和2写死,然后再细分出来两个产品A和B,这样当我们扩展一个产品C的时候(当然这里默认C也是有两个方法1和2),我们只要在对应的产品方面增加出来一个C,然后再在工厂方面增加一个子类就行了,这样的话,两个模块都是增加而不是修改,所以满足OCP,这就是抽象工厂的思路,抽象工厂看上去比较重,我们可以采取反射的技术去优化这个地方,当然简单工厂和工厂方法也可以采取反射的思路(它们两个用反射优化完就长得一样了)。下面通过例子来继续解释这个思路。
需求:有两个产品PC和Phone,同时每个产品都分为两个系统,WIndows和Linux,系统方面以后可能存在扩充,设计一个方便系统扩充的架构思路。通过抽象工厂思路实现上面要求:
UML结构如下:
抽象工厂实现代码:
#pragma once
#include <iostream>
using namespace std;
class CInterfaceOS
{
public:
virtual void StartOs() = 0;
virtual void CloseOs() = 0;
};
class CWindows : public CInterfaceOS
{
public:
void StartOs()
{
cout<<"StartWindowsOs"<<endl;
}
void CloseOs()
{
cout<<"CloseWindowsOs"<<endl;
}
};
class CLinux : public CInterfaceOS
{
public:
void StartOs()
{
cout<<"StartLinuxOs"<<endl;
}
void CloseOs()
{
cout<<"CloseLinuxOs"<<endl;
}
};
class CWindowsPc : public CWindows
{
public:
void StartOs()
{
cout<<"WindowsPc:";
CWindows::StartOs();
}
void CloseOs()
{
cout<<"WindowsPc:";
CWindows::CloseOs();
}
};
class CWindowsPhone : public CWindows
{
public:
void StartOs()
{
cout<<"WindowsPhone:";
CWindows::StartOs();
}
void CloseOs()
{
cout<<"WindowsPhone:";
CWindows::CloseOs();
}
};
class CLinuxPc : public CLinux
{
public:
void StartOs()
{
cout<<"LinuxPc:";
CLinux::StartOs();
}
void CloseOs()
{
cout<<"LinuxPc:";
CLinux::CloseOs();
}
};
class CLinuxPhone : public CLinux
{
public:
void StartOs()
{
cout<<"LinuxPhone:";
CLinux::StartOs();
}
void CloseOs()
{
cout<<"LinuxPhone:";
CLinux::CloseOs();
}
};
class CInterfaceFactory
{
public:
virtual CInterfaceOS * CreatePcOs() = 0;
virtual CInterfaceOS * CreatePhoneOs() = 0;
};
class CCreateFactoryWindows : public CInterfaceFactory
{
public:
CInterfaceOS * CreatePcOs()
{
return new CWindowsPc();
}
CInterfaceOS * CreatePhoneOs()
{
return new CWindowsPhone();
}
};
class CCreateFactoryLinux : public CInterfaceFactory
{
public:
CInterfaceOS * CreatePcOs()
{
return new CLinuxPc();
}
CInterfaceOS * CreatePhoneOs()
{
return new CLinuxPhone();
}
};
客户端调用代码:
// TTT.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "AbstractFactory.h"
#include <iostream>
#include <windows.h>
using namespace std;
int main()
{
CInterfaceFactory *pHashFactory[10] = {0};
CInterfaceOS *pHashOs[10] = {0};
int nHashFactoryId = -1 ,nHashOsId = -1;
pHashFactory[++nHashFactoryId] = new CCreateFactoryWindows();
pHashFactory[++nHashFactoryId] = new CCreateFactoryLinux();
for(int i = 0 ;i <= nHashFactoryId ;i ++)
{
pHashOs[++nHashOsId] = pHashFactory[i]->CreatePcOs();
pHashOs[++nHashOsId] = pHashFactory[i]->CreatePhoneOs();
}
for(int i = 0 ;i <= nHashOsId ;i ++)
{
pHashOs[i]->StartOs();
pHashOs[i]->CloseOs();
}
for(int i = 0 ;i <= nHashFactoryId ;i ++)
{
delete pHashFactory[i];
}
for(int i = 0 ;i <= nHashOsId ;i ++)
{
delete pHashOs[i];
}
return 0;
}
结果:优化:
如上就是抽象工厂的基本实现,比较优雅,当扩充OS的时候是满足OCP的,当然上面的代码如果是扩充硬件,比如增加一个平板,那将是一个无比蛋疼的扩展,所以开发前自己衡量好。还有就是因为下面两个原因,在需要的时候有必要给抽象工厂进行一次优化:
1.抽象工厂本身过于复杂,在方便维护扩展的同时也增加了维护的难度(有可能有人看上去会吃力或者蒙圈)。
2.看上面,在客户端使用的时候客户端要了解所有工厂类,然后决定自己new哪个工厂,这样如果一个系统有100个地方用了一个数据库,那么我们100个地方都写了new但是如果换数据库的时候就算服务方面很容易就扩充了,但是客户端调用方面则要修改100个地方的new。
所以抽象工厂有必要进行优化(简单工厂和工厂方法一样可以优化),采取的方式就是利用反射技术(当然有的语言不支持这个技术),大体思路就是我们把工厂相关的东西全都去掉,比如上面那个就是去掉三个类 CCreateFactoryLinux,CCreateFactoryWindows,CInterfaceFactory。然后增加这个(反射):
const string g_Key = "A";
class CCreateBody
{
CInterfaceOS * CreatePcOs()
{
//根据相关语言进行反射(g_Key为key值)
//return new sg_Key();
}
CInterfaceOS * CreatePhoneOs()
{
//根据相关语言进行反射(g_Key为key值)
//return new sg_Key();
}
};
总结:
最大的好处便是易于交换产品系列,由于具体工厂类,在一个应用中只需要在吃实话的时候出现一次,也就使得改变一个应用的具体工厂变得非常容易,他只需要改变具体工厂即可使用不同的产品配置。可以这么理解,一个东西可以分成两部分,其中一部分是比较固定的,另一部分需要扩展,这个时候可以考虑使用抽象工厂。
它让具体的创建实例过程与客户端分离,客户端通过他们的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户端代码中。
缺点就是过于复杂,并且客户端使用的时候要知道太多的工厂类,耦合度太高,不过还好可以考虑反射优化。