6.2 例子:为一个编辑器存储文本
让我们考虑一个软件设计课上的例子,学生被要求构建简单的GUI文本编辑器。该编辑器必须显示一个文件,并允许用户指向、单击和键入来编辑文件。编辑器必须支持在不同窗口中查看同一文件的多个同步视图;它们还必须支持多级撤销和重做以修改文件。
每个学生项目都包括一个管理文件底层文本的类。文本类通常提供将文件加载到内存中、读取和修改文件文本以及将修改后的文本写回文件的方法。
许多学生小组为文本类实现了特殊用途的API。他们知道该类将用于一个交互式编辑器,所以他们考虑了编辑器必须提供的功能,并根据这些特定的功能定制了文本类的API。例如,如果编辑器的用户输入了退格键,编辑器就会删除光标左边的字符;如果用户输入了删除键,编辑器就会删除光标右边的字符。了解到这一点,一些小组在文本类中创建了一个方法来支持这些具体的功能:
这些方法中的每一个都将光标位置作为其参数;一个特殊的类型Cursor表示这个位置。编辑器还必须支持可以复制或删除选择的区域。学生们通过定义一个Selection
类并在删除时将该类的对象传递给文本类来处理这个问题:
学生们可能认为,如果文本类的方法与用户可见的功能相对应,那么实现用户界面会更容易。但实际上,这种专用化对用户界面代码几乎没有好处,而且它给开发用户界面或文本类的开发人员带来了很高的认知负荷。文本类最终包含了大量的浅方法,每个方法都只适用于一个用户界面操作。许多方法,如delete
,只在一个地方被调用。结果,从事用户界面工作的开发人员不得不了解大量的文本类方法。
这种方法在用户界面和文本类之间造成了信息泄露。与用户界面相关的抽象,如选择或退格键,都反映在文本类中;这增加了开发文本类的开发人员的认知负荷。每一个新的用户界面操作都需要在文本类中定义一个新的方法,因此从事用户界面工作的开发人员很可能最终也会进行文本类的开发。类设计的目标之一是允许每个类独立开发,但专用的方法将用户界面和文本类束缚在了一起。
Last updated