设计模式-抽象工厂模式
来源:互联网 发布:sql合并查询结果 编辑:程序博客网 时间:2024/06/01 20:14
上次我讲了一下工厂方法模式,这次我来讲一下抽象工厂模式。
其实,我在工作当中基本没有用到过抽象工厂模式。我用的比较多的是工厂方法模式。我对抽象工厂模式的理解主要来自《设计模式》这本书。同时也搜索了一些网上的文章。
这里我就讲一下我自己的理解,如果讲的不好,或者根本就讲错了,请大家谅解。
抽象工厂模式 (Abstract Factory)
意图
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
结构图
从结构图来看,抽象工厂模式主要包括:工厂类和产品类。
我们可以从意图里面看到,抽象工厂模式的主要目的就是“提供一系列创建对象的接口”。
我个人的理解是,抽象工厂模式是比工厂方法模式更加抽象的一种模式。当我们的程序里面有很多产品类时,我们就可以使用抽象工厂模式。
我们还是以上一篇文章里面的跑酷游戏(http://blog.csdn.net/zj510/article/details/8089191)的地形为例子。
之前我们用了工厂方法模式来创建地形,这次我们尝试用抽象工厂模式来创建。
我们这次增加一个新的产品类CWeather,这个类用来表示和地形有关系的天气效果。
先给出类图
从类图我们可以看到我们新增加了一个产品系列,天气类CWeather以及一个工厂类CFactory。
地形类以及地形类里面的背景,地面类和上一个文章一模一样,没有任何改动。
这里直接列出代码
class CComponent{public:virtual void LoadPicture() = 0;};class CTerrain{public:virtual void SetBackground(CComponent* bg){if (m_BackGround){delete m_BackGround;}m_BackGround = bg;}virtual void SetGround(CComponent* ground){if (m_Ground){delete m_Ground;}m_Ground = ground;}CTerrain(){m_BackGround = NULL;m_Ground = NULL;}virtual ~CTerrain(){if (m_BackGround){delete m_BackGround;m_BackGround = NULL;}if (m_Ground){delete m_Ground;m_Ground = NULL;}}protected:CComponent* m_BackGround;CComponent* m_Ground;};class CSnowBackground: public CComponent{public:virtual void LoadPicture(){std::cout<< "Load snow background picture \n"; }};class CForestBackground: public CComponent{public:virtual void LoadPicture(){std::cout<< "Load forest background picture \n"; }};class CSnowGround: public CComponent{public:virtual void LoadPicture(){std::cout<< "Load snow ground picture\n"; }};class CForestGround: public CComponent{public:virtual void LoadPicture(){std::cout<< "Load forest ground picture\n"; }};
接下来给出天气类CWeather,这个类里面只有一个函数Effect()
class CWeather{public:virtual void Effect() = 0;};class CSunWeather: public CWeather{public:virtual void Effect(){std::cout<< "Add sun effect \n";}};
这里我们只用一个子类CSunWeather来作为示例。这个类显示一个太阳效果。
然后看关键的工厂类:
class CFactory{public://生产一个Terrain对象virtual CTerrain* MakeTerrain(){return new CTerrain();}virtual CComponent* MakeBackground() = 0;virtual CComponent* MakeGround() = 0;//生产一个天气对象virtual CWeather* MakeWeather() = 0;};class CSnowSunFactory: public CFactory{public:virtual CComponent* MakeBackground(){return new CSnowBackground();}virtual CComponent* MakeGround(){return new CSnowGround();}virtual CWeather* MakeWeather(){return new CSunWeather();}static CSnowSunFactory& GetInst(){static CSnowSunFactory factory;return factory;}private:CSnowSunFactory(){}};
工厂类总共提供了4个接口,用来创建4个产品地形,背景,地面和天气。
其中地形类组合了背景和地面。
背景和地面是同一个层面的类。
天气是另外的一个类。
子工厂类CSnowSunFactory实现了这4个接口,然后生产雪地背景,雪地地面和太阳效果。由雪地背景和雪地地面来组成地形实例。
ok,现在再来看看CCreator类,这个类和上一个文章(工厂方法模式)里面的CCreator是一样的职责,这里我不过是改了成员函数Create的参数,主要就是用来传递一个factory实例进去,这样CCreator可以根据factory参数来创建相应的产品。看代码:
class CCreator{public:void Create(CFactory& factory, CTerrain** t, CWeather** w){CTerrain* terrain = factory.MakeTerrain();CComponent* bg = factory.MakeBackground();CComponent* ground = factory.MakeGround();bg->LoadPicture();ground->LoadPicture();terrain->SetBackground(bg);terrain->SetGround(ground);CWeather* weather = factory.MakeWeather();*t = terrain;*w = weather;}};
ok, Create()函数返回2个对象,地形和天气。
其中地形部分和工厂方法模式中的一模一样,只是增加了天气对象。
(对于CTerrain对象,我们这里没有子类化,其实也是可以。)
客户端代码:
CSnowSunFactory& factory = CSnowSunFactory::GetInst();CCreator creator;CTerrain* terrain = NULL;CWeather* weather = NULL;creator.Create(factory, &terrain, &weather);weather->Effect();delete terrain;delete weather;
上面的代码例子创建了一个雪地地形配合下雪天气。
仔细的把抽象工厂模式和工厂方法模式相比,我们会发现,其实抽象工厂模式只是增加了一个工厂类CFactory,然后把CCreator里面的工厂方法移到了工厂类中。
然后通过传递一个参数,把CFactory的实例传到CCreator里面。
如果我们要增加森林配合下雨天气的场景,那么只需要增加一个新的工厂类,CForestRainFactory,然后实现相应代码(省略,实现跟CSnowSunFactory差不多)。
然后这样调用来创建森林,下雨效果。
CForestRainFactory& factory = CForestRainFactory::GetInst();CCreator creator;CTerrain* terrain = NULL;CWeather* weather = NULL;creator.Create(factory, &terrain, &weather);weather->Effect();delete terrain;delete weather;
通常我们把工厂类设计成单例的,因为一个工厂类通常只生产一个特定的产品系列。
那么抽象工厂模式有什么优点呢?
1. 首先,当我们的需求里面产品系列比较多或者复杂的时候,用一个专门的CFactory类来管理会比较好。这样实现了客户类和产品创建的分离;
2. 很容易更改一个工厂,从而更改所有的产品,因为一个工厂类实现了一个完整的产品体系;
3. 一个工厂类封装了一个完整的产品体系,那么也就是说一个应用一次只能使用同一个系列中的产品(由当前的工厂类决定)。
当然抽象工厂模式也有个很明显的缺点:
难以支持新的产品。 看上面的例子,CFactory里面只提供了4个接口。如果要支持新的产品,就得增加一个接口,这就会影响所有的子类。
这里有一个办法,通过参数来控制,比如我们增加一个接口:virtual void* MakeMagicProduct() = 0;
不同的工厂之类可以返回不同的产品。那么这里有个问题,对于调用者来说,怎么知道返回的是个什么类型呢?
比如:
void Create(CFactory& factory){ void* p = factory.MakeMagicProduct();}
那么怎么知道p是什么的?这个好像比较困难。如果用强行转换的话,感觉不太安全,或者不具有通用性。
我的理解是,真要这么做的话,就得小心一点。不然就直接改接口吧。
那么什么时候用工厂方法模式,什么时候用抽象工厂模式呢?这里引用书上对于什么时候使用抽象工厂模式的原话:
1. 一个系统要独立于它的产品的创建,组合和表示时;
2. 一个系统要由多个产品系列中的一个来配置时;
3. 当你要强调一系列相关的产品对象的设计以便进行联合使用时;
4. 当你提供一个产品类库,而只想显示他们的的接口而不是实现时。
上面的4点需要细细体会,其中第二点是最容易理解的,一个配置就是一个CFactory子类的实例。然后另外3点,我觉得可以简单理解为产品结构比较复杂并且有一定联系(比如地形是和天气联系在一起的)的时候。
回到跑酷例子,因为产品体系不复杂,而且我的那个小游戏里面,游戏场景是可以事先设定的,也就是说当玩家登录游戏主界面的时候,使用什么地形已经决定了。然后里面有一个CCreator的数据成员来专门创建各种地形,所以我的做法是每次登录主界面的时候就相应创建一个CCreator的子类的实例,然后赋值给CCreator数据成员。CCreator并不知道要创建什么背景,地面,这些是在CCreator的子类里面决定。所以这个地方用了工厂方法模式。
当然我们也可以用抽象工厂模式来实现。只是我觉得用工厂方法模式已经足够了。
这里再重复一次G4的一个建议:通常,设计以使用Factory Method开始,并且当设计者发现需要更大的灵活性时,设计便会向其他创建型模式演化。
- 抽象工厂设计模式
- 设计模式-----抽象工厂
- 抽象工厂设计模式
- 抽象工厂设计模式
- 抽象工厂设计模式
- 设计模式 抽象工厂
- 设计模式--抽象工厂
- 设计模式--抽象工厂
- 设计模式 抽象工厂
- 设计模式-抽象工厂
- 设计模式-> 抽象工厂
- 抽象工厂设计模式
- 设计模式--抽象工厂
- 【设计模式】抽象工厂
- 设计模式---抽象工厂
- 【设计模式-抽象工厂】
- 设计模式-抽象工厂
- 设计模式--抽象工厂
- windows 下安装 eclipse for c /C++出现的问题
- java连接MYSQL
- 跟燕十八学习PHP-第十九天-热身项目完善
- 开源传感器网络平台OpenWSN
- linux 时间格式化函数strftime和strptime使用
- 设计模式-抽象工厂模式
- BUG:使用android:layout_weight权重导致对齐出错
- Ubuntu安装中文语言包
- Shell分析access_log
- 理解inode笔记
- 【小技巧】Select下拉列表覆盖DIV菜单的另类解决方案
- Sharepoint 性能考量
- AWStats+QQ纯真库IP解析插件安装
- JQUERY弹框效果,背景变暗,可以自行添加弹框的动画效果