您现在的位置是:首页 >学无止境 >实例研究:设计一个文档编辑器(5)网站首页学无止境
实例研究:设计一个文档编辑器(5)
组合模式
递归组合不仅可用来表示文档,我们还可以用它表示任何潜在复杂的、层次式的结构。C o m p o s i t e ( 4 . 3 )模式描述了面向对象的递归组合的本质。现在是回到此模式并学习它的时候了,需要时再回头参考这个场景。
格式化
我们已经解决了文档物理结构的表示问题。接着,我们需要解决的问题是怎样构造一个特殊物理结构,该结构对应于一个恰当地格式化了的文档。表示和格式化是不同的,记录文档物理结构的能力并没有告诉我们怎样得到一个特殊格式化结构。这个责任大多在于L e x i,它必须将文本分解成行,将行分解成列等等。同时还要考虑用户的高层次的要求,例如,用户可能会指定边界宽度、缩进大小和表格形式、是否隔行显示以及其他可能的许多格式限制条件。L e x i的格式化算法必须考虑所有这些因素。
现在我们将“格式化”含义限制为将一个图元集合分解为若干行。下面我们可以互换使用术语“格式化” ( f o r m a t t i n g )和“分行” ( l i n e b r e a k i n g )。下面讨论的技术同样适用于将行分解为列和将列分解为页。
封装格式化算法
由于所有这些限制条件和许多细节问题,格式化过程不容易被自动化。这里有许多解决方法,实际上人们已经提出了各种各样具有不同能力和缺陷的格式化算法。因为L e x i是一个所见即所得编辑器,所以一个必须考虑的重要权衡之处在于格式化的质量和格式化的速度之间的取舍。我们通常希望在不牺牲文档美观外表的前提下,能得到良好的反映速度。这种权衡受许多因素影响,而并不是所有因素在编译时刻都能确定的。例如,用户也许能忍受稍慢一点的响应速度,以换取较好的格式。这种选择也许导致了比当前算法更适用的彻底不同的格式化算法。另一个例子,更多实现驱动的权衡是在格式化速度和存储需求之间:很有可能为了缓存更多的信息而降低格式化速度。因为格式化算法趋于复杂化,因而可以考虑将它们包含于文档结构之中,但最好是将它们彻底独立于文档结构之外。理想情况下,我们能够自由地增加一个G l y p h子类而不用考虑格式算法。反过来,增加一个格式算法不应要求修改已有的图元类。
这些特征要求我们设计的L e x i易于改变格式化算法。最好能在运行时刻改变这个算法,如果难以实现,至少在编译时刻应该可以很方便地改变。我们可以将算法独立出来,并把它封装到对象中使其便于替代。更进一步,可以定义一个封装格式化算法的对象的类层次结构。类层次结构的根结点将定义支持许多格式化算法的接口,每个子类实现这个接口以执行特定的算法。那时就能让G l y p h子类对象自动使用给定算法对象来排列其子图元。
Compositor和Composition
我们为能封装格式化算法的对象定义一个C o m p o s i t o r类。它的接口(见表2 - 2)可让c o m p o s i t o r获知何时去格式化哪些图元。它所格式化的图元是一个被称为C o m p o s i t i o n的特定图元的各个子图元。一个C o m p o s i t i o n在创建时得到一个C o m p o s i t o r子类实例,并在必要的时候(如用户改变文档的时候)让C o m p o s i t o r对它的图元作C o m p o s e操作。图2 - 5描述了C o m p o s i t i o n类和C o m p o s i t o r类之间的关系。
一个未格式化的C o m p o s i t i o n对象只包含组成文档基本内容的可见图元。它并不包含像行和列这样的决定文档物理结构的图元。C o m p o s i t i o n对象只在刚被创建并以待格式化的图元进行初始化后,才处于这种状态。当C o m p o s i t i o n需要格式化时,调用它的C o m p o s i t o r的C o m p o s e操作。C o m p o s i t o r依次遍历C o m p o s i t i o n的各个子图元,根据分行算法插入新的行和列图元。图2 - 6显示了得到的对象结构。图中由C o m p o s i t o r创建和插入到对象结构中的图元以灰色背景显示。
每一个C o m p o s i t o r子类都能实现一个不同的分行算法。例如,一个S i m p l e C o m p o s i t o r可以执行得很快,而不考虑像文档“色彩”这样深奥的东西。好的色彩意味着文本和空白的平滑
分布。一个Te X C o m p o s i t o r会实现完全的TEX算法[ K n u 8 4 ],会考虑像色彩这样的东西,而以较长的格式化时间作为代价。
C o m p o s i t o r- C o m p o s i t i o n类的分离确保了支持文档物理结构的代码和支持不同格式化算法的代码之间的分离。我们能增加新的C o m p o s i t o r子类而不触及G l y p h类,反之亦然。事实上,我们通过给C o m p o s i t i o n的基本图元接口增加一个S e t C o m p o s i t o r操作,即可在运行时刻改变分行算法。
策略模式
在对象中封装算法是S t r a t e g y ( 5 . 9 )模式的目的。模式的主要参与者是S t r a t e g y对象(这些对象中封装了不同的算法)和它们的操作环境。其实C o m p o s i t o r就是S t r a t e g y。它们封装了不同的格式算法。C o m p o s i t i o n就是C o m p o s i t o r策略的环境。
S t r a t e g y模式应用的关键点在于为S t r a t e g y和它的环境设计足够通用的接口,以支持一系列的算法。你不必为了支持一个新的算法而改变S t r a t e g y或它的环境。在我们的例子中,支持子图元访问、插入和删除操作的基本G l y p h接口就足以满足一般的用户需求,不管C o m p o s i t o r子类使用何种算法,都足以支持其对文档的物理结构的修改。同样地, C o m p o s i t o r接口也足以支持C o m p o s i t i o n启动格式化操作。