设计模式文本编辑器

  这次,我将紧随设计模式第二章的步伐,来完成一个文本编辑器Lexi的实现,大部分内容都会原样的copy书中的东西,最后我将实现一个完整的文本编辑器,并且附上完整的源代码,好了,让我们开始吧。

  一.设计问题

  1. 文档结构 对文档内部表示的选择几乎影响到Lexi设计的每个方面。所有的编辑、格式安排、显示和问题吧分析都设计到这种表示。我们怎样组织这个信息会影响到应用的其他方面。
  2. 格式化   Lexi是怎样将文本和图形安排在行和列上的?哪些对象负责执行不同的格式策略?这些策略又是怎样和内部表述相互作用的?
  3. 修饰用户界面   Lexi的用户界面包括滚动条、边界和用来修饰WYSIWYG文档界面的阴影。这些修饰有可能随着Lexi用户界面的演化而发生变化。因此,在不影响应用其他方面的情况下,能自由增加和去除这些修饰就十分重要了。
  4. 支持多种视感(look-and-feel)标准  Lexi应不需做较大修改就能适应不同的视感标准,如Motif何Presentation Manager等
  5. 支持多种窗口系统 不同的视感标准通常是在不同的窗口系统中实现的。Lexi的设计应尽可能的对立与窗口系统
  6. 用户操作 用户通过不同的用户界面控制Lexi,包括按键和下拉菜单。这些界面对应的功能分散在整个应用对象中。这里的难点在于提供一个统一的机制,既可以访问这些分散的功能,又可以对操作进行撤销。
  7. 拼写检查和连字符 Lexi是怎样支持想检查拼音错误和决定连字符的连字典这样的分析操作的?当我们不得不添加一个新的分析操作时,我们怎样尽量少修改相关的类。

   二.文档结构

  内部表示应该支持如下几点:

  1. 保持文档的物理结构。即将文本和图形安排到行、列、表等
  2. 可视化生成和显示文档
  3. 根据显示位置来影射文档内部表示的元素。这可以是Lexi根据用户在可视化表示中所点击的某个东西来决定用户所引用的文档元素

  限制条件:

  1. 我们应该一致对待文本和图形
  2. 我们的实现不应该过分强调内部表示中单个元素和元素组之间的差别。Lexi应该能够一致对待简单元素和组合元素,这样就允许任意复杂的文档。

    递归组合:

  层次结构信息的表述通常是通过一种被称为递归组合(Recursive Composition)的技术来实现的。递归组合可以由简单的元素逐渐建立复杂的元素,是我们通过简单图形管事构造文档的方法之一。第一步,我们将字符和图形从左到右排列形成文档的一行,然后由多行形成一列,再由多列形成一页。

  我们将每一个重要元素表示成一个对象,就可以描述这种物理结构。它不仅包括字符、图形等可见元素,也包括不可见、结构化的元素,如行和列。

  通过用对象表示文档的每一个字符和图形管事,我们可以提高Lexi最佳设计的灵活性。

  其中有两个比较重要的地方:对象需要相应的类。为了一致性对待这些对象,我们这些类必须有兼容的接口。

  图元:

  我们将为出现在文档结构中的所有对象定义一个抽象类图元(Glyph)。它的子类即定义了基本的图形元素(像字符和图形),又定义了结构元素(像行和列)。设计如下图:

图元的三个基本的职责:1.怎样画自己,2.他们占用多大的空间3.它们的父图元和子图元是什么

Glyph的接口:

virtual void Draw(window*) windows类为了在屏幕上表示文本和基本对象,定义了一下图形操作。

virtual void Bounds(Rect &) 父图元通常需要知道想子图元需要占用多大的空间这样的信息,以把它和其他图元安排在一行上,保证不会相互覆盖。Bounds操作返回图元占用的矩形区域,它的返回的是包含该图元的最小矩形的对角顶点。Glyph各个子类重新定义该操作,返回他们各自画图所用的矩形区域。

virtual bool Intersecs(const Point&) 判断一个指定的点是否与图元相交。任何时候用户点击文档某处时,Lexi都能调用该操作确定鼠标所在的图元和图元结构。

virtual void Insert(Glyph*,int)Insert操作在整数Index指定的位置上插入一个图元。

virtual void Remove(Glyph*)Remove操作移去一个指定的子图元

virtual Glyph* Child(int)返回给定Index的子图元,像这样有子图元的图元应该内部使用Child草,而不是直接访问子数据结构。

virtual Glyph*Parent() 返回它的父图元

三.格式化

  我们将“格式化”含义限制为将一个图元集合分解为若干行。格式化(formatting)和分行(linebreaking)。这些技术同样适用于将行分解为列和将列分解为页。

  封装格式化算法:

  格式化算法趋于复杂化,因而可以考虑将它们包含于文档结构之中,但最好是将它们彻底独立于文档结构之外。理想情况下,我们能够自由地增加一个Glypah子类而不用考虑格式化算法。反过来,增加一个格式化算法不应该要求修改已有的图元类。

  Compositor和Composition:

  我们为能封装格式化算法的对象定义一个Compositor类。它的接口可让compositor获知何时去格式化那些图元。他所格式化的图元是一个被称为Compositon的特定的图元的各个子图元。一个Compositon在创建时得到一个Compositor子类实例,并在必要的时候让Compositor对它的图元作Composecoa。下面是他们的关系:

基本Compositor接口

void SetComposition(Composition*);

virtual void Compose();

其实,这个设计就是策略模式(Strategy),Strategy模式应用的关键点在于为Strategy和它的环境设计足够的通用接口,以支持一系列的算法。你不必为了支持一个新的算法改变Strategy或他的环境。

四.修饰用户界面

  第一种是在文本编辑区域周围边界以界定文本页。第二种事加滚动条让用户能看到同一页的不同部分。

  透明围栏(Transparent Enclosure):1.单子女(单组件)组合2.兼容的接口

  MonoGlyph

  我们可以将透明围栏的概念用于所有的修饰其他图元的图元。MonolGlyph保存了指向一个组件的引用并且传递所有的请求给这个组件。如下图所示:

五 支持多种视觉感标准

  视感标准本来是用来加强某一窗口平台上各个应用之间用户界面的一致性的。这些标准定义了应用程序应该怎样显示和对用户请求作出反映。最好能够在运行时刻可以改变Lexi的改观和感觉。

  对象创建的抽象:1.第一个集合是由抽象Glyph子类构成的,对每一个窗口组件图元都有一个抽象Glyph子类。

  2.另一个集合是与抽象子类对应的实现不同视感标准的具体的子类的集合。

  Abstract Factory模式:

  工厂(Factory)和产品(Product)是Abstract Factory模式的主要参与者。该模式描述了怎样在不直接实例化类的情况下创建一系列相关的产品对象。它最适用于产品对象的数目和种类不变,而具体产品系列之间存在不同的情况。

  六.支持多种窗口系统

  封装实现依赖关系:显示一个图元或图元结构的Windows类。

  Windows类封装了窗口要各个窗口系统都要做的一些事情:

  1. 它们提供了画基本几何图形的操作
  2. 它们能办厂图标或还原几何图形的操作
  3. 它们能改变自己的大小
  4. 它们能够更加需要画出窗口内容。例如,当它们由图标还原为窗口时,或它们在屏幕空间上重叠、出界的部分重新显示时,都要重画。

  Windows类的窗口功能必须跨越不同的窗口系统,有两种比较极端的观点:

    1.功能的交集 Windows类的接口只是提供所有窗口系统共有的功能

  2.功能并集  创建一个合并了所有存在系统的功能的接口

  我们一般采取Window类将提供一个支持大多数窗口系统的方便的接口。

  Windows和WindowImp

  我们定义一个独立的WindowImp类层次来隐藏不同窗口系统的实现。为了使Lexi运行于一个特定的窗口系统,我们用该系统的一个WindowImp子类实例设置Window对象。下面的图显示了Window和WindowImp层次结构之间的关系。

  通过在WindowImp类中隐藏实现,我们避免了对窗口系统的直接依赖,这可以让Window类层次保持相对较小并且比较稳定。同时我们还能方便地扩展实现层次结构以支持新的窗口系统。

  七,用户操作

  我们可以定义一个Command抽象类,以提供发送请求的接口,这个基本接口由一个抽象超值“Execute”组成。Command的子类以不同方式实现Execute操作,以满足不同的请求。

  撤销和重做

  在交互应用中撤销和重做能力是很重要的。为了撤销和重做一个命令,我们在Command接口中增加Unexecute操作。Unexecute操作是Execute的逆操作。为了决定一个命令是否可以撤销,我们给Command接口增加了一个抽象的Reversible操作,它返回Boolean值。子类可以重定义这个操作,以根据运行情况返回true或false。

  命令历史记录

  支持任意层次的撤销和重做命令的最后一步是定义一个命令历史记录(Command History)或已经执行的命令列表。

  该模式描述了怎样封装请求,也描述了一致性的发送请求的接口,允许你配置客户端以处理不同请求。

  八、拼写检查和断字处理

  我们引入一个类称之为iterators的对象,他们的目的是定义这些机制的不同集合。我们可以通过继承来统一访问不同的数据结构和支持新的遍历方式,同时不改变图元接口或打乱已有的图元实现。

  Iterator类及其子类

    Iterator模式描述了那些支持访问和遍历对象结构的技术,它不仅可用于组合结构也可用于集合。该模式抽象了遍历算法,对客户隐藏了它所遍历对象的内部机构。

  现在貌似我们已经解决了图元的遍历的问题,下面我们就把图元的分析加入其中。我们使用了Vistor模式。

  我们使用术语访问者(vistor)来泛指在遍历过程中“访问”被遍历对象并做适当操作的一类对象。

  我在边看书,边写代码,只是把书中的代码稍微加工了一下,具体的功能实现,都没有做,只是一个框架上的实现。

     代码下载:Lexi.rar

  

  

原文地址:https://www.cnblogs.com/lufangtao/p/2469309.html