目录
何时选用抽象工厂:强调一系列相关产品接口、联合使用它们的时候
抽象工厂介绍-定义、结构、参考实现、场景
场景
- 完成两套GUI的表示层,一个在PC,另一个在平板、手机上的应用程序界面
- 通常一个显著的设备差别在于分辨率
如何设计
- 从封装的角度来讲,希望根据布局器来配置i我们的控件,我们开放布局器接口和控件接口,供客户端调用
首先来看不使用设计模式的做法:
#include <iostream>
using namespace std;
class FrameApi {
public:
virtual void draw() = 0;
protected:
FrameApi() {}
};
class LayoutApi {
public:
virtual void installFrame() = 0;
protected:
LayoutApi() {}
};
//PC Frame
class ComputerFrame :public FrameApi {
public:
ComputerFrame(int pins) :m_pins(pins) {}
void draw() {
cout << "现在是PC Frame,分辨率适用:" << m_pins << endl;
}
private:
int m_pins;
};
//Mobile Frame
class MobileFrame :public FrameApi {
public:
MobileFrame(int pins) :m_pins() {}
void draw() {
cout << "现在是Mobile Frame,分辨率适用:" << m_pins << endl;
}
private:
int m_pins;
};
//高分辨率布局
class HighLayout :public LayoutApi {
public:
HighLayout(int FrameAdapterPins) :m_FrameAdapterPins(FrameAdapterPins) {}
void installFrame() {
cout << "现在是在PC环境下使用高分辨率布局" << m_FrameAdapterPins << endl;
}
private:
int m_FrameAdapterPins;
};
//低分辨率布局
class LowLayout :public LayoutApi {
public:
LowLayout(int FrameAdapterPins) :m_FrameAdapterPins(FrameAdapterPins) {}
void installFrame() {
cout << "现在是在Mobile环境下使用低分辨率布局" << m_FrameAdapterPins << endl;
}
private:
int m_FrameAdapterPins;
};
//创建产品的简单工厂
class FrameFactory {
public:
static FrameApi* createFrame(int type) {
if (type == 1)return new ComputerFrame(1024);
if (type == 2)return new MobileFrame(800);
return nullptr;
}
};
class LayoutFactory {
public:
static LayoutApi* createLayout(int type) {
if (type == 1)return new HighLayout(1024);
if (type == 2)return new LowLayout(800);
return nullptr;
}
};
class GUIEngineer {
public:
void prepareDraw(int FrameType, int LayoutType) {
this->pFrame = FrameFactory::createFrame(FrameType);
this->pLayout = LayoutFactory::createLayout(LayoutType);
pFrame->draw();
pLayout->installFrame();
}
private:
FrameApi* pFrame;
LayoutApi* pLayout;
};
int main() {
GUIEngineer* pEng = new GUIEngineer;
pEng->prepareDraw(1, 1);
return 0;
}
这样的作法,应该告诉客户:
FrameType是用来选择Frame控件的类型
LayoutType是用来选择布局管理器的类型
如果客户这么做:
pEng->prepareDraw(1, 2);
则:
明显结果不适配。
为了避免这种结果,我们需要进行类似提取公因式的办法,来解决此类问题,把相像的关系进行提取,避免造成关系不适配的情况。
这里就引出抽象工厂的概念:
定义
- 功能:
- 为一系列相关对象或相互依赖的对象创建一个接口
- 从某种意义上看,抽象工厂其实是一个产品系列,或者是产品簇
- 实现:AbstractFactory为接口
- 方法:
- AbstractFactory定义产品所需接口,具体实现在实现类里面。
- AbstractFactory定义的创建产品的方法可以看成是工厂方法,而这些工厂方法的具体实现就延迟到了具体的工厂里面,也就是说用工厂来实现抽象工厂
- 切换产品簇:
- AbstractFactory定义了一个产品簇,因此切换产品簇时提供不同的AbstractFactory就好了
我们将简单工厂替换为抽象工厂,也就是将产品替换为产品簇:
#include <iostream>
using namespace std;
class FrameApi {
public:
virtual void draw() = 0;
protected:
FrameApi() {}
};
class LayoutApi {
public:
virtual void installFrame() = 0;
protected:
LayoutApi() {}
};
//PC Frame
class ComputerFrame :public FrameApi {
public:
ComputerFrame(int pins) :m_pins(pins) {}
void draw() {
cout << "现在是PC Frame,分辨率适用:" << m_pins << endl;
}
private:
int m_pins;
};
//Mobile Frame
class MobileFrame :public FrameApi {
public:
MobileFrame(int pins) :m_pins() {}
void draw() {
cout << "现在是Mobile Frame,分辨率适用:" << m_pins << endl;
}
private:
int m_pins;
};
//高分辨率布局
class HighLayout :public LayoutApi {
public:
HighLayout(int FrameAdapterPins) :m_FrameAdapterPins(FrameAdapterPins) {}
void installFrame() {
cout << "现在是在PC环境下使用高分辨率布局" << m_FrameAdapterPins << endl;
}
private:
int m_FrameAdapterPins;
};
//低分辨率布局
class LowLayout :public LayoutApi {
public:
LowLayout(int FrameAdapterPins) :m_FrameAdapterPins(FrameAdapterPins) {}
void installFrame() {
cout << "现在是在Mobile环境下使用低分辨率布局" << m_FrameAdapterPins << endl;
}
private:
int m_FrameAdapterPins;
};
//AbstractFactory产品簇
class AbstractFactory {
public:
virtual FrameApi* createFrameApi() = 0;
virtual LayoutApi* createLayoutApi() = 0;
protected:
AbstractFactory(){}
};
//产品簇1
class Schemal :public AbstractFactory {
public:
FrameApi* createFrameApi() {
return new ComputerFrame(1024);
}
LayoutApi* createLayoutApi() {
return new HighLayout(1024);
}
};
//产品簇2
class Schema2 :public AbstractFactory {
public:
FrameApi* createFrameApi() {
return new MobileFrame(1024);
}
LayoutApi* createLayoutApi() {
return new LowLayout(1024);
}
};
class AdanvancedGuiEngineer {
public:
void prepareMaterials(AbstractFactory* pSchema) {
this->pFrame = pSchema->createFrameApi();
this->pLayout = pSchema->createLayoutApi();
pFrame->draw();
pLayout->installFrame();
}
private:
FrameApi* pFrame;
LayoutApi* pLayout;
};
int main() {
AdanvancedGuiEngineer* pEng = new AdanvancedGuiEngineer();
pEng->prepareMaterials(new Schemal());
return 0;
}
Nice!
其实就相当于:点套餐,这样就不会出现不适配的情况。
本质:选择产品簇的实现,优缺点
- 优点:
- 分离接口和实现
- 切换产品簇变的容易
- 缺点:
- 不太容易扩展新产品
- 容易造成类层次复杂
何时选用抽象工厂:强调一系列相关产品接口、联合使用它们的时候
抽象工厂模式的工程应用-控件式开发
- 业务功能封装成控件
- 微软有一个叫做asp.net的控件组,他认为对于一个Web应用来说,虽然搜索框、广告条、导航条、页面主题的设计和实现有很大差别,但是绝大多数的Web应用基本布局还是大致相同的。最后都可以组装成一个页面的过程也是大致相同的。
- 这种具有同一属组的产品簇特别适合用抽象工厂进行装配