软件设计的七大原则

概述

在软件开发中,为了提高软件系统的可维护性和可复用性,增加软件的可扩展性和灵活性,程序员要尽量根据7大原则来开发程序,从而提高软件开发效率、节约软件开发成本和维护成本
1-单一职责原则:实现类职责要单一
2-里氏替换原则:子类可以代替父类,子类不要重写父类方法
3-开闭原则:要求对扩展开放,对修改关闭
4-依赖倒置原则:面向接口编程
5-接口隔离原则:在设计接口的时候粒度要单一
6-迪米特原则:只与直接的朋友的通信
7-合成复用原则:尽量使用聚合等关联关系,而不是使用继承

相关概念

  1. 可维护性:在不破坏原有代码设计、不引入新的bug的情况下,能够快速地修改或者添加代码
  2. 可扩展性:在不修改或少量修改原有代码的情况下,可以通过扩展的方式添加新的功能代码
  3. 可复用性:尽量减少重复代码的编写,直接复用已有的代码
  4. 内聚性:模块内部元素的紧密程度,内聚性越高,那么模块独立性越好,可维护性、复用性也越高
  5. 耦合性:模块与模块之间的关联关系,耦合性越高,那么模块与模块之间的关联越复杂,那么可维护性、复用性也越差

1-单一职责原则(SRP)

单一职责原则(SRP):Single Responsibility Principe,一个类或者模块只负责完成一个职责(或者功能)。高内聚,低耦合

例如:

  1. 一个“账户”的类中有ID、姓名、电话、省市区等属性
  2. 其中的省市区可以单拎出来作为一个“地址”类
  3. 使该类仅表示地址这一个功能,因此具有单一性
    在这里插入图片描述

2-里氏替换原则(LSP)

里氏替换原则(LSP):Liskov Substitution Principle,子类对象能够替换程序中父类对象出现的任何地方,并且保证原来程序的逻辑行为不变及正确性不被破坏。 通俗理解:子类可以扩展父类的功能,但不能改变父类原有的功能。 换句话说,子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法,如果重写父类的方法,程序运行会发生出错概率。如果一定要用多态,那么父类可以设计成抽象父类或者接口。

例如:

  1. “鹦鹉”类继承“鸟”类,在“鸟”类中写有动物“飞”和“跑”的函数。
  2. 飞行时间=飞行距离/飞行速度
  3. 添加一个“鸵鸟”类,使他继承于“鸟”类,但鸵鸟不会飞
  4. 也就是说“鸵鸟”飞行速度为0,则运行程序,其飞行时间将会报错
  5. 因此,抽象出一个“动物”类,将“鸟”类中“跑”的方法添加到“动物”类中
  6. 跑的时间=跑的距离/跑的速度
  7. 使“鸟”类继承于“动物”类
  8. “鸵鸟”类不再继承“鸟”类,而是继承“动物”类,从而可以实现跑的动作
  9. 在该程序中,“鸟”类可以扩展“动物”类的方法,而不改变“动物”类原有的方法。将“动物”类设计成了抽象父类
    在这里插入图片描述

3-开闭原则(OCP)

开闭原则(OCP):Open Closed Princide:对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有代码,实现一个热插拔的效果。 简言之,是为了使程序的扩展性更好,易于维护和升级。 想要达到这样的效果,需要使用接口和抽象类。因为抽象类灵活性好,适应性广,只要抽象的合理,可以基本保持软件架构的稳定。而软件中易变的细节可以从抽象派生来的实现类来进行扩展,当软件需要发生变化时,只需要根据需求重新派生一个实现类来扩展就可以了。

例如:

  1. 要实现计算功能,有一个“计算”类,其中有计算加法的方法
  2. 如果需要继续添加减法、乘法等其他功能的话
  3. 需要修改这个“计算”类中的内容,如果该类之后与其他类有所关联,则可能会导致出错
  4. 所以需要对扩展开放,对修改关闭
  5. 将“计算”类设计为抽象父类,将所有计算方法设置为抽象方法
  6. 将其他计算设计为类,如“加法”类、“减法”类,让这些类去继承“计算”类
  7. 这些类中有实现自己计算功能的方法,之后如果想要增加其他的计算功能,如“乘法”、“除法”,就可以直接增加 “乘法”、“除法”的类,去继承于“计算”类即可,无需修改“计算”类中的内容,即对扩展开放,对修改关闭

在这里插入图片描述

抽象类:

  • 抽象类是一种不能被实例化的类,只能被继承。通常用于作为其他类的基类。
  • 抽象类可以包含抽象方法和非抽象方法。
  • 抽象类可以定义抽象属性、常量和普通属性等。
  • 抽象类可以提供默认实现或共享的代码,以减少子类之间的代码重复。
  • 抽象类的主要作用是为其子类提供共同的接口和规范,以便子类可以继承和实现。

抽象方法:

  • 抽象方法是一种没有具体实现的方法,只有方法的声明而没有方法体。
  • 抽象方法必须在抽象类中声明,并且标记为抽象关键字。
  • 抽象方法没有方法体,因此无法直接调用,必须由子类来实现具体的方法体。
  • 子类继承抽象类后,必须重写(实现)所有的抽象方法,否则子类也需要标记为抽象类。

抽象类和抽象方法的主要作用是:

  • 提供一种接口或规范,强制子类实现指定的方法。通过抽象类和抽象方法,可以实现代码的模板设计和扩展性。
  • 实现代码的复用,抽象类中可以提供通用的方法实现,子类可以直接继承并使用这些方法。
  • 增加代码的可读性和可维护性,通过抽象类和抽象方法可以更清晰地表示类之间的关系和行为约定。

4-依赖倒转原则(DIP)

依赖倒转原则(DIP):Dependency Inversion Principle,模块之间要依赖抽象,不依赖实现,要面向接口编程,不要面向实现编程。高层次模块不应该直接依赖低层模块,这样就降低了客户端与实现模块间的耦合。

例如:

  1. 有个“Computer”类,与它关联的有“InterCpu”和“InterMemory”类,直接面向实现类编程,实现启动电脑
  2. 那现在这个程序的扩展性如何呢?如果我想给电脑配备AmdCpu,该如何实现?需要在实现类中去修改,有没有更好的办法?
  3. 创建两个接口,“Cpu”和“Memory”接口
  4. 使“Computer”类关联这两个接口
  5. 创建“InterCpu”和“AmdCpu”类,使其实现Cpu接口;创建“InterMemory”和“AmdMemory”类,使其实现Memory接口;
  6. 在“Test”类中就可以实现“AmdComputer”的功能
  7. 之后如还需改为“HuaweiComputer”,只需添加“HuaweiCpu”和“HuaweiMemery”,并在测试类中做修改即可。“Computer”类不需做任何变化

在这里插入图片描述

5-接口隔离原则(ISP)

接口隔离原则(ISP):Interface Segregation Principle,接口最小粒度设计。客户端不应该被迫依赖于它不使用的方法;一个类对另一个类的依赖应该建立在最小的接口上。一个类实现一个接口,就必须实现这个接口的所有抽象方法,如果接口设计的过于庞大的话,实现类就被迫实现不需要的抽象方法。

例如:开发一套手机接口功能PhoneFunction,包含通话call,短信message,摄像camera功能,基于这套接口开发了最新的苹果手机ApplePhone

  1. 创建一个“PhoneFunction”接口,包含三个功能
  2. 根据这三个功能,创建一个“ApplePhone”实现类
  3. 根据市场需求,需要开发一款老年机,只具有电话、短信功能
  4. 此时“OldPhone”就要被迫实现“camera”方法,不合理
  5. 所以需要重新设计接口,将通话call,短信message,摄像camera功能各自设计为最小粒度的接口
  6. 使“ApplePhone”和“OldPhone”根据各自需要的功能去实现相应的接口
    在这里插入图片描述

6-迪米特法则(LOD)

迪米特法则(LOD):Law of Demeter,迪米特法则来自于1987年美国东北大学的一个名为Demeter的一个研究项目,只跟朋友联系,不跟“陌生人”说话。如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性。

例如:明星由于全身心投入艺术,所以许多日常事务由经纪人负责处理,如和粉丝的见面会,和媒体公司的业务洽谈等。这里的经纪人是明星的朋友,而粉丝和媒体公司是陌生人,所以适合迪米特法则。

在这里插入图片描述

7-合成复用原则(CRP)

合成复用原则(CRP):Composite Reuse Principle,尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现
通常类的复用分为继承复用和合成复用两种。
继承复用虽然有简单和易实现的优点,但它也存在以下缺点:

  1. 继承复用破坏了类的封装性。因为继承会将父类的实现细节暴露给子类,父类对子类是透明的所以这种复用又称为**“白箱”**复用。
  2. 子类与父类的耦合度高。父类的实现的任何改变都会导致子类的实现发生变化,这不利于类的扩展与维护。

采用组合或聚合复用时,可以将已有对象纳入新对象中,使之成为新对象的一部分,新对象可以调用已有对象的功能,它有如下优点:

  1. 它维持了类的封装性。因为成分对象的内部细节是新对象看不见的,所以这复用又称为**“黑箱”**复用。
  2. 对象间的耦合度低。可以在类的成员位置声明抽象 (抽象类或者接口)

示例

  1. 创建一个A类和B类,让B类继承A类
  2. 可以通过B类访问父类(A类)中的所有内容,只要父类发生变化,子类中必将发生变化
  3. 使用聚合(或组合)的关系,使B类中包含A类
  4. 因此,B类中可以调用A类的方法,但相比于聚合复用,降低了类之间的耦合性。即A类的变化与B类没有任何关系。

在这里插入图片描述

示例2:手机软件划分为QQ、微信等,按品牌可划分为华为、小米等。如果同时考虑这两种分类,其组合就很多。往下继续扩展软件、手机品牌,都会新增许多子类,增加了耦合度,限制了复用性。

在这里插入图片描述

软件设计原则-波波酱老师-哔哩哔哩

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值