第七章 七大软件设计原则

本文介绍了软件设计中的六个核心原则:开闭原则(OCP)、依赖倒置原则、单一职责原则、接口隔离原则、迪米特法则和里氏替换原则,以及它们在提高代码灵活性、可复用性和可维护性方面的应用,强调了封装、抽象和遵循原则的重要性。
摘要由CSDN通过智能技术生成

一、开闭原则

基本介绍
- 开闭原则(Open Close Principle),俗称 OCP 原则,是 Java 世界里最基础的设计原则, 它指导我们如何建立一个稳定的、 灵活的系统。
  
- 怎么定义开闭原则呢?
定义:一个软件实体如类、 模块和函数应该对扩展开放、对修改关闭,用抽象构建框架,用实现扩展细节!
  
- 怎么理解定义?
意思就是说,当软件需要变化时,一个软件实体应该尽量通过扩展来实现变化,而不是通过修改已有的代码来实现变化。
  
- 软实体是?
项目或软件产品中按照一定的逻辑规则划分的模块、类、函数(方法)。
  

作用
- 对软件测试的影响软件遵守开闭原则的话,软件测试时只需要对扩展的代码进行测试就可以了,因为原有的测试代码仍然能够正常运行。
- 可以提高代码的可复用性粒度越小,被复用的可能性就越大;在面向对象的程序设计中,根据原子和抽象编程可以提高代码的可复用性。
- 可以提高软件的可维护性遵守开闭原则的软件,其稳定性高和延续性强,从而易于扩展和维护。
  
怎么使用
- 可以通过“抽象约束”来实现开闭原则。
- “封装变化”来实现开闭原则。
  
对变化的封装包含两层含义:第一,将相同的变化封装到一个接口或抽象类中;第二,将不同的变化封装到不同的接口或抽象类中, 不应该有两个不同的变化出现在同一个接口或抽象类中。 封装变化, 也就是受保护的变化(protected variations) , 找出预计有变化或不稳定的点, 我们为这些变化点创建稳定的接口, 准确地讲是封装可能发生的变化, 一旦预测到或“第六感”发觉有变化, 就可以进行封装。
  
- 通过接口或者抽象类为软件实体定义一个相对稳定的抽象层,而将相同的可变因素封装在相同的具体实现类中。
- 当然,编程规范很重要,约束自己约束团队,制定项目章程。
  
总结
- 通过接口或者抽象的方式将功能拓展出去,达到程序适应多样性的运行。
- 避免修改内部代码,导致引用的部分代码功能无法使用实在运行奔溃等灾难性问题。
- 编程中,`ocp`原则是核心,所有原则都是为了实现 ocp 原则!
  

二、依赖倒置原则

依赖倒置原则也称依赖倒转原则(Dependence Inversion Principle)
  
定义
- 高层模块不应该依赖底层模块,二者都应该依赖其抽象。
- 抽象不应该依赖细节,细节应该依赖抽象。
- 依赖倒置的中心思想是面向接口编程。
  
理解
- 琢磨概念之前先简单说一下 Java 基础:
在 Java 中,抽象指的是接口或者抽象类,而具体的实现类指的是细节。
- 依赖倒置基于细节应该依赖抽象等设计理念,相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架构比以细节为基础的框架要稳定的多。
- 使用接口或抽象类的目的是指定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现去完成。
  
作用
1. 可以降低类间的耦合性。
2. 可以提高系统的稳定性。
3. 可以减少并行开发引起的风险。
4. 可以提高代码的可读性和可维护性。
  
使用原则
1. 底层模块,尽量都要有抽象类或者接口,或者两者都具备,这样程序的稳定性会更好;
2. 变量的声明类型尽量是接口或者是抽象类。
3. 使用继承时尽量遵循里氏替换原则。
  

三、单一职责原则

定义
单一职责原则,就一个类而言,应该仅有一个引起它变化的原因。
  
理解:
- 对类来说,即一个类应该只负责一项职责,如类 A 负责两个不同的职责:职责 1 和职责 2,当职责 1 修改导致类 A 改变时,可能会造成职责 2 执行错误,因此需要将类 A 的粒度分解为 A1 和 A2。
   
总结
- 单一职责原则主要是降低类的复杂度,一个类只负责一项职责;
- 提高类的可读性,可维护性;
- 降低需求新增变更引起的风险;
- 一般情况下,我们应该遵守该原则,特殊情况:
1. 只有逻辑足够简单,才可以在代码级别上违反单一职责原则;
2. 只有类中方法数量足够少,才可以在类级别上违反单一职责原则,而在方法级别上保持单一职责原则。
  
- 需要注意的是:
开发过程中,尽量避免在方法里使用 if else 多分支来处理业务逻辑,随着业务功能的增加,过多的 if else 耦合性非常高,后期很难维护,所以用方法、类来化解多分支来优化代码,实现低耦合。
  

四、接口隔离原则

定义
客户端不应该依赖它不需要的接口。一个类对另一个类的依赖应该建立在最小的接口上。
  
总结
- 客户端不应该依赖它不需要的接口。
- 一个类对另外一个类的依赖,应建立在最小的接口上。
- 其实就是一个接口里不要放一个功能模块外的其他方法,理解起来跟单一职责原则有点像。
  

五、迪米特法则

定义
- 迪米特法则又叫做最少知道原则,就是说一个对象应当对其它对象有尽可能少的了解,不要和陌生人说话。
- 强调只和朋友说话,不和陌生人说话。这里的朋友指的是:出现在成员变量,方法输入,输出参数中的类称为成员朋友类,而出现在方法体内部的类不属于朋友类。
- 迪米特法则初衷在于降低类之间的耦合。由于每个类尽量减少对其它类的依赖,因此。很容易使得系统的功能模块独立,相互之间不存在(或很少有)依赖关系。
  

六、里式替换原则

里氏替换原则(Liskov Substitution Principle,LSP),为什么叫里氏替换?因为是由麻省理工学院的一位姓李的女士提出来的。
  
定义
- 如果对每一个类型为 P 的对象 op1,都有类型为 S 的对象 os1,使得以 P 定义的所有程序 Px 在所有的对象 oP1 都代换成 os1 时,程序 Px 的行为没有发生变化,那么类型 S 是类型 T 的子类型。
- 定义可理解为:所有引用基类的地方必须能透明地使用其子类的对象。
- 白话就是说:用类型为子类的对象 os1 调用 “父类的所有方法” 时,父类没有方法没有被重写(行为没有发生变化),即完全继承。
  
理解
父类的方法子类也可以同等生效,并且将父类用子类替换后,也不会产生任何问题。但是,需要注意的是里氏替换原则告诉我们反过来使用是不行的,子类重写后特有的方法,父类未必适用。
简单粗暴的道理就是:单身爹的遗产儿子继承,儿子的遗产第一继承人不是爹是老婆大人,所以爹和儿子都能继承的遗产是儿子妈的,此时妈妈是基类。
  
总结
使用继承?
- 在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类的方法。
- 继承在给程序设计带来便利的同时也带来了弊端。使程序的可移植性降低,也增强了对象间的耦合性。在父子类中,如果父类需要修改,必须考虑到所有的子类,并且父类修改后,所有涉及到子类的功能都有可能出现问题。
  
如何使用继承?
- 使用继承时,子类中尽量不要重写父类的方法;
即:在实际使用中,采用里氏替换原则应当尽量避免子类的“个性”,一旦子类产生“个性”,这个子类和父类之间的关系将难以调和。
- 所有引用基类的地方必须能透明地使用子类的对象。开发中使用继承会增强两个类之间的耦合性,在适当的情况下,可以通过聚合、组合、依赖来解决问题。这就是在遵守里氏替换原则下,使用继承。
  
里氏替换原则作用
- 采用里氏替换原则的目的是增强程序的健壮性,版本升级时也可以保持非常好的兼容性。即使增加子类,原有的父类仍然可以运行。
  

七、合成复用原则

定义
尽量使用对象组合,而不是继承来达到复用的目的。
  
通常类的复用分为继承复用和合成复用两种。
  
继承复用虽然有简单和易实现的优点,但它也存在以下缺点:
- 继承复用破坏了类的封装性。因为继承会将父类的实现细节暴露给子类,父类对子类是透明的,所以这种复用又称为“白箱”复用。
- 子类与父类的耦合度高。父类的实现的任何改变都会导致子类的实现发生变化,这不利于类的扩展与维护。
- 它限制了复用的灵活性。从父类继承而来的实现是静态的,在编译时已经定义,所以在运行时不可能发生变化。
  
采用组合或聚合复用时,可以将已有对象纳入新对象中,使之成为新对象的一部分,新对象可以调用已有对象的功能,它有以下优点:
- 它维持了类的封装性。因为成分对象的内部细节是新对象看不见的,所以这种复用又称为“黑箱”复用。
- 对象间的耦合度低。可以在类的成员位置声明抽象。
- 复用的灵活性高。这种复用可以在运行时动态进行,新对象可以动态地引用与成分对象类型相同的对象。
  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

方寸之间不太闲

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值