7.5.1 用结构模式表示数据

7.5.1 用结构模式表示数据

 

 

    如果我们讨论程序,是根据数据结构,而不是对象的话,我们可以说,结构模式是描述设计数据结构常见和行之有效的方法。设计模式是更为具体,并指定如何使用面向对象语言的对象来实现这些结构。在本章中,我们会看到,用函数式方法来表示数据。在第一种表示中,我们使用简单的记录列表,用任何一种语言来写都很容易,但是,第二种表示(使用差别联合)更有趣。我们来看一下第一种模式,叫复合模式(composite pattern)。

 

复合设计模式

 

    复合模式可以把几个对象组合成一个复合对象,处理方式与我们在原始对象上的相同。图 7.6 显示了面向对象实现此模式的通常方法。

7-6

图 7.6 CompositeComponent 是一个类,包含其他组件的集合 ;它继承自 AbstractComponent,因此,它可以用在原始组件的地方,

与其他组件的使用方式相同,比如,ConcreteComponent。


    这个复合对象由复合类表示,然后,程序使用 AbstractComponent 类处理对象,因此,它不需要了解原始对象与复合对象之间的区别。还可以看到一个虚拟的方法的示例,称为 Operation。在 CompositeComponent 类中,它在来自 components 的集合中的所有对象上进行迭代,并对它们调用 Operation 方法。在我们的文档表示中,可以找到类似的情况。当使用 SplitPart,把一个部件拆分为多列或行时,我们把它当作普通文档部件看待,与其他部件有完全相同的方式。这个部件由存储在列表中的其他部件组成。我们可以重写图 7.6 普通示例,以相同的方式,使用 F# 中递归的差别联合类型:
 

type AbstractComponent
  | CompositeComponent of list<AbstractComponent>
  | ConcreteComponent of (...)
  | (...)

 

 

    在此示例中,复合值表示成除了其他原始组件之外的可选值之一。它以递归方式引用 AbstractComponent 类型,把此类型的值存储在表示组合对象的列表中。当处理 AbstractComponent 类型的值时,我们不需要区别对待组合值和原始值,这正是这种设计模式的主要目的。

    在函数式编程中,组合是类型的公开表现。因此,类型的任何用户都知道有由组合创建的组件,并且可以用在写原始处理函数时,像我们在实现 mapDocument 操作时一样。

    当使用函数式数据结构时,重点是在已有的类型上添加新函数的能力,所以,公开组成是有效的设计决策。这意味着,代码的函数式版本也不需要定义 Operation 方法,它在面向对象的表示中,是 AbstractComponent 类型的一部分。使用该类型的任何操作可以独立实现,作为一个处理函数的类型。

    F# 有一个高级功能,叫活动模式(active patterns),可以在一定程度上封装构成。这使我们能够公开组成,而不是整个差别联合类型,对开发F # 库是有用的。在书中,我们不讨论此功能的细节,你可以在本书的网站上找到更多的内容。

 

装饰设计模式

 

    与复合相关的另一种模式是装饰模式。这种模式的目标是,在运行时,可以为已有的类添加一种新的行为。图 7.7 中可以看到,这种结构看起来类似于复合模式。

    这两个模式看上去相似,但目的完全不同的。复合模式把组合值与原始值同行对待,装饰模式的目的是为已有对象添加一项新功能。如你所见,在图 7 中的 DecoratedComponent 类封装了其他被装饰的组件,它可以携带额外的状态(例如,decoration 字段)。装包饰的组件也可以添加行为,在 Operation 方法中使用这个额外的状态。

7-7

图 7.7 DecoratedComponent 类封装一个组件,并给它添加一个新的状态 ;在这个装饰组件中,Operation 方法调用包装的函数,并添加一个新的行为,使用这个装饰组件的状态。

 

    我们可以看到再次这种模式与我们的文档表示中的组合之一的对应。在我们的应用程序中,添加装饰到另一个部件的部件是 TitledPart。这个装饰是标题,添加的状态是文本和标题的字体。表示相同的结构的F# 类型,如图 7.7 的图解,与复合模式的情况一样,同样简单:

 

type AbstractComponent =
  | DecoratedComponent of AbstractComponent * (...)
  | ConcreteComponent of (...)
  | (...)

 

    在这里,由装饰可选值携带的数据,是一个装饰的组件(不是复合模式情况下的组件列表),还有额外的状态,它可以在不同的装饰之间改变。在前面的清单中,我们使用 (...) 语法来象征,但这是只伪代码。在真正 F# 代码中,在这里,可以指定实际状态的类型,比如,我们带标题的部件中的 TextContent 。正如具有复合模式,在装饰组件上实现操作的代码,位于我们为数据结构实现的处理函数中。在这个处理函数中,DecoratedComponent 的代码能够递归调用自已,处理封装的组件,并执行由该装饰添加的行为,比如,绘制文档部件的标题。

    在这一节中,两种模式的F# 实现依赖于使用递归的差别联合类型。在下一节中,我们会再次使用它,但是以不同的方式。我们将看一下面向对象的方法,来为已有的数据类型添加新的操作。

你可能感兴趣的:(设计模式,休闲,装饰设计模式,表示数据,复合设计模式)