Chapter 6 设计模块

6.1 设计原则

模块(module)与构件(component)

模块:定义输入、输出和特性的程序实体构件:可重复使用的软件组件

设计原则是指把系统功能和行为分解成模块的指导方针6种重要的原则:

模块化、接口、信息隐藏、增量式开发、抽象、通用性

模块化

模块化是一种把系统中各不相关的部分进行分离的原则,以便于各部分能够独立研究,也称为关注点分离

•如果该原则运用得当, 每个模块都有自己唯一的目的,并且相对独立于其它模块

–每个模块理解和开发会更加简单

–故障定位更加简单 (because there are fewer suspect modules per fault)

–系统修改更加简单 (because a change to one module affects relatively few other modules

•为了确定是否很好地分离了关注点,我们使用两个概念来度量模块的独立程度:耦合度 和 内聚度

耦合度

耦合衡量不同模块彼此间互相依赖的紧密程度。

两个模块之间存在着很强的依赖关系称为紧密耦合,两个模块之间存在较少依赖关系称为松散耦合,模块之间没有任何依赖关系称为无耦合。耦合越松散,模块之间的联系就越小,模块的独立性就越强。

耦合的强度依赖于以下因素:

(1)一个模块对另一个模块的引用**

如模块A调用模块B那么模块A的功能依赖于模块B的功能

(2)传递数据

(3)施加控制

(4)接口复杂程度

非直接耦合

两个模块都能独立地工作而不需要另一个的存在,这表明模块间无任何连接,耦合程度最低。

数据耦合

如果两个模块彼此间通过参数交换信息,而且交换的信息仅仅是数据,那么这种耦合称为数据耦合。数据耦合是最简单的耦合形式,模块间耦合程度较低。

标记耦合

两个模块间传递的参数是数据结构变量,如高级语言中的数组名、记录名和文件名等,模块间的这种联系称为标记耦合。

在设计时应尽量避免或采用其它方法消除这种耦合,但有时因为其它功能的缘故,标记耦合是不可避免的。

控制耦合

两个模块间传递的是控制信息,如开关量、标志和名字等,则这两个模块之间的耦合称为控制耦合。

公共耦合

一组模块都访问同一个公共数据环境,则这组模块之间的耦合就称为公共耦合。公共数据环境可以是全局数据结构、共享的通信区、内存的公共覆盖区、任何存储介质上的文件等。

如果一个模块只向公共数据环境传送数据,而另一个模块只从公共数据环境中提取数据,则这种公共耦合称为松散公共耦合,如下图a所示

如果两个模块对既往公共数据环境输送数据又从里面提取数据,则这种耦合称为紧密公共耦合,如下图b所示。

如使用公共耦合,应尽量采用松散公共耦合

内容耦合

内容耦合是最高程度的耦合,如果发生下列情形,两模块之间就发生了内容耦合:

①一个模块直接访问另一个模块的内部数据。

②一个模块不通过正常入口转到另一模块内部。

③两个模块有一部分程序代码重叠

内聚度

内聚是衡量一个模块内部各个元素彼此结合的紧密程度。一个模块内聚程度越高,说明该模块内部各元素之间的关联也就越强

偶然内聚

模块内部各个元素在功能上不相关或者关系松散。

偶然内聚被认为是最差的一种内聚,其缺点是:模块不易理解,不易维护,不易重用。

逻辑内聚

一个模块完成的任务在逻辑上属于相同或相似的一类则称为逻辑内聚。下图中被调用模块就是一个逻辑内聚模块,它根据输入的控制信息判定执行相应的功能

时间内聚

一个模块包含了需要在同一时间段中执行的多个任务,则称该模块的内聚为时间内聚。例如,将多个变量的初始化放在同一个模块中实现。

优点:不需要选择参数,所有任务可以任意次序执行,逻辑简单。

缺点:这种模块结合了许多无关的任务,模块一旦失效,难以直接确定是哪一个任务失效。

过程内聚

一个模块内的处理元素是相关的,而且必须以特定次序执行,则称为过程内聚。例如,在利用流程图划分模块时,如果将流程图中完成同一个处理的循环部分、判定部分、计算部分分成3个模块,则这3个模块就是过程内聚模块。

6.2 面向对象的设计方法

面向过程:SP

面向对象:OOP

面向对象设计原则概述

必考一个

单一职责原则

在软件系统中,一个类只负责一个功能领域中的相应职责

就一个类而言,应该就有一个引起他变化的原因

类的职责主要包括两个方面:数据职责和行为职责,数据职责通过其属性来体现,而行为职责通过其方法来体现。单一职责原则是实现高内聚、低耦合的指导方针,在很多代码重构手法中都能找到它的存在,它是最简单但又最难运用的原则,需要设计人员发现类的不同职责并将其分离,而发现类的多重职责需要设计人员具有较强的分析设计能力和相关重构经验。

例子:

开闭原则

一个软件实体应当对扩展开放,对修改关闭。

也就是说在设计一个模块的时候,应当使这个模块可以在不被修改的前提下被扩展,即实现在不修改源代码的情况下改变这个模块的行为。在开闭原则的定义中,软件实体可以指一个软件模块、一个由多个类组成的局部结构或一个独立的类

里氏代换原则

所有引用基类(父类)的地方必须能透明地使用其子类的对象

在软件中如果能够使用基类对象,那么一定能够使用其子类对象。把基类都替换成它的子类,程序将不会产生任何错误和异常,反过来则不成立,如果一个软件实体使用的是一个子类的话,那么它不一定能够使用基类。

里氏代换原则是实现开闭原则的重要方式之一,由于使用基类对象的地方都可以使用子类对象,因此在程序中尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类对象

依赖倒转原则

–依赖倒转原则(Dependence Inversion Principle, DIP)的定义如下:

•高层模块不应该依赖低层模块,它们都应该依赖抽象。抽象不应该依赖于细节,细节应该依赖于抽象。

–另一种表述为:

•要针对接口编程,不要针对实现编程。

–简单来说,依赖倒转原则就是指:代码要依赖于抽象的类,而不要依赖于具体的类;要针对接口或抽象类编程,而不是针对具体类编程。

合成复用

尽量使用对象组合,而不是继承来达到复用的目的

迪米特法则

又称为最少知识原则–它有多种定义方法,其中几种典型定义如下:

•(1) 不要和“陌生人”说话。

•(2) 只与你的直接朋友通信。

•(3) 每一个软件单位对其他的单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位。

迪米特法则就是指一个软件实体应当尽可能少的与其他实体发生相互作用。这样,当一个模块修改时,就会尽量少的影响其他的模块,扩展会相对容易,这是对软件实体之间通信的限制,它要求限制软件实体之间通信的宽度和深度。

6.3 面向对象设计模式

**设计模式(Design pattern)**是一套被反复使用、多数人知晓

的、经过分类编目的、代码设计经验的总结。

为何提倡设计模式?

根本原因是为了代码复用,增加可维护性 。

设计模式有助于对框架结构的理解,成熟的框架通常使用了多

种设计模式。

•设计模式从应用的角度被分为三个大的类型

–创建型模式

–结构型模式

行为型模式

创造型设计模式

单例模型

保证一个类有且仅有一个实例,提供一个全局访问点

工厂方法 (Factory Method)

父类负责定义创建对象的公共接口,而子类则负责生成具体对象,将类的实例化操作延迟到子类中完成

抽象工厂(Abstract Factory)

为一个产品族提供统一的创建接口。当需要这个产品族的某一系列的时候,可以从抽象工厂中选出相应的系列创建一个具体的工厂类

建造者模式 (Builder)

将复杂对象的构建与它的表示分离,同样的构建过程可以创建不同的表示。允许用户可以只通过指定复杂对象的类型和内容就可以构建它们,用户不知道内部的具体构建细节

原型模式

通过“复制”一个已经存在的实例来返回新的实例,而不是新建实例。被复制的实例就是我们所称的“原型”,这个原型是可定制的。原型模式多用于创建复杂的或者耗时的实例,因为这种情况下,复制一个已经存在的实例使程序运行更高效;或者创建值相等,只是命名不一样的同类数据

结构型设计模式

•结构型模式讨论的是类和对象的结构,它采用继承机制来组合接口或实现(类结构型模式),或者通过组合一些对象,从而实现新的功能(对象结构型模式)

–合成(Composite)模式:定义一个接口,使之可以用于单一对象,也可以应用于多个单一对象组成的对象组

–装饰(Decorator)模式:动态给一个对象添加一些额外的职责,就好像给一个物体加上装饰物,完善其功能

–代理(Proxy)模式:在软件系统中,有些对象有时候由于跨越网络或者其他的障碍,而不能够或者不想直接访问另一个对象,如果直接访问会给系统带来不必要的复杂性,这时候可以在客户程序和目标对象之间增加一层中间层,让代理对象来代替目标对象打点一切,这就是代理(Proxy)模式

–享元(Flyweight)模式:Flyweight是一个共享对象,它可以同时在不同上下文(Context)使用

–外观(Facade)模式:外观模式为子系统提供了一个更高层次、更简单的接口,从而降低了子系统的复杂度和依赖。这使得子系统更易于使用和管理。外观承担与子系统中类交互的责任

–桥梁(Bridge)模式:桥梁模式的用意是将问题的抽象和实现分离开来实现,通过用聚合代替继承来解决子类爆炸性增长的问题

–适配器(Adapter)模式:将一个类的接口适配成用户所期待的接口。一个适配器允许通常因为接口不兼容而不能在一起工作的类工作在一起,做法是将类自己的接口包装在一个已存在的类中

行为型设计模式

•着力解决的是类实体之间的通讯关系,希望以面向对象的方式描述一个控制流程

–模版方法(Template Method):定义了一个算法步骤,并允许子类别为一个或多个步骤提供其实现方式。让子类别在不改变算法架构的情况下,重新定义算法中某些步骤

–观察者(Observer)模式:定义了对象之间一对多的依赖,当这个对象的状态发生改变的时候,多个对象会接受到通知,有机会做出反馈

–迭代子(Iterator)模式:提供一种方法顺序访问一个聚合对象中各个元素, 而又不需暴露该对象的内部表示

单例模式

单例设计模式的特点:

1.单例设计模式保证一个类只有一个实例;

2.要提供一个访问该类对象实例的全局访问点。

单例模式最重要的就是要保证一个类只有一个实例并且这个类

易于被访问。

一个全局类使得一个对象可以被访问,但是这样做却不能防止

你实例化多个对象。

1.为了避免其它程序过多的建立该类的对象,先禁止其它程序建

立该类对象实例(将构造器私有化)。

2.为了方便其它程序访问该类的对象,只好在本类中自定义一个

对象,由1可知该对象是static的,并对外提供访问方式。

饿汉式(总结)

对象预先加载,线程是安全的,在类创建好的同时对象生成,

调用获得对象实例的方法反应速度快,代码简练。

懒汉式(总结)

对象延迟加载,效率高,只有在使用的时候才实例化对象,若

设计不当线程会不安全,代码相对于饿汉式复杂,第一次加

载类对象的时候反应不快。

参考链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: