9.5 例子:插入光标和选中区
Last updated
Last updated
接下来的章节将通过三个例子来说明上面讨论的原则。在其中的两个例子中,最好的方法是将相关的代码段分开;在第三个例子中,最好将它们合并。
第一个例子由GUI编辑器项目中的插入光标和选择组成。编辑器显示了一条闪烁的垂直线,表示用户输入的文本将出现在文档中的位置。它还可以高亮显示一段范围的字符,称为选中区(selection),用于复制或删除文本。插入光标总是可见的,但有可能出现没有选择文本的情况。如果选中区存在,插入光标总是位于它的一端。
选中区和插入光标在某些方面是相关的。例如,光标总是位于选中区的一端,而且光标和选中区往往是一起操作的:点击并拖动鼠标可以同时设置它们,文本插入首先会删除选中的文本(如果有的话),然后在光标位置插入新文本。因此,使用一个对象来管理选中区和光标似乎是合乎逻辑的,一个项目小组就采取了这种方法。该对象存储了文件中的两个位置,以及指示光标在哪一端和选中区是否存在的布尔值。
然而,这个组合对象很尴尬。它没有为高层代码提供任何好处,因为高层代码仍然需要将选中区和光标视为不同的实体,并对它们分别进行操作(在插入文本时,它首先调用组合对象上的一个方法来删除选择的文本;然后调用另一个方法来检索光标位置,以便插入新的文本)。组合对象的实现实际上比独立对象更复杂。它避免了将光标位置作为一个单独的实体来存储,但必须存储一个布尔值,表明光标在选中区的哪一端。为了检索光标位置,这个组合对象必须首先测试布尔值,然后选择合适的选中区端点。
当一个通用的机制也包含专门用于该机制的特定用途的代码时,就会出现这种危险信号。这使得机制更加复杂,并在机制和特定用例之间产生信息泄露:未来对用例的修改可能也需要对底层机制进行修改。
在这种情况下,选中区和光标的关系不够密切,不能将它们合并。当代码被修改为将选中区和光标分开时,使用和实现都变得简单了。与必须从中提取选中区和光标信息的合并对象相比,分开的对象提供了一个更简单的接口。光标的实现也变得更简单了,因为光标的位置是直接表示的,而不是通过选中区和布尔值间接表示的。事实上,在修改后的版本中,选择和光标都没有使用特殊的类。相反,一个新的Position
类被引入来表示文件中的一个位置(一个行号和行内的字符)。选中区用两个Position
表示,光标用一个表示。在这个项目中,Position
也有其他用途。这个例子还展示了更低层但更通用的接口的好处,这些在中已经讨论过。