软件设计七大原则

依赖、关联、组合、聚合

  • 依赖(Dependency)关系是类与类之间的联接。依赖关系表示一个类依赖于另一个类的定义。例如,一个人(Person)可以买车(car)和房子(House),Person类依赖于Car类和House类的定义,因为Person类引用了Car和House。与关联不同的是,Person类里并没有Car和House类型的属性,Car和House的实例是以参量的方式传入到buy()方法中去的。一般而言,依赖关系体现为局域变量、方法的形参,或者对静态方法的调用。
  • 关联(Association)关系是类与类之间的联接,它使一个类知道另一个类的属性和方法。关联可以是双向的,也可以是单向的。关联关系一般使用成员变量来实现。
  • 聚合(Aggregation) 关系是关联关系的一种,是强的关联关系。聚合是整体和个体之间的关系。例如,汽车类与引擎类、轮胎类,以及其它的零件类之间的关系便整体和个体的关系。与关联关系一样,聚合关系也是通过实例变量实现的。但是关联关系所涉及的两个类是处在同一层次上的,而在聚合关系中,两个类是处在不平等层次上的,一个代表整体,另一个代表部分。
  • 组合(Composition) 关系是关联关系的一种,是比聚合关系强的关系。它要求普通的聚合关系中代表整体的对象负责代表部分对象的生命周期,组合关系是不能共享的。代表整体的对象需要负责保持部分对象和存活,在一些情况下将负责代表部分的对象湮灭掉。代表整体的对象可以将代表部分的对象传递给另一个对象,由后者负责此对象的生命周期。换言之,代表部分的对象在每一个时刻只能与一个对象发生组合关系,由后者排他地负责生命周期。部分和整体的生命周期一样。
  • 由定义我们已经知道,依赖关系实际上是一种比较弱的关联,聚合是一种比较强的关联,而组合则是一种更强的关联,所以笼统的来区分的话,实际上这四种关系、都是关联关系。

1.开-闭原则

定义

《Object Oriented Software Construction》写到:软件实体应当对扩展开放,对修改关闭。

通俗解释

软件系统中包含的各种组件,例如模块、类以及功能等等,应该在不修改现有代码的基础上,引入新功能。开闭原则中“开”,是指对于组件功能的扩展是开放的,是允许对其进行功能扩展的;开闭原则中“闭”,是指对于原有代码的修改是封闭的,即不应该修改原有的代码。

如何实现开闭原则

实现开闭原则的关键就在于“抽象”。把系统的所有可能行为抽象成一个抽象底层,这个抽象底层规定出所有的具体实现必须提供的方法的特征。作为系统设计的抽象层,要预见所有可能的扩展,从而使得在任何扩展情况下,系统的抽象底层不需修改;同时,由于可以从抽象底层导出一个或多个新的具体实现,可以改变系统的行为,因此系统设计对扩展是开放的。
关于系统可变的部分,还有一个更具体的原则是对可变性的封装原则,它从软件工程实现的角度对开闭原则进行了进一步的解释。EVP要求在做系统设计的时候,对系统所有可能发生变化的部分进行评估和分类,每一个可变的因素都单独进行封装。“对可变性的封装原则”意味着两点:
(1)一种可变性不应当散落在代码的很多角落里,而应当被封装到一个对象里面。同一种可变性的不同表象意味着同一个继承等级结构中的具体子类,因此,我们可以期待在设计模式中看到继承关系。继承应当被看做是封装变化的方法,而不应当被认为是从一般的对象生成特殊的对象的方法。
(2)一种可变性不应当与另一种可变性混合在一起。

2.里氏代换原则(LSP)

里氏代换原则面向对象设计的基本原则之一。

定义

里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。

4层含义

  1. 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。
  2. 子类中可以增加自己特有的方法。
  3. 当子类的方法重载父类的方法时,方法的重置条件(即方法的形参)要比父类方法的输入参数更宽松。
  4. 当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更加严格。

3.依赖倒置原则

定义

依赖倒转原则就是要依赖于抽象,不要依赖于实现。要针对接口编程,不要针对实现编程。也就是说应当使用接口和抽象类进行变量的类型声明、参数类型声明、方法返回类型说明,以及数据类型的转换等。要保证做到这一点,一个具体类应当只实现接口和抽象类中声明过的方法,而不要给出多余的方法。

耦合(依赖)关系的种类

零耦合关系:两个类没有耦合关系
具体耦合关系:发生在两个具体的(可实例化的)类之间,经由一个类对另一个具体类的直接引用造成的。
抽象耦合关系:发生在一个具体类和一个抽象类(或接口)之间,使两个必须发生关系的类之间存有最大的灵活性

如何把握耦合

我们应该尽可能的避免实现继承,原因如下:
1. 失去灵活性,使用具体类会给底层的修改带来麻烦。
2. 耦合问题,耦合是指两个实体相互依赖于对方的一个量度。程序员每天都在(有意识或者无意识地)做出影响耦合的决定:类耦合、API耦合、应用程序耦合等等。在一个用扩展的继承实现系统中,派生类是非常紧密的与基类耦合,而且这种紧密的连接可能是不被期望的。我们必须客观的评价耦合度,系统之间不可能总是松耦合的,那样肯定什么也做不了。

4.接口隔离原则

定义

建立单一接口,不要建立臃肿庞大的接口。接口尽量细化,同时接口中的方法尽量少。

注意

  1. 接口尽量小,但是要有限度。对接口进行细化可以提高程序设计灵活性是不争的事实,但是如果过小,则会造成接口数量过多,使设计复杂。所以一定要适度。
  2. 为依赖接口的类定制服务,只暴露给调用的类它需要的方法,它不需要的方法则隐藏起来。只有专注地为一个模块提供定制服务,才能建立最小的依赖关系。
  3. 提高内聚,减少对外交互。使接口用最少的方法去完成最多的事情。

5.合成/聚合复用原则

定义

在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分;新的对象通过向这些对象的委派,达到复用这些对象的目的。应首先使用合成/聚合,使系统灵活,其次才考虑继承,达到复用的目的。而使用继承时,要严格遵循里氏代换原则。有效地使用继承会有助于对问题的理解,降低复杂度,而滥用继承会增加系统构建、维护时的难度及系统的复杂度。

6.迪米特法则(最少知道法则)

定义

一个软件实体应当尽可能少的与其他实体发生相互作用。这样,当一个模块修改时,就会尽量少的影响其他的模块。扩展会相对容易。
这样对软件实体之间通信的限制。它要求限制软件实体之间通信的宽度和深度。
软件编程的总的原则:低耦合,高内聚。

7.单一职责原则

定义

一个类,应该只有一个职责。每一个职责都是变化的一个轴线,如果一个类有一个以上的职责。这些职责就耦合在了一起。这会导致脆弱的设计。当一个职责发生变化时,可能会影响其它的职责。另外,多个职责耦合在一起,会影响复用性。我们可能只需要复用该类的某一个职责,但这个职责跟其它职责耦合在了一起,很难分离出来。

优点

  1. 可以降低类的复杂度,一个类只负责一项职责,其逻辑肯定要比负责多项职责简单的多;
  2. 可以提高类的可读性,提高系统的可维护性;
  3. 变更引起的风险率降低,变更时必然的,如果单一职责原则遵守的好,当修改一个功能时,可以显著降低对其他功能的影响。

组合/聚合复用原则

定义

要尽量的使用合成和聚合,而不是继承关系达到复用的目的。
该原则就是在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分;新的对象通过向这些对象的委派达到复用已有功能的目的。

组合/聚合复用优点

  1. 新对象存取成分对象的唯一方法是通过成分对象的接口;
  2. 这种复用是黑箱复用,因为成分对象的内部细节是新对象所看不到的;
  3. 这种复用支持包装;
  4. 这种复用所需的依赖较少;
  5. 每一个新的类可以将焦点集中在一个任务上;
  6. 这种复用可以在运行时动态进行,新对象可以使用合成/聚合关系将新的责任委派到合适的对象。

缺点

  1. 通过这种方式复用建造的系统会有较多的对象需要管理

继承复用优点

新的实现较为容易,因为基类的大部分功能可以通过继承关系自动进入派生类;
修改或扩展继承而来的实现较为容易;

继承复用缺点

继承复用破坏包装,因为继承将基类的实现细节暴露给派生类,这种复用也称为白箱复用;如果基类的实现发生改变,那么派生的实现也不得不发生改变;从基类继承而来的实现是静态的,不可能在运行时发生改变,不够灵活;

最终目的:高内聚、低耦合。

耦合性也称块间联系。指软件系统结构中各模块间相互联系紧密程度的一种度量。模块之间联系越紧密,其耦合性就越强,模块的独立性则越差。模块间耦合高低取决于模块间接口的复杂性、调用的方式及传递的信息。

  1. 无直接耦合:
  2. 数据耦合: 指两个模块之间有调用关系,传递的是简单的数据值,相当于高级语言的值传递;
  3. 标记耦合: 指两个模块之间传递的是数据结构,如高级语言中的数组名、记录名、文件名等这些名字即标记,其实传递的是这个数据结构的地址;
  4. 控制耦合: 指一个模块调用另一个模块时,传递的是控制变量(如开关、标志等),被调模块通过该控制变量的值有选择地执行块内某一功能;
  5. 公共耦合: 指通过一个公共数据环境相互作用的那些模块间的耦合。公共耦合的复杂程序随耦合模块的个数增加而增加。
  6. 内容耦合: 这是最高程度的耦合,也是最差的耦合。当一个模块直接使用另一个模块的内部数据,或通过非正常入口而转入另一个模块内部。

内聚性又称块内联系。指模块的功能强度的度量,即一个模块内部各个元素彼此结合的紧密程度的度量。若一个模块内各元素(语名之间、程序段之间)联系的越紧密,则它的内聚性就越高。
内聚性匪类(低――高): 偶然内聚;逻辑内聚;时间内聚;通信内聚;顺序内聚;功能内聚;

  1. 偶然内聚: 指一个模块内的各处理元素之间没有任何联系。
  2. 逻辑内聚: 指模块内执行几个逻辑上相似的功能,通过参数确定该模块完成哪一个功能。
  3. 时间内聚: 把需要同时执行的动作组合在一起形成的模块为时间内聚模块。
  4. 通信内聚: 指模块内所有处理元素都在同一个数据结构上操作(有时称之为信息内聚),或者指各处理使用相同的输入数据或者产生相同的输出数据。
  5. 顺序内聚: 指一个模块中各个处理元素都密切相关于同一功能且必须顺序执行,前一功能元素输出就是下一功能元素的输入。
  6. 功能内聚: 这是最强的内聚,指模块内所有元素共同完成一个功能,缺一不可。与其他模块的耦合是最弱的。

耦合性与内聚性是模块独立性的两个定性标准,将软件系统划分模块时,尽量做到高内聚低耦合,提高模块的独立性,为设计高质量的软件结构奠定基础。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值