软件体系结构与设计模式 第5章 面向对象的几个基本原则
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
该设计中的Geometry和Pillar类就是系统 中对修改关闭的部分,而Geometry的子类是对 扩展开放的部分。当我们向系统再增加任何 Geometry的子类时(对扩展开放),不必修改 Pillar类,就可以使用Pillar创建出具有 Geometry的新子类指定的底的柱体。 通常我们无法让设计的每个部分都遵守“开 -闭原则”,甚至不应当这样去做,我们应当把 主要精力集中在应对设计中最有可能因需求变 化而需要改变的地方,然后想办法应用“开-闭 原则”。 当设计某些系统时,我们经常需要面向抽象 来考虑系统的总体设计,不要考虑具体类,这 样就容易设计出满足“开-闭原则”的系统。
3、如果被调用的模块需要使用作为参数传递进来的数据结 构中的所有元素,那么,把整个数据结构作为参数传递就 是完全正确的。但是,当把整个数据结构作为参数传递而 被调用的模块只需要使用其中一部分数据元素时,就出现 了特征耦合。在这种情况下,被调用的模块可以使用的数 据多于它确实需要的数据,这将导致对数据的访问失去控 制,从而给计算机犯罪提供了机会。 4、当两个或多个模块通过一个公共数据环境相互作用时, 它们之间的耦合称为公共环境耦合。公共环境可以是全程 变量、共享的通信区、内存的公共覆盖区、任何存储介质 上的文件、物理设备等等。
总之,耦合是影响软件复杂程度的一个重要因素。应 该采取下述设计原则: 尽量使用数据耦合,少用控制耦合和特征耦合,限制 公共环境耦合的范围,完全不用内容耦合。 2. 内聚 内聚标志一个模块内各个元素彼此结合的紧密程度, 它是信息隐藏和局部化概念的自然扩展。简单地说,理想 内聚的模块只做一件事情。 设计时应该力求做到高内聚,通常中等程度的内聚也 是可以采用的,而且效果和高内聚相差不多;但是,低内 聚很坏,不要使用。 内聚和耦合是密切相关的,模块内的高内聚往往意味 着模块间的松耦合。
(2)接口 接口(interface)具有如下特点: •接口中只可以有public权限的abstract方 法,不能有非abstract方法。 •接口由类去实现,即一个类如果实现一 个接口,那么它必须重写接口中的 abstract方法,即将abstract方法重新声 明,但必须去掉abstract修饰符,同时 要保证声明的方法名字、返回类型、参 数个数和接口中的方法完全相同。
第五章 面向对象的几个基本原则
5.1 面向抽象原则
1.抽象类和接口 (1)抽象(abstract)类 抽象(abstract)类具有如下特点: •抽象类中可以有abstract方法,也可以有非abstract 方法。 •抽象类不能用new运算符创建对象。 •如果一个非抽象类是某个抽象类的子类,那么它必 须重写父类的abstract方法,即在子类中将abstract 方法重新声明,但必须去掉abstract修饰符,同时要 保证声明的方法名字、返回类型、参数个数和类型 与父类的abstract方法完全相同。
//Circle.java public class Circle extends Geometry{ double r; Circle(double r){ this.r = r; } public double getArea(){ return(3.14*r*r); } }
//Rectangle.java public class Rectangle extends Geometry{ double a,b; Rectangle(double a,double b){ this.a = a; this.b = b; } public double getArea(){ return a*b; } }
2.面向抽象
所谓面向抽象编程,是指当设计一个 类时,不让该类面向具体的类,而是面向 抽象类或接口,即所设计类中的重要数据 是抽象类或接口声明的变量,而不是具体 类声明的变量。
//Circle.java public class Circle{ double r; Circle(double r){ this.r = r; } public double getArea(){ return(3.14*r*r); } }
内聚和耦合都是进行模块化设计的有力工具,但是实 践表明内聚更重要,应该把更多注意力集中到提高模块的 内聚程度上。 低内聚有如下几类: 1、如果一个模块完成一组任务,这些任务彼此间即使有关 系,关系也是很松散的,就叫做偶然内聚。有时在写完一 个程序之后,发现一组语句在两处或多处出现,于是把这 些语句作为一个模块以节省内存,这样就出现了偶然内聚 的模块。 2、如果一个模块完成的任务在逻辑上属于相同或相似的一 类,则称为逻辑内聚。 3、如果一个模块包含的任务必须在同一段时间内执行,就 叫时间内聚。
5.3 多用组合少用继承原则
之所以提倡多用组合,少用继承, 是因为在许多设计中,人们希望系统 的类之间尽量是低耦合的关系,而不 希望是强偶合关系。即在许多情况下 需要避开继承的缺点,而需要组合的 优点。
1.继承与复用
• “白盒”复用 • 优点
– 易于修改、扩展被复用的方法
• 缺点
– 无法在运行时改变从父类继承的方法的行为 – 子类和父类是强耦合关系 – 父类的内部细节对于子类而言是可见的
中内聚主要有两类: 4、如果一个模块内的处理元素是相关的,而且必须以特定次 序执行,则称为过程内聚。使用程序流程图作为工具设计软 件时,常常通过研究流程图确定模块的划分,这样得到的往 往是过程内聚的模块。 5、如果模块中所有元素都使用同一个输入数据和(或)产生同 一个输出数据,则称为通信内聚。 高内聚也有两类: 6、如果一个模块内的处理元素和同一个功能密切相关,而且 这些处理必须顺序执行(通常一个处理元素的输出数据作为下 一个处理元素的输入数据),则称为顺序内聚。根据数据流图 划分模块时,通常得到顺序内聚的模块,这种模块彼此间的 连接往往比较简单。
5、最高程度的耦合是内容耦合。 如果出现下列情况之一,两个模块间就发生了内容耦 合: 一个模块访问另一个模块的内部数据; 一个模块不通过正常入口而转到另一个模块的内部; 两个模块有一部分程序代码重叠(只可能出现在汇编程 序中); 一个模块有多个入口(这意味着一个模块有几种功能)。 应该坚决避免使用内容耦合。事实上许多高级程序设 计语言已经设计成不允许在程序中出现任何形式的内容耦 合。
5.2 开-闭原则
•所谓 “开-闭原则”(Open-Closed Principle)就是让你的设计应当Baidu Nhomakorabea扩 展开放,对修改关闭。 •如果您的设计遵守了“开-闭原则”, 那么这个设计一定是易维护的,因为在 设计中增加新的模块时,不必去修改设 计中的核心模块。
如果您的设计遵守了“开-闭原则”,那么这个设计一 定是易维护的,因为在设计中增加新的模块时,不必去 修改设计中的核心模块。
模块独立
模块独立的概念是模块化、抽象、信息 隐藏和局部化概念的直接结果。 开发具有独立功能而且和其他模块之间 没有过多的相互作用的模块,就可以做到模 块独立。换句话说,希望这样设计软件结构, 使得每个模块完成一个相对独立的特定子功 能,并且和其他模块之间的关系很简单。
模块的独立性重要性,主要有两条理由: 第一,有效的模块化(即具有独立的模块)的软件比较容易开发 出来。这是由于能够分割功能而且接口可以简化,当许多人 分工合作开发同一个软件时,这个优点尤其重要。 第二,独立的模块比较容易测试和维护。这是因为相对说来, 修改设计和程序需要的工作量比较小,错误传播范围小,需 要扩充功能时能够“插入”模块。 总之, 模块独立是好设计的关键,而设计又是决定软件质量的 关键环节。
//Application.java public class Application{ public static void main(String args[]){ Pillar pillar; Geometry bottom; bottom = new Rectangle(12,22); pillar = new Pillar(bottom,58); System.out.println(pillar.getVolume()); bottom = new Circle(10); pillar = new Pillar(bottom,58); System.out.println(pillar.getVolume()); } }
//Geometry.java public abstract class Geometry{ public abstract double getArea(); }
//Pillar.java public class Pillar{ Geometry bottom; double height; Pillar(Geometry bottom, double height){ this.bottom = bottom; this.height = height; } public double getVolume(){ return bottom.getArea()*height; } }
模块的独立程度可以由两个定性标准度量,这两个标准分 别称为内聚和耦合。耦合衡量不同模块彼此间互相依赖(连接)的 紧密程度;内聚衡量一个模块内部各个元素彼此结合的紧密程 度。以下分别详细阐述。 1. 耦合 耦合是对一个软件结构内不同模块之间互连程度的度量。耦 合强弱取决于模块间接口的复杂程度。在软件设计中应该追求 尽可能松散耦合的系统。 如果两个模块中的每一个都能独立地工作而不需要另一个 模块的存在,那么它们彼此完全独立,这意味着模块间无任何 连接,耦合程度最低。但是,在一个软件系统中不可能所有模 块之间都没有任何连接。模块间的耦合程度强烈影响系统的可 理解性、可测试性、可靠性和可维护性。
1、如果两个模块彼此间通过参数交换信息,而且交换的信 息仅仅是数据,那么这种耦合称为数据耦合。 2、如果传递的信息中有控制信息(尽管有时这种控制信息以 数据的形式出现),则这种耦合称为控制耦合。 数据耦合是低耦合。系统中至少必须存在这种耦合,因 为只有当某些模块的输出数据作为另一些模块的输入数据 时,系统才能完成有价值的功能。一般说来,一个系统内 可以只包含数据耦合。控制耦合是中等程度的耦合,它增 加了系统的复杂程度。控制耦合往往是多余的,在把模块 适当分解之后通常可以用数据耦合代替它。
//Pillar.java public class Pillar{ Circle bottom; double height; Pillar(Circle bottom, double height){ this.bottom = bottom; this.height = height; } public double getVolume (){ return bottom.getArea()*height; } }
信息隐藏和局部化
应用模块化原理时,自然会产生的一个问题是: “为了得到最好的一组模块,应该怎样分解软件呢?”信息隐 藏原理指出:设计和确定模块,使得一个模块内包含的信 息(过程和数据)对于不需要这些信息的模块来说,是不能访 问的。 局部化的概念和信息隐藏概念是密切相关的。所谓局 部化是指把一些关系密切的软件元素物理地放得彼此靠近。 在模块中使用局部数据元素是局部化的一个例子。显然, 局部化有助于实现信息隐藏。
2.组合与复用
• “黑盒”复用。 • 优点
– 使用委托机制 – 弱耦合关系 – 运行时动态制定所包含的对象
• 缺点
– 容易导致对象过多 – 必须仔细地设计接口
5.4 高内聚-低耦合原则
如果类中的方法是一组相关的行为, 则称该类是高内聚的,反之称为低内聚的。 高内聚便于类的维护,而低内聚不利于类 的维护。 所谓低耦合就是尽量不要让一个类含 有太多的其它类的实例的引用,以避免修 改系统的其中一部分会影响到其它部分, 比如在后面学习中介者模式时,就会体会 到这一原则。
在偶然内聚的模块中,各种元素之间没有实质性联系, 很可能在一种应用场合需要修改这个模块,在另一种应用 场合又不允许这种修改,从而陷入困境。事实上,偶然内 聚的模块出现修改错误的概率比其他类型的模块高得多。 在逻辑内聚的模块中,不同功能混在一起,合用部分 程序代码,即使局部功能的修改有时也会影响全局。因此, 这类模块的修改也比较困难。 时间关系在一定程度上反映了程序的某些实质,所以 时间内聚比逻辑内聚好一些。