StupidBeauty
Read times:4080Posted at:Tue Oct 17 12:26:05 2017 - no title specified

《C++面向对象软件设计及构建》文章翻译:4.2利用聚合来实现一个新类,4.2 Implementing a New Class Using Aggregation

聚合的概念


聚合 ,这个术语,描述 的是,这样一个结构,在其中,某个组件 ,作为一个整体,包含着其它组件 ,被包含的组件是整体的一部分。 在面向对象编程的术语中, 被包含的那些组件,被称作 子对象( sub-objects ,它们 被外层 的包装对象所封装。封装 (外层 整体 )对象 ,利用那些 子对象所提供的功能来实现它自己的行为。3.2 小节 中,那个 Location 类,仅仅包含 了内置类型 (两个整 型(int) ) 。而在更常见的情形中,是,那些类,会使用其它的类,同时也会使用内置的类型,以用来定义它们自己的私有数据。 在本章中,将会见到多个 这样的类的示例。

聚合, 关联 有关系,但也有区别。聚合 与关联之间的区别,在下面的图片中展示。它们展示 的是,之前开发 的某个 定时器系统 ,以及组成该系统的那些对象。正如下 图所示, 以关联的方式来创建一个定时器系统,就需要,单独创建那些 Clock Counter Message Panel Canvas Button对象 ,再使用那些对象的方法来构造出互相之间所预期的连接结构。

使用关联来构建一个停表(StopWatch

在这个图片中,那些实线,表示的是,对象之间使用指针来连接。例如,Counter对象,就维护着指向Message对象的指针,它向那个对象传递自己的当前值。图片中的虚拟箭头,表示的是,Frame 对象和显示在该Frame 中的那些用户界面组件之间的关系。

下一张图片中,展示了,在使用聚合的情况下,同样的这个系统会是什么样子。利用聚合,则,这个定时器系统的基本机能,会被封装到一个其类为 StopWatch 的对象中。那个类,则会在本小节中开发完成。StopWatch 类的公有接口,提供了一些方法,可用来将定时器系统作为一个整体来进行操作。

利用聚合来构建一个停表(StopWatch)

特定的类,例如Clock类,可能会在多个不同的聚合中用到。例如,在另一个不同的定时器系统中,可能并不需要提供用户控制按钮。这样的系统, SimpleTimer,可按照下图的方式使用聚合来构建。

SimpleTimer聚合

可能还会有另一种定时器系统,只用于记录与程序相关的时间,而无需向用户显示。这样的定时器系统, InternalTimer,可按照下图所示的方式来组织。

InternalTimer聚合

类似地,可能还会有其它类型的系统,其中会将这些类及其它类按照不同的组合方式聚合起来。

聚合的优点

聚合,带来狠多好处,其中大部分好处都是因为封装而产生的。这些好处非常重要,值得说三遍:

简洁:

对类或对象的聚合,使得,被封装的那些子对象,可以作为一个整体来对待。这样,就可以更容易地构造且管理这种子对象所组成的系统的多个独立实例。举个例子,想象一下,构建一个拥有三个独立定时器系统的系统,使用两种不同的方式,会有何区别。使用关联方式的话,就需要在代码中直接操作十五个不同的对象(三个Clock对象,三个Counter对象,三个Textbox对象,和六个Button对象),另外还有一个Frame对象。使用聚合方式的话,代码中只需要直接操作三个对象(三个StopWatch对象)以及另外的一个Frame对象

安全:

通过封装,定时器系统中的那些子对象得到了保护,不会被定时器系统自身之外的元素意外误用了。

特化

StopWatch 类的公有接口,提供了操作,可用来做以下事情,或具有以下特性:

  • •.将多个(或全部)子对象当成一个组来操作。例如,可在StopWatch 类中定义一个单个的方法,用于将三个用户界面子对象(一个Textbox和两个Button)显示出来。

  • •.具有更有意义的名字,能够与子对象的操作所具有的通用名字区分开来。例如,定时器启动之后,所经历过的定时器时间间隔的个数,是由Counter 对象来记录的。Counter的内部数字值,可通过调用该Counter 对象的Value()方法来获取。然而,可以定义一个ElapsedTime()方法,用于返回这个值(自身调用Counter::Value()方法),这样有更明确的意义,因为,这个名字,更直接地表现了这个方法的意图。

结构

封装边界的存在,表达了设计者的意图,那就是,定时器系统中的所有组件,是以整个单位的形式来工作的。它们之间的组织方式和关系,是直接在StopWatch 类中表现的。这个类,可以作为一个与任何特定应用程序都无关的独立实体来学习和理解。

替代

由聚合定义的对象,可被另一个替代实现所替换,而不会影响到系统中的其它部分。所要满足的条件就是,聚合对象的公有接口保持不变。

从以上优点可以看出,发展能够定义且实现聚合的技能,是一个狠重要的目标。

聚合的种类

此处说明两种类型的聚合: 静态聚合 动态聚合 。在静态聚合中,子对象的生命周期与外层包装对象的生命周期一致。那些子对象是在外层包装对象的类中显式声明的,它们随着外层包装对象的构造而构造,随着外层包装对象的销毁而销毁。在动态聚合中,最少有一个只被外层包装对象所知道的子对象是在运行时通过new 操作符来动态创建的。

对于静态和动态聚合之间的区别,有一个直观的例子,那就是,汽车和轮胎店的对比。汽车拥有固定数量的轮胎。轮胎店从工厂进货,再卖轮胎给顾客。对于汽车,轮胎的数量是预先就知道的。然而,对于轮胎店,它的货物(也就是说,汽车轮胎)种类是已知的,但是,在任何时刻的轮胎数量,却是无知的,无法预先确定。

静态聚合,通常更简单、更安全,并且在系统中可以更高效地管理,因而应当尽可能地采用。然而,在某些同样重要的场景中,是需要使用动态聚合的,在那些场景中,子对象的类型是能够在设计时得知的,其数量却无法预先得知。两种形式的聚合,都是狠有用的。一个好的设计者,必须能够区分出二者的特点,并使用最适合手头上问题的方式。

定义一个新的类

聚合,无论是静态的还是动态的,天生就是用来定义新类的。新类的对象,即是聚合中的外层(封装、整体)组件。那些子对象(各组成部分),被声明为新类的私有数据。要想创建高质量的聚合,就必须精通如何定义及实现新类。

在一个好的类定义和实现中,必须拥有若干个重要属性。因为 一个 是用来表达一个 抽象 的,所以,那个类中必须拥有一个好的抽象所具有的所有 属性 而一个类的可执行的表示中, 还必须拥有一个抽象所不具有的额外三个重要属性:

正确 性:

该类的对象,必须正确地维护自己的状态,并且,对于它的方法的调用做出响应,给出符合预期的结果。最严格的正确性级别,是给出一个正式证明。不过,这种级别的证明,通常都只在与安全性严重相关的组件中进行,因为,要证明软件的正确性,成本极高。较常见的是,通过测试来达到不那么严格的正确性级别。无论进行多少数量的测试,都不能确保一个类的正式正确性。但是,通过严格的测试,可以达到有用级别和可控级别的可靠性和可依赖性。

安全 性:

在以常规方式使用时,一个类的某个对象,不应当产生出预料之外的或者有害的效果。尤其是,这个类,在作为参数传递时,或者其对象被赋值给另一个对象时,应当做出安全的行为。在C++中,可以针对复制动作提供特殊的构造函数,可以针对赋值动作提供特定的处理过程。

高效 性:

这个对象,应当高效地使用处理器和内存资源。虽然说,确保高效 性的最重要手段,是进行合理的整体系统设计 以及选择正确的关键数据结构和算法 但是, 类的实现者,也能通过多种手段来提升实现中的效率,例如,将某些元素声明为 常量 ,以及,使用 内联方法

在说明了抽象和类该具有的特点之后,狠显然,可以看到,做出好的面向对象设计,是一件极具创造性和挑战性的事情。

类的实现者,必须能够高效地使用工具。除了能够使用编译器这一必备技能之外,还需要能够在开发和测试阶段高效地使用某个符号调试器。除了最简单的系统之外,任意其它的系统,都需要,当某些代码发生变动时,使用工具来利用源代码(重新)构建出可执行程序。对于简单的系统,每当有任何部分的代码发生变化时,都可以将全部代码重新编译一遍。但是,对于中型的及大型的系统,这种蛮力编译方式就不现实了,因为,用于重新编译及重新链接整个系统的时间会是狠长的。然而,对于实现者来说,要在修改了某部分的代码之后,记住系统中的哪些部分需要重新编译,也是不现实的。因此,必须使用工具,来让(重新)构建的过程高效而准确地进行。

未知美人

未知美人

未知美人

Your opinions
Your name:Email:Website url:Opinion content:
- no title specified

HxLauncher: Launch Android applications by voice commands