GOF - 设计模式实例研究(一) 组合模式
标签(空格分隔): 设计模式
写在前面:读设计模式的时候没有例子总是一种似懂非懂的感觉,直到有了实例导读,切身地去考虑这些需求的时候,才发现设计模式的伟大之处。挑选自己认为的一些模式的应用关键点和需求瓶颈记录下来,加深印象。
目的
通过对 Lexi 文档编辑器的设计实例,体会设计模式是如何解决设计问题的
Lexi 的 7 个设计问题
文档结构
格式化
修饰用户界面
支持多种视感
支持多种窗口系统
用户操作
拼写检查和连字符
文档结构
考虑设计一个文档编辑器的时候,我是直接懵逼的,好复杂啊,那么种类的元素,图片、文字、图片、代码段等等,这么多的不同的元素要整齐划一地展现出来,四个字,毫无头绪。
基本需求
好的,那我们从需求入手,一个好的文档结构应该可以允许哪些操作呢
- 保持文档的物理结构。即将文本和图形安排到行、列、表等。
- 可视化生成和显示文档。(这必须的嘛)
- 根据显示位置来映射文档内部表示的元素。这个简单来说就是可以让我们点哪里就实际获取到对应位置的元素的引用以便下一步的操作
限制条件
另外,还有一些限制的地方。
- 首先,我们应该一致对待图形和文本,而不是将图片看成文本的特例或者将文本看成图形的特例。为什么这么说呢,考虑我们如果想在图形中自由地嵌入文本,需要写一种特殊的操作,反之要在文本中嵌入图片又需要一种特殊操作,最后就会得到一堆冗余的操作机制。
- 其次,我们不应该过分地强调内部表示中单个元素和元素组之间的区别,简单元素和组合元素也应该被一致地对待。这样的好处就是我们的文档可以是任意复杂的,第 10 行第 9 个元素可以使图片也可以是多个元素组装成的复杂图表。一旦这个元素可以画出自己并描述自己的区域,我们便可以任意地操纵它的显示。
- 然而,为了检查拼写错误和确定字符的连接点,需要对文本进行分析。这就与第二个的限制条件产生了矛盾。我们并不关心一行上的元素是简单元素还是复杂对象,但是文本分析有时候是依赖于被分析的对象的。显然对一个多边形进行文本分析是没有意义的。
解决方法
这里我们首先介绍 递归组合 的概念,通过递归组合可以又较简单的元素逐渐建立复杂的元素。比如说,将字符和图形从左到右排列形成文档的一行,然后由多行形成一列,再由多列形成一页等等。
也就是说,我们可以通过将每一个重要元素表示成一个 对象 的方法来描述文档的结构。它不仅包含字符、图形等 可见元素,也包括 不可见的、结构化的元素如 行和列。通过这样的方法,我们就可以做到在显示、格式化和互相嵌入等方面一致地对待图形和文本了。
另外,这里隐含了很重要的两个地方
- 对象需要相应的类。
- 因为要一致地对待这些对象,所以这些类必须有兼容的接口。
实现思路
为了是这些类具有兼容的接口,我们为出现在文档结构中的所有对象定义一个抽象类 图元,至于基本的图形元素(像字符和图像)以及结构元素(行和列)都由它的子类来定义。
接着在看图元定义了哪些的接口之前,我们明确图元的 基本责任
- 怎样画出自己
- 占用了多大的空间
- 父图元和子图元是什么
Responsibility | Operation | Description |
---|---|---|
Appearance | virtual void Draw(Window*) | 画出自己 |
virtual void Bounds(Rect&) | 返回图元占用的矩形区域 | |
hit detection | virtual void Intersects(Const Point&) | 判断一个指定的点是否和图元相交 |
Structure | virtual void Insert(Glyph*, int) | 插入一个子图元 |
virtual void Remove(Glyph*) | 删除子图元 | |
virtual Glyph* Child(int) | 返回指定索引的子图元 | |
virtual Glyph* Parent() | 返回父图元 |
可以看到,通过上面的这些接口,我们可以 一致地对待 由 Glyph 抽象类派生出的子类的任意数量组合的 复杂或简单对象 。
组合模式
上面运用的就是典型的一种组合模式,简单地总结一下组合模式。
特点
- 表示对象的部分 - 整体结构
- 用户可以忽略组合对象和单个对象的不同,一致地对待它们
关键
- 组合模式的关键是一个 抽象类,它既可以代表图元,又可以代表图元的容器。
- 这个抽象类声明了一些大家都会需要的操作,如
Draw()
。同时也声明了所有组合对象共享的一些操作来访问和管理他的子部件。
总结
组合模式对于我们使用原有的组件去创建组合新的组件有巨大的便利性和可拓展性。而同时组合模式在实现的时候也有很多需要实际权衡的问题,如抽象类接口最大化、共享组件、子部件排序等等需要我们在实际开发中再慢慢体会。