面向对象设计的原则210907

本文探讨了面向对象设计的七大原则,包括SRP(单一职责原则)、OCP(开放-封闭原则)、LSP(里氏替换原则)、ISP(接口分离原则)、DIP(依赖倒置原则)、CRP(组合/聚合复用原则)和PLK(最小知识原则),这些原则有助于提高软件的可读性、灵活性和维护性。通过实例解析,阐述了每个原则的含义、实施方法及优缺点,强调了正确设计的重要性,以减少软件的耦合度和提高模块的内聚性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

软件设计七宗罪

  • Rigidity(僵化)–make it hard to change
  • Fragility(脆弱)–make it easy to break
  • Immobility(固化)–make it hard to reuse
  • Viscosity(黏滞)–make it hard to do the right thing
  • NeedlessComplexity(非必要复杂性)–over design
  • NeedlessRepetition(非必要重复)–error prone
  • Not doing any design

面向对象设计的原则

  • SRP:Single Responsibility Principle 单一职责原则
  • OCP:Open-Closed Principle 开放-封闭原则
  • LSP:Liskov Substitution Principle 里氏替换原则
  • ISP:Interface Segregation Principle 接口分离原则
  • DIP:Dependence Inversion Principle 依赖倒置原则
  • CRP:Composite/Aggregate Reuse Principle 组合/聚合复用原则
  • PLK:Principleof Least Knowledge 最小知识原则
  • SOLID让软件系统更易懂、灵活、易于维护,也可以形成迅捷软件开发的方法论

SRP:Single Responsibility Principle 单一职责原则

Definition

  • 从软件变化的角度来看,就一个类而言,应该仅有一个让他发生变化的原因(即职责)
  • 简单来说就是一个类只做一件事

Description

  • 满足单一职责可以提高内聚性,降低耦合
  • 若耦合过高:
    • 难以复用
    • 改变其中一个职责可能会影响其他,使其在改变时很脆弱
  • 比如手机,有很多功能,就是一个多职责的系统,我们离了它就不行

Example

  • 以一个modem的接口为例,这是一个多职责系统,有负责连接(拨号、挂断)、负责通信(收发信息)的功能
    -
  • 此时我们可以把两种功能分开在这里插入图片描述

Kernel

  • SRP原则的核心是正确的抽象,从实现方面来说,核心就是把代码(职责)移到另一个类中,通过调用来使用它们
  • 在面向过程编程时,我们也有类似的思想,我们通常不会写一个很长的函数,而是写多个函数,再依次调用它们

OCP:Open-Closed Principle 开放-封闭原则

Definition

  • Open For Extension
  • Closed For Modification
  • 软件实体(类、模块、函数等等)应该是可以扩展的,但是不可修改的

Description

  • 实现OPC原则的优点
    • 可以不修改现存代码以增加代码的方式实现现有需求,软件适应性和灵活性更好
    • 已有的代码通常已经经过了测试,不适合改

Implementation

  • 主要依赖抽象、多态、继承关系、接口来实现
  • 接口与抽象类都是相对固定的,但它们的实现可以有很多种不同行为
  • 接口适宜扩展,因为它可以有多种实现,但是不适合修改,因为修改后会影响到实现

Example

  • 举个例子,我要在图形编辑器类中判断图形的形状来调用不同的绘制方法
  • 如果我要加一个图形,就要修改GraphicEditor类在这里插入图片描述
  • 我们可以通过多态对其进行修改,对一个抽象方法有多种实现在这里插入图片描述

Kernel

  • OCP原则很难完全被满足,设计者需要预判软件系统可能的变化,将其实现到一个较为合适的程度即可
  • OCP是面向对象设计的核心,抽象是OCP原则的核心
  • 设计方式
    • 传统方式是不要给系统添加新的需求,OCP的方式是在不重新设计的前提下系统支持什么样的变化
  • 不必过度考虑,也不可能完全满足

LSP:Liskov Substitution Principle 里氏替换原则

Definition

  • 任何基类可以出现的地方,子类一定可以出现
  • 当一个子类的实例应该能够替换任何其超类的实例时,它们之间才具有is-A关系
  • 也就是说,只有子类的实例替换任何其他父类的实例时,程序运行结果没有改变,才说它们是is-A的关系

Example

在这里插入图片描述
在这里插入图片描述

  • 这里如果说正方形是长方形,那么计算面积时就有冲突了,就不合理了
  • 可以让正方形和长方形都实现一个四边形的接口在这里插入图片描述

Solution

  • 由以上内容我们知道了子类重写父类的方法其实违背了LSP原则
  • 一种方式是把A、B代码的公共部分放进一个抽象类C,把它们要实现的方法放进一个接口IC,然后让C实现IC在这里插入图片描述
  • 另一种方法是不用继承而是聚合,即人要像车一样跑得快,不需要继承车变成汽车人,只需要拥有一辆车在这里插入图片描述

Conclusion

  • 在设计继承关系时我们一定要仔细考虑LSP原则
  • 只有在有充足的理由时才可以打破这个规则,具体如何以后会说

ISP:Interface Sepregation Principle 接口分离原则

Definition

  • 两个类之间的依赖程度取决于最小的接口
  • 接口要内聚,尽量只把相关程度高的方法放进一个接口,而不是把所有放进一个接口
  • 节省接口数量不能减少代码量,而是会污染接口
  • 有时候接口里没有方法也是有其合理性的
    • 用作指示,Cloneable,Serializable,Remote

Example

在这里插入图片描述

  • 应该改成在这里插入图片描述

Kernel

  • ISP是SRP的接口版本

DIP:Dependence Inversion Principle 依赖倒置原则

  • 也叫Hollywood Principle在这里插入图片描述

Definition

  • A类的某个方法中,使用了B类,那么就说A类依赖于B类
  • 高层模块不应该依赖于低层模决,二者都应该依赖于抽象。进一步的,抽象不应该依赖于细节,细节应该依赖于抽象
  • 如果B类出现漏洞,A也会受到影响

Description

  • 增加灵活性,减少耦合

Implementation

  • 高层模块依赖于接口,底层模块实现接口
  • 人拥有一辆奔驰,如果奔驰出问题了,人就跑不快了,不如直接给人一辆车,具体是什么车人不必知道,如果奔驰坏了,可以换别的车在这里插入图片描述

Example

  • 实例一,一个模块的实现
    在这里插入图片描述
    在这里插入图片描述
  • 实例二,开关与灯
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

Rules

  • 任何变量存储的引用都不能指向实体类,只能指向接口或抽象类
  • 任何类都不能从实体类继承
  • 任何方法都不能重写其基类实现的方法
  • DIP的规则有点过度严格了

Kernel

  • DIP可以解耦,从而为实现OCP提供基础
  • DIP总是针对接口编程,不针对实现编程
  • 当类经常有可能变化时使用DIP,除非是一些工具类和静态类
  • 共同的代码应该放在抽象层,私有的数据之类的要放在实现在这里插入图片描述

关于耦合

  • 三种耦合
    • NilCoupling(零耦合:两个类丝毫不依赖于对方。但只使用零耦合却无法创建出一个有意义的OO系统,因为所有的类都是独立、不相关的,相互之间没有消息的传递,这样最多只能创建出一个类库。)
    • Concrete Coupling
    • Abstract Coupling
  • Abstract Coupling更灵活,也更复杂
  • 当类比较稳定时(即不怎么变化)Concrete Coupling更合适

Conclusion

  • 接口不是万能的,不是银子弹,不稳定的接口可能打破抽象与实现间的隔离
  • 接口应该由使用者的需求定义
  • DIP是面向对象设计的基础与关键

CRP:Composite/Aggregate Reuse Principle 组合/聚合复用原则

Definition

  • 多用组合,少用继承。
  • 组合/聚合复用原则就是在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分;新的对象通过向这些对象的委派达到复用已有功能的目的。

Description

  • 聚合与组合
    • 聚合是HAS-A的关系
    • 组合是整体与部分的关系
  • 面向对象初学者会过度使用继承,最后会产生巨大的僵化的继承关系,CRP原则是为了提醒我们,可以通过更灵活的聚合和组合来实现复用的目的

Example

  • JDK容器类,栈没继承Vector,而是有一个Vector在这里插入图片描述
  • 老古董例子,如果Worker要升值,可能比较困难,需要先移植记忆到新的Manager躯体再人道处理(销毁)
    在这里插入图片描述
  • 这样就方便了,至少我还是我在这里插入图片描述

Kernel

  • 组合/聚合比继承要好
  • 从复用来说
    • 组合/聚合是黑盒复用
    • 继承是白盒复用
  • 从多态来说
    • 组合/聚合是更灵活的多态
    • 继承比较固定
  • 组合/聚合在运行时可修改,继承不能修改

PLK:Principle of Least Knowledge(Law of Demeter) 最小知识原则

  • 因为美国东北大学的德米特里项目出名的

Definition

  • 每个模块只知道自己最近的模块的知识
  • 就是不和陌生人说话

Description

  • 不要让太多类耦合在一起,否则会脆弱且难懂

Advantages

  • 易维护,适应性好

Disadvantage

  • 需要增加许多包装类
  • 某些情况下,会增加时间和空间开销
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值