《设计模式》之Creational模式:简介

来源:互联网 发布:猫王收音机 知乎 编辑:程序博客网 时间:2024/06/08 10:46

Creational Patterns简介

创造(Creational)设计模式抽象了实例化的过程。它使得系统可以独立于对象的创建、组成和表示。一个类的Creational模式利用继承机制来改变它实例化的类。

当一个系统发展到更依赖于对象组合而不是类的继承时,Creational模式变得尤其重要。在这种情况下,不能将一些程序功能写死,而是定义一个小一点的基本功能集合,然后由其去构成其它复杂的功能。因此,创建特定功能的对象不仅仅是简单地实例化一个类。

在这里需要强调两点:第一,Creational模式都封装了该系统所需的具体类的内容;第二,这种模式隐藏了这些具体类如何创建和如何被放在一起的。在什么被创建、由谁创建、如何创建和何时创建,这几个方面Creational模式给予你了很大的灵活性。这种模式可以让你的系统配置结构和功能上广泛变化的“产品(product)”对象。这种配置可以是静态的(在编译时指定),或者是动态的(运行时)。

一些Creational模式是竞争关系。例如,有些案例中,Prototype和Abstract Factory模式都可以发挥很好的作用。有些时候,它们又相互补充:Builder模式可以使用一种其它的模式来构建组件。Prototype在实现的时候可以用Singleton。

因为这些Creational模式是紧密关联的,我们将要学习5种这种模式,来对比它们之间异同。我们将会使用一个实际案例–建立一个迷宫游戏–来展现具体的实现。这个迷宫游戏的具体设计在讲述不同的模式时可能会有些变化。有时,这个游戏就是寻找一条出去的路,所以玩家将会智能看到迷宫的局部。有时,迷宫又会有各种难题和危险,迷宫将会展现出已经探索过的区域。

我们将会忽略迷宫的很多细节,也不必关系是单个玩家或者多个玩家。我们只关心迷宫如何构建的。我们将迷宫第一成一个房间的集合。一个房间会知道它的邻居,它的邻居可能是其它房间、一堵墙或者一个通向其它房间的门。

Room、Door和Wall类定义得了迷宫(maze)的主要组件。我们在这里只定义了主要部分,我们忽略了玩家,操作和移动等其它和构建迷宫无关的功能。

下图展示了这些类之间的关系:

这些类之间的关系

每个房间有四个边。我们使用了C++中的枚举方向Direction来指定房间的东西南北边:

enum Direction{North,Sourth,East,West};

在Smalltalk中的实现使用了对应的符号来表示方向。

MapSit是迷宫所有组件的抽象类。为了简化例子,MapSit定义了一个操作Enter,意思是说明你如何进入的。如果你进入一个房间,那么你的位置就会发生改变。

这里写图片描述

Enter提供为其它游戏操作提供了一个简单的基础。

RoomMapSite的具体的一个类,定义了迷宫中组件的重要关系。它拥有指向其它MapSite对象的引用,同时存储了房间号。这个号码在迷宫中唯一标识这个房间。

这里写图片描述

下面的类标识墙或者门:

这里写图片描述

我们不仅需要表示迷宫的一部分,我们还需要表示迷宫整体。Maze可以使用RoomNo和房间号找到指定的房间。

这里写图片描述

RoomNo可以使用线性搜索来查询,或者hash表,或者一个简单的数组。我们在这里先忽略这些具体细节,我们只关系如何指定maze对象的组件。

我们定义了另一个类MazeGame,用来创建迷宫。一个直接的方法是通过一些列操作在迷宫中来添加组件,同时将它们连接起来。例如,下面的成员函数将会创建两个房间和一个它们之间的门:

这里写图片描述

这个函数很复杂,因为它只是建立了两个房间,显然还有其它更简单的方法。例如,Room的constructor可以提前初始化墙的边,但这只是将代码转移到其它地方去了。这个函数最致命的问题不是它的代码行数,而是它的灵活性,它写死了迷宫的布局。修改布局需要重新写这个函数,或者重写(overriding)这个函数–也就是重新实现–或者修改一部分–这样做会易出错的,同时不利于重用。

Creational模式告诉我们如何将这设计的更加灵活,不仅仅于更少的代码。特别的是,这样做可以使得改变迷宫组件的类很容易。

假设在一个新的魔法迷宫游戏中,你想重用现有的迷宫布局代码。这个魔法迷宫使用了新的组件,例如DoorNeedingSpell,一个可以利用咒语来锁上和打开的门;还有EnchantedRoom,一个可能有神奇物品的房间,像魔法钥匙和咒语。如何简单的改变CreateMaze,让它可以创建这个新的迷宫呢?

  1. 如果Createmaze调用虚函数而不是具体的room、wall和door的构建函数,你使用MazeGame的子类和重新定义的虚函数来改变实例化的类。这是一个Factory Method模式的例子。

  2. 如果Createmaze被传入一个对象来创建room、wall和door。你可以通过传入不同的参数来修改这些组件的类。这是一个Abstract Factory模式的例子。

  3. 如果Createmaze被传入一个对象,这个对象可以在内部创建一个新的迷宫,同时可以向这个迷宫添加room、door和wall。你可以使用继承来修改这个迷宫和这个迷宫建立的方式。这是一个Builder模式的例子。

  4. 如果Createmaze以各种原型room、door和wall对象参数化,然后它将这些组件复制添加进这个maze。你可以通过替换这些原型对象来改变maze。这是一个Prototype的例子。

最后剩下的一个Creational模式是Singleton,可以保证每个游戏只有唯一的迷宫,同时所有游戏对象可以访问这个迷宫,而不需要求助于全局变量和函数。Singleton 方便的扩展和替换maze,而不需修改现有的代码。

(本系列文章是对《Design Patterns: Elements of Reusable Object-Oriented Software》的部分翻译和个人理解,如有错误,欢迎批评指正)

1 0