首页 > 代码库 > 面向程序员的设计模式 ——GoF《设计模式》读书总结(壹)抽象工厂&生成器
面向程序员的设计模式 ——GoF《设计模式》读书总结(壹)抽象工厂&生成器
第一部分:创建型模式
创建型模式抽象了实例化过程。它们帮助一个系统独立于如何创建、组合和表示它的那些对象。(把一些小的对象组装成大对象,这个工作由专门的类对象来做)
一个类创建型模式使用继承改变被实例化的类,而一个对象创建型模式将实例化委托给另一个对象。
随着系统演化得越来越依赖于对象复合而不是类继承,创建型模式变得更为重要。因为系统定义一个较小的行为集,这些行为可以被组合成任意数目的更复杂的行为。
(把行为分散在各类中,用对象组合的方式,根据需要组合成大类)
这些创建型模式有两个特点:
1、它们都将关于该系统使用哪些具体的类的信息封装起来。
2、它们隐藏了这些类的实例是如何被创建和组装在一起的。
创建型模式在什么被创建,谁创建它,怎样被创建,以及何时创建这些方面给你很大的灵活性。
(一)、抽象工厂(Abstract Factory)——对象创建型模式
意图:
提供一个 创建一系列相关或相互依赖对象的 接口,而无需指定它们具体的类。(用一个工厂类创建系统中相关连的对象)
引子:
考虑一个支持多种视感风格的用户界面工具包。不同的视感风格为滚动条、窗口和按钮等窗口组件定义不同的外观和行为。为保证视感风格间的可移植性,一个应用不应该为一个特定的视感外观编码它的窗口组件。
(考虑系统中哪些是未来可能变化的,一组相关的窗口组件。那么我们就封装变化,专门定义一个类来生成这一组相关的窗口组件。)
为解决这一问题,我们可以定义一个抽象的WidgetFactory类,这个类声明了一个用来创建每一类基本窗口组件的接口。每一类窗口组件都有一个抽象类,而具体子类则实现了窗口组件特定的视感风格。
WidgetFactory接口有一个返回新窗口组件对象的操作。客户调用这些操作以获得窗口组件实例,但客户并不知道他们正在使用的是哪些具体类。这样客户就不依赖于一般的视感风格。
每一种视感标准都对应于一个具体的WidgetFactory子类。(例如,MotifWidgetFactory的CreateSrollBar实例化并返回一个Motif滚动条。)
客户仅通过WidgetFactory接口创建窗口组件,他们并不知道哪些类实现了特定视感的窗口组件。换言之,客户仅与抽象类定义的接口交互,而不使用特定的具体类的接口。(客户不知道用的是哪一套创建方案)
对于客户来说,其只知道WidgetFactory、Window、SrollBar这三个抽象类。
适用:
1、一个系统要独立于它的产品的创建、组合和表示时
2、一个系统要由多个产品系列中的一个来配置时。
3、当你强调一系列相关的产品对象的设计以便进行联合使用时。
特点:
1、它分离了具体的类。
因为一个工厂封装创建产品的责任和过程,它将客户与类的实现分离。客户通过它们的抽象接口操纵实例。产品的类名也不出现在客户代码中。(比如:具体的产品类名ProductA1,客户使用产品的抽象类名AbstractProductA)
2、它使得易于交换产品系列。
一个抽象工厂创建了一个完整的产品系列,只需改变具体的工厂即可使用不同的产品配置,整个产品系列会立刻改变。
3、难以支持新种类的产品
难以扩展抽象工厂以生产新种类的产品。这是因为AbstractProduct接口确定了可以被创建的产品集合。支持新种类的产品就需要扩展该工厂接口,这将涉及AbstractProduct类及其所有子类的改变。
代码示例:
为一个电脑游戏创建一个迷宫,我们将忽略许多迷宫中的细节以及一个迷宫游戏中有一个还是多个游戏者。我们仅关注迷宫是怎样被创建的。
我们将一个迷宫定义为一系列房间,一个房间知道它的邻居。可能的邻居要么是另一个房间,要么是一堵墙,或者是到另一个房间的一扇门。
类Room、Door和Wall定义了我们所有的例子中使用到的构件。
我们将使用Abstract Factory模式创建这个迷宫。
类MazeFactory可以创建迷宫组件。它建造房间、墙壁和房间之间的门。 建造迷宫的程序将MazeFactory作为一个参数,这样程序员就能指定要创建的房间、墙壁和门等类。
class MazeFactory
{
public:
MazeFactory() ;
virtual Maze* MakeMaze() const
{ return new Maze ; }
virtual Maze* MakeWall() const
{ return new Wall ; }
virtual Maze* MakeRoom(int n) const
{ return new Room(n) ; }
virtual Maze* MakeDoor(Room* r1, Room* r2) const
{ return new Door(r1, r2) ; }
} ;
//我们创建MazeFactory的子类EnchantedMazeFactory,这是一个创建施了魔法的迷宫的工厂。
//EnchantedMazeFactory将重定义不同成员函数并返回Room,Wall等不同的子类。
class EnchantedMazeFactory : public MazeFactory
{
public:
EnchantedMazeFactory() ;
virtual Room* MakeRoom(int n) const
{ return new EnchantedRoom(n) ; }
virtual Maze* MakeDoor(Room* r1, Room* r2) const
{ return new DoorNeedingSpell(r1, r2) ; }
} ;
//我们创建MazeFactory的子类BombedMazeFactory。这是一个炸弹毁坏过的迷宫的工厂
//Room的子类RoomWithABomb,Wall的子类BombedWall来表示炸过的房间和墙壁
class BombedMazeFactory : public MazeFactory
{
public:
BombedMazeFactory() ;
virtual Maze* MakeWall() const
{ return new BombedWall ; }
virtual Maze* MakeRoom(int n) const
{ return new RoomWithABomb(n) ; }
} ;
客户使用:
客户的CreateMaze函数,以MazeFactory为参数
Maze* MazeGame::CreateMaze (MazeFactory&factory)
{
Maze* aMaze = factory.MakeMaze() ;
Room* r1= factory.MakeRoom(1) ;
Room* r2= factory.MazeRoom(2) ;
Door* aDoor = factory.MakeDoor(r1, r2) ;
aMaze->AddRoom(r1) ;
aMaze->AddRoom(r2) ;
//用户自己组装(工厂只负责创建)
r1->SetSide(North, factory.MakeWall()) ;
//......
return aMaze ;
}
为创建一个包含炸弹的简单迷宫,我们仅用BombedMazeFactory调用CreateMaze。
MazeGame game ;
BombedMazeFactory factory ;
game.CreateMaze(factory) ;
【通过抽象工厂来创建对象(面向接口编程),而不是自己直接创建,为什么要假手工厂来创建对象?因为灵活性,当我们要其它风格的对象时,换工厂就行了。(因为接口不变,我们做的改动小)】
关键词:工厂
(二)、生成器(Builder)——对象创建型模式
意图:
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
【即把组装与生成分离,由一个类(生成器Builder)负责生成所需的配件,由另一个类(向导器Director)负责组装配件成产品】
ConcreteBuilder创建该产品的内部表示并定义它的装配过程。
生成器逐步的构造它们的产品。因此Builder类接口必须足够普遍,以便为各种类型的具体生成器构造产品。
且注意:在Builder中缺省的方法为空,而不是纯虚函数,把它们定义为空方法,这使客户只重定义他们所感兴趣的操作。
不同的生成器提供不同的小配件给向导器。
协作:
1、客户创建Director对象,并用它所想要的Builder对象进行配置。
2、一旦产品部件被生成(导向器调Builder),导向器就会通知生成器。(生成部件)
3、生成器处理导向器的请求,并将部件添加到该产品中。(组装)
4、客户从生成器中检索(获取)产品。
特点:
1、它使你可以改变一个产品的内部表示。
Builder对象提供给导向器一个构造产品的抽象接口。该接口使得生成器可以隐藏这个产品的表示和内部结构。它同时也隐藏了该产品是如何装配的。
2、它将构造代码和表示代码分开。
客户不需要知道定义产品内部结构的类的所有信息,这些类是不出现在Builder接口中的。每个实际的生成器包含了创建和装配一个特定产品的所有代码。
3、它使你可对构造过程进行更精细的控制。
Builder模式与一下子就生成产品的创建型模式不同,它是在导向者的控制下一步一步构造产品的。仅当该产品完成时导向者才从生成器中取回它。(导向者控制装配的顺序)
代码举例:
以创建迷宫为例:
一个CreateMaze函数专门用来创建迷宫,它以类MazeBuilder的一个生成器对象作为参数。
class MazeBuilder
{
public:
virtual void BuildMaze() { }
virtual void BuildRoom(int room) { }
virtual void BuildDoor(int roomFrom, int roomTo) { }
virtual Maze* GetMaze() { return 0; }
protected:
MazeBuilder() ;
} ;
该接口可创建:1、迷宫(空架子) 2、有一个特定房间号的房间 3、在有号码的房间之间的门
GetMaze操作返回这个迷宫给客户。 MazeBuilder的子类将重定义这些操作,返回它们所创建的迷宫。
注意:MazeBuilder自己并不创建迷宫;它的主要目的仅仅是为创建迷宫定义一个接口,MazeBuilder的子类做实际工作。
class StandardMazeBuilder : public MazeBuilder
{
public:
StandardMazeBuilder() ;
virtual void BuildMaze() ;
virtual void BuildRoom(int) ;
virtual void BuildDoor(int, int) ;
virtualMaze* GetMaze() ;
private:
Direction CommonWall(Room*, Room*) ;
Maze* _currentMaze ;
} ;
void StandardMazeBuilder::BuildMaze() //向构造一个空的Maze,再逐步往里面加东西。
{
_currentMaze = new Maze ;
}
void StandardMazeBuilder::GetMaze()
{
return _currentMaze ;
}
//BuildRoom操作创建一个房间并建造它周围的墙壁
void StandardMazeBuilder::BuildRoom(int n)
{
if(!_currentMaze->RoomNo(n))
{
Room* room = new Room(n) ;
_currentMaze->AddRoom(room) ;
//创建Room周围的墙壁
room->SetSide(North, new Wall) ;
room->SetSide(South, new Wall) ;
room->SetSide(East, new Wall) ;
room->SetSide(West, new Wall) ;
}
}
//.......
导向器上场:其定义了组装的流程(向建一个Maze框架,再填充两个Room,再在两房子之间填充Door)
Maze* MazeGame::CreateMaze(MazeBuilder& builder)
{
builder.BuildMaze() ;
builder.BuildRoom(1) ;
builder.BuildRoom(2) ;
builder.BuildDoor(1, 2) ;
returnbuilder.GetMaze() ;
}
用户使用:用户现在可以用CreateMaze和StandardMazeBuilder来创建一个迷宫
Maze* maze ;
MazeGame game ;
StandardMazeBuilder builder ;
game.CreateMaze(builder) ;//CreateMaze根据其定义的流程去指导builder逐步创建、组装好产品。
maze = builder.GetMaze() ;
抽象工厂VS 生成器
两者的目的不同,抽象工厂就是生产一些相关的产品给用户,用户怎么用这些产品其不关心。生成器就是生产一些小部件,由导向器来装配这些部件,组装成一个产品。
抽象工厂是一个工厂,一个工厂里生产各种产品(这些产品都是相关的)。它提供给用户的是多个接口,每个接口产生一个产品。
抽象工厂与生成器相似,因为它也可以创建复杂对象。主要的区别是Builder模式着重于一步步构造一个复杂对象。而抽象工厂着重于多个系列的产品对象(简单的或是复杂的)。Builder在最后一步返回产品,而对于抽象工厂来说,产品是立即返回的。
关键词:装配