面向对象设计——抽象工厂(Abstract Factory)模式
1 前言
工厂模式分类:
抽象工厂(Abstract Factory):提供一个创建一系列相关或者相互依赖对象的接口,而无需指定它们具体的类。抽象工厂允许客户使用抽象的接口来创建一组相关的产品,而不需要知道或关心实际产出的具体产品是什么。这样一来,客户就能从具体的产品中被解耦。
工厂方法(Factory Method):定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
简单工厂(Simple Factory):简单工厂其实不是一个设计模式,比较像一种编程习惯,就是提供一个静态函数,根据不同的参数动态创建同一个类的不同子类对象。
工厂模式针对的是一个产品等级结构 ,抽象工厂模式针对的是面向多个产品等级结构的。
2 抽象工厂
2.1 定义
提供一个创建一系列相关或者相互依赖对象的接口,而无需指定它们具体的类。抽象工厂允许客户使用抽象的接口来创建一组相关的产品,而不需要知道或关心实际产出的具体产品是什么。这样一来,客户就能从具体的产品中被解耦。
2.2 适用场景
在以下情况可以使用Abstract Factory模式
- 一个系统要独立于它的产品的创建、组合和表示时
- 一个系统要由多个产品系列中的一个来配置时
- 当你要强调一系列相关的产品对象的设计以便进行联合使用时
- 当你提供一个产品类库,而只想显示它们的接口而不是实现时
2.3 UML图
抽象工厂模式UML图a)
- AbstractFactory
——声明一个创建抽象产品对象的操作接口。
- ConcreteFactory
——实现创建具体产品对象的操作。
- AbstractProduct
——为一类产品对象声明一个接口。
- ConcreteProduct
——定义一个将被相应的具体工厂创建的产品对象。
——实现AbstractProduct接口。
在这里指的是ProductA1,ProductA2,ProductB1,ProductB2。
- Client
——仅适用由AbstractFactory和AbstractProduct类声明的接口。
一般而言,有多少个产品等级结构,就会在工厂角色中发现多少个工厂方法。每一个产品等级结构中有多少个具体的产品,就有多少个产品族,也就会在工厂等级结构中发现多少个具体工厂。
2.4 代码示例
《设计模式:可复用面向对象软件的基础》中一书中,给出了一个应用工厂方法设计迷宫的例子,这里修改为应用抽象工厂模式来实现。
首先,给出了枚举类型Direction表明东南西北四个方向,定义抽象产品AbstractProduct接口,包含一个纯虚函数Enter,用于在迷宫间移动位置。
- enum Direction {North, South, East, West};
- //AbstractProduct
- class MapSite{
- public:
- virtual void Enter() = 0;
- };
接下来,基于抽象产品接口MapSite实现ConcreteProduct具体产品类:
定义迷宫房间Room类(ProductA);
- //ProductA
- class Room : public MapSite{
- public:
- Room(int roomNo);//初始化房间号
- MapSite *GetSide(Direction) const;//获取当前房间四周具体产品ConcreteProduct类MapSite
- void SetSide(Direction, MapSite*);//设置当前房间四周具体产品ConcreteProduct类MapSite
- virtual void Enter();
- private:
- MapSite* _side[4];
- int _roomNumber;
- };
定义迷宫的墙Wall类(ProductB);
- //ProductB
- class Wall : public MapSite{
- public:
- Wall();
- virtual void Enter();
- };
定义迷宫的门Door类(ProductC);
- //ProductC
- class Door : public MapSite{
- public:
- Door(Room * = 0, Room * = 0);//初始化门两边的房间
- virtual void Enter();
- Room* OtherSideFrom(Room *);
- private:
- Room * _room1;
- Room * _room2;
- bool _isOpen;
- };
定义迷宫Maze类(ProductD);
- //ProductD
- class Maze{
- public:
- Maze();
- void AddRoom(Room *);
- Room* RoomNo(int) const;
- private:
- //...
- };
在完成抽象产品接口和具体产品类定义后,接下来定义抽象工厂接口AbstractFactory类MazeFactory,对外提供接口,用于生成上述定义的具体产品类ProductA——Room,ProductB——Wall,ProductC——Door,ProductD——Maze。
- //AbstractFactory
- class MazeFactory{
- public:
- MazeFactory();
- virtual Maze* MakeMaze() const
- {return new Maze;}
- virtual Wall* MakeWall() const
- {return new Wall;}
- virtual Room* MakeRoom(int n) const
- {return new Room(n);}
- virtual Door* MakeDoor(Room* r1, Room* r2) const
- {return new Door(r1,r2);}
- };
在定义好抽象工厂接口、抽象产品接口、具体产品后,就可以由Client来动态创建和处理各个对象的关系,这里创建一个包含两个房间的简单迷宫。
- //Client
- Maze * MazeGame::CreateMaze(MazeFactory &factory){
- Maze * aMaze = factory.MakeMaze();
- Room *r1 = factory.MakeRoom(1);
- Room *r2 = factory.MakeRoom(2);
- Door *aDoor = factory.MakeDoor(r1,r2);
- aMaze->AddRoom(r1);
- aMaze->AddRoom(r2);
- r1->SetSide(North, factory.MakeWall());
- r1->SetSide(East, aDoor);
- r1->SetSide(South,factory.MakeWall());
- r1->SetSide(West, factory.MakeWall());
- r2->SetSide(North, factory.MakeWall());
- r2->SetSide(East, factory.MakeWall());
- r2->SetSide(South, factory.MakeWall());
- r2->SetSide(West, aDoor);
- return aMaze;
- }
如果需要创建带各种特效或者属性的Room、Door、Wall ,只需要继承具体产品类ProductA、ProductB、ProductC、ProductD,以及抽象工厂AbstractFactory接口MazeFactory。
如果要生成魔法迷宫,只需要继承MazeFactory生成ConcreteFactory类EnchantedMazeFacotry,
- //ConcreteFactory
- class EnchantedMazeFactory:public MazeFactory{
- public:
- EnchantedMazeFacotry();
- virtual Room* MakeRoom(int n) const
- {return new EnchantedRoom(n, castSpell());}
- virtual Door* MakeDoor(Room* r1, Room* r2) const
- {return new DoorNeedingSpell(r1,r2);}
- protected:
- Spell* CastSpell() const;
- };
调用CreateMaze就可以生成带魔法房间和符咒门的迷宫了。。。
先创建一个工厂生产器FactoryProducer:
- MazeFactory *FactoryProducer(string type){
- if(type == "Maze"){
- return new MazeFactory;
- }
- else if(type == "EnchantedMaze"){
- return new EnchantedMazeFactory;
- }
- }
客户端只需要针对抽象接口编程即可:
- MazeFacoty *factory = FactoryProducer("Maze");
- Maze *maze = CreateMaze(factory);
- factory = FactoryProducer("EnchantedMaze");
- maze = CreateMaze(factory);
同理还可以,由爆炸工厂生成带爆炸门和爆炸房间的迷宫。。。
- Wall * BombedMazeFactory::MakeWall() const {
- return new BombedWall;
- }
- Room* BombedMazeFactory::MakeRoom(int n) const{
- return new RoomWithABomb(n);
- }
如果嫌上面例子麻烦, 下面这个例子(截图自wikipedia官网),简单明了的阐释了抽象工厂模式:
2.5 优缺点
优点:
1、抽象工厂模式隔离了具体类的生产,使得客户并不需要知道什么被创建。
2、当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。
3、增加新的具体工厂和产品族很方便,无须修改已有系统,符合“开闭原则”。
缺点:
增加新的产品等级结构很复杂,需要修改抽象工厂和所有的具体工厂类,对“开闭原则”的支持呈现倾斜性。
3 工厂方法
3.1 定义
定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
3.2 适用场景
在下列情况下可以使用Factory Method模式:
- 当一个类不知道它所必须创建的对象的类的时候。
- 当一个类希望由它的子类来指定它所创建的对象的时候。
- 当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。
3.3 UML图
UML结构
- Product
——定义工厂方法所创建的对象的接口。
- ConcreteProduct
——实现Product接口。
- Creator
——声明工厂方法,该方法返回一个Product类型的对象。Creator也可以定义一个工厂方法的却省实现,它返回一个缺省的ConcreteProduct对象。
——可以调用该工厂方法以创建一个Product对象。
- ConcreteCreator
——重定义工厂方法以返回一个ConcreteProduct实例。
3.4 代码示例
与抽象工厂不同的时,在抽象工厂中,只需要将抽象工厂接口Factory传到client中,然后就能生成不同的Product,然而在工厂方法中,是通过Creator来实现的。Creator中FactoryMethod是一些产品虚函数,用于生成具体的产品的,而AnOperation成员函数,相当于抽象工厂中的Client,调用ConcreteCreator的虚函数FactoryMethod来生成真正的产品。
修改抽象产品中迷宫例子,将CreateMaze函数和MazeFactory合并到一块,并将CreateMaze参数去掉,改名为MazeGame,这样就是工厂方法的实现了,这里MazeGame就是Creator,如果需要扩展生成更多的产品,只需要继承MazeGame,实现ConcreteCreator,然后重载虚函数FactoryMethod,就能创建更多的产品。
- class MazeGame{
- public:
- Maze * CreateMaze();
- //factory method
- virtual Maze* MakeMaze() const
- {return new Maze;}
- virtual Wall* MakeWall() const
- {return new Wall;}
- virtual Room* MakeRoom(int n) const
- {return new Room(n);}
- virtual Door* MakeDoor(Room* r1, Room* r2) const
- {return new Door(r1,r2);}
- };
CreateMaze是AnOperation的具体实现。
- Maze * MazeGame::CreateMaze(){
- Maze * aMaze = MakeMaze();
- Room *r1 = MakeRoom(1);
- Room *r2 = MakeRoom(2);
- Door *aDoor = MakeDoor(r1,r2);
- aMaze->AddRoom(r1);
- aMaze->AddRoom(r2);
- r1->SetSide(North, MakeWall());
- r1->SetSide(East, aDoor);
- r1->SetSide(South,MakeWall());
- r1->SetSide(West, MakeWall());
- r2->SetSide(North, MakeWall());
- r2->SetSide(East, MakeWall());
- r2->SetSide(South, MakeWall());
- r2->SetSide(West, aDoor);
- return aMaze;
- }
EnchantedMazeGame是ConcreteCreator的具体实现。
- class EnchantedMazeGame:public MazeGame{
- public:
- EnchantedMazeFacotry();
- virtual Room* MakeRoom(int n) const
- {return new EnchantedRoom(n, castSpell());}
- virtual Door* MakeDoor(Room* r1, Room* r2) const
- {return new DoorNeedingSpell(r1,r2);}
- protected:
- Spell* CastSpell() const;
- };
Client生成具体的产品调用方式为:
- //Factory Producer
- MazeGame *CreateMaze(string type){
- if(type == "Maze"){
- return new MazeGame;
- }
- else if(type == "EnchantedMaze"){
- return new EnchantedMazeGame ;
- }
- }
- //Creator
- MazeGame *creator= CreateMaze("Maze");
- Maze *maze = creator->CreateMaze();
- //ConcreteCreator
- creator= CreateMaze("EnchantedMaze");
- maze = creator->CreateMaze();
3.5 优缺点
优缺点除了抽象工厂的外,不能很好的支持多种产品。
4 简单工厂
4.1 定义
简单工厂其实不是一个设计模式,比较像一种编程习惯,就是提供一个静态函数,根据不同的参数动态创建同一个类的不同子类对象。
4.2 适用场景
4.3 UML图
- Product
——产品类,具有一些有用的实现,这些实现是虚函数,可以被子类覆盖。
- ProductFactory
——产品工厂,是一个静态函数,根据不同的参数信息,生成不同的产品对象。
4.4 代码示例
MazeGame和EnchantedMazeGame为UML图中的Product和ConcreteProduct。
- class MazeGame{
- public:
- virtual Maze* MakeMaze() const
- {return new Maze;}
- virtual Wall* MakeWall() const
- {return new Wall;}
- virtual Room* MakeRoom(int n) const
- {return new Room(n);}
- virtual Door* MakeDoor(Room* r1, Room* r2) const
- {return new Door(r1,r2);}
- };
- class EnchantedMazeGame:public MazeGame{
- public:
- EnchantedMazeFacotry();
- virtual Room* MakeRoom(int n) const
- {return new EnchantedRoom(n, castSpell());}
- virtual Door* MakeDoor(Room* r1, Room* r2) const
- {return new DoorNeedingSpell(r1,r2);}
- protected:
- Spell* CastSpell() const;
- };
用简单工厂的方法重写CreateMaze如下,根据不同的type类型创建不同的迷宫:
- Maze * CreateMaze(string type){
- Maze * aMaze = NULL;
- if(type == "Maze"){
- aMaze = new MazeGame;
- }
- else if(type == "EnchantedMaze"){
- aMaze = new EnchantedMazeGame;
- }
- Room *r1 = aMaze->MakeRoom(1);
- Room *r2 = aMaze->MakeRoom(2);
- Door *aDoor = aMaze->MakeDoor(r1,r2);
- aMaze->AddRoom(r1);
- aMaze->AddRoom(r2);
- r1->SetSide(North, MakeWall());
- r1->SetSide(East, aDoor);
- r1->SetSide(South,MakeWall());
- r1->SetSide(West, MakeWall());
- r2->SetSide(North, MakeWall());
- r2->SetSide(East, MakeWall());
- r2->SetSide(South, MakeWall());
- r2->SetSide(West, aDoor);
- return aMaze;
- }
4.5 优缺点
缺点是为违背了编码“高内聚低耦合”的原则,将很多细节暴露了给客户,如CreateMaze中函数,房间、墙、门是如何组成迷宫的细节都暴露了。
5 如何选择三种设计模式
参考资料:
a) https://en.wikipedia.org/wiki/Abstract_factory_pattern
b) 《设计模式:可复用面向对象软件的基础》
c) http://www.runoob.com/design-pattern/abstract-factory-pattern.html
d) https://zh.wikipedia.org/wiki/%E6%8A%BD%E8%B1%A1%E5%B7%A5%E5%8E%82
面向对象设计——抽象工厂(Abstract Factory)模式的更多相关文章
- Java 工厂模式(一)— 抽象工厂(Abstract Factory)模式
一.抽象工厂模式介绍: 1.什么是抽象工厂模式: 抽象工厂模式是所有形态的工厂模式中最为抽象和最具有一般性的一种形态,抽象工厂模式向客户端提供一个接口,使得客户端在不知道具体产品的情类型的情况下,创建 ...
- 抽象工厂(Abstract Factory),工厂方法(Factory Method),单例模式(Singleton Pattern)
在谈工厂之前,先阐述一个观点:那就是在实际程序设计中,为了设计灵活的多态代码,代码中尽量不使用new去实例化一个对象,那么不使用new去实例化对象,剩下可用的方法就可以选择使用工厂方法,原型复制等去实 ...
- 5、抽象工厂 abstract factory 将关联组件组成产品 创建型模式
趁热打铁,紧跟着上一节的工厂方法模式.这一节介绍一下抽象工厂模式,以及分析俩个模式的不同 1.何为抽象模式? 抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他 ...
- 工厂模式[3] 抽象工厂 Abstract Factory
简介 1.简单工厂,或静态工厂,产品接口 定义:专门定义一个类来负责创建其他类的实例,被创建的实例通常具有共同的父类或实现同一接口 优点:客户端可以直接消费产品,而不必关心具体产品的实现(不关心对象的 ...
- 设计模式——抽象工厂(Abstract Factory)
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们的具体类. ——DP UML类图 模式说明 抽象工厂与工厂方法在定义上最明显的区别是“创建一系列相关或相互依赖对象的接口”,由此可以看出抽象工 ...
- 设计模式四: 抽象工厂(Abstract Factory)
简介 抽象工厂模式是创建型模式的一种, 与工厂方法不同的是抽象工厂针对的是生产一组相关的产品, 即一个产品族. 抽象工厂使用工厂方法模式来生产单一产品, 单一产品的具体实现分别属于不同的产品族. 抽象 ...
- 【设计模式】——抽象工厂Abstract Factory
模式意图 提供对象的使用接口,隐藏对象的创建过程. 模式结构 AbstractFactory 提供创建对象的接口. ConcreteFactory 提供真正创建对象的实现类,用于组合并创建不同的对象, ...
- C2:抽象工厂 Abstract Factory
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类. 应用场景: 一系列相互依赖的对象有不同的具体实现.提供一种“封装机制”来避免客户程序和这种“多系列具体对象创建工作”的紧耦合 UM ...
- Headfirst设计模式的C++实现——抽象工厂(Abstract Factory)
Dough.h #ifndef _DOUGH_H #define _DOUGH_H class Dough { }; #endif ThinCrustDough.h #ifndef _THIN_CRU ...
随机推荐
- Java IO学习笔记三
Java IO学习笔记三 在整个IO包中,实际上就是分为字节流和字符流,但是除了这两个流之外,还存在了一组字节流-字符流的转换类. OutputStreamWriter:是Writer的子类,将输出的 ...
- thinkphp5.0学习笔记(三)获取信息,变量,绑定参数
1.构造函数: 控制器类必须继承了\think\Controller类,才能使用: 方法_initialize 代码: <?php namespace app\lian\controller; ...
- MongoDB-配置翻译
Configuration File(配置文件) File Format(文件格式) Use the Configuration File(使用配置文件) Core Options(核心设置) sys ...
- jenkins构建后操作添加“Publish to Subversion repository”与Eclipse更新提交SVN文件冲突
jenkins配置环境信息: 1.安装“SVN Publisher plugin”插件: 2.在系统管理-系统设置中“Global SVN Publisher Settings” 填写信息:
- Example012点击修改属性
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 微软虐我千百遍——记一次比较漫长的TFS数据库迁移
起因 七月三日早晨刚到公司,同事就跟我讲TFS开始返回 TF30042错误,报告数据库已满.按照处理问题的第一直觉,我上bing的英文网站搜了一下,发现是部署TFS的时候使用的SQL Express限 ...
- 消息队列NetMQ 原理分析4-Socket、Session、Option和Pipe
消息队列NetMQ 原理分析4-Socket.Session.Option和Pipe 前言 介绍 目的 Socket 接口实现 内部结构 Session Option Pipe YPipe Msg Y ...
- Spring 极速集成注解 redis 实录
Redis 做为基于内存的 Key-Value 数据库,用来做缓存服务器性价比相当高. 官方推出的面向 Java 的 Client Jedis,提供了很多接口和方法,可以让 Java 操作使用 Red ...
- Exception in thread "main" java.lang.NoClassDefFoundError: javax/transaction/Synchronization
解决办法:原因是缺少jta.jar包,添加jta.jar包就好
- MySQL慢查询日志
实验环境: OS X EI Captian + MySQL 5.7 一.配置MySQL自动记录慢查询日志 查看变量,也就是配置信息 show (global) variables like '%slo ...