软件设计六大原则

软件设计七大原则

所谓的软件设计原则,就是开发人员在长久的软件开发过程中整理总结的约定俗成的设计原则。遵循这些原则就能够对软件的开发和维护起到很好的作用。说白了就是程序员在编写代码时高瞻远瞩的设计思想。其中这种思想应用较多的就是设计模式。

七大原则分别为:

1.开闭原则

2.单一职责原则

3.依赖倒置原则

4.接口隔离原则

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

6.里氏替换原则

7.合成复用原则

其中第七条合成复用原则有的文章里把这个去掉了变成了六大设计原则

一、开闭原则

开闭原则,Software entities like classes, modules and functions should be open for extension but closed for modifications,软件实体比如类,模块,和函数应该对拓展开放,对修改关闭,也就是尽量通过拓展实体行为来实现变化。

特点:

  • 通过接口或者抽象类约束扩展,对扩展进行边界限定,不允许出现在接口或抽象类中不存在的public方法
  • 参数类型、引用对象尽量使用接口或者抽象类,而不是实现类
  • 抽象层尽量保持稳定,一旦确定即不允许修改

开闭原则是面向对象设计中最基础的设计原则,它指导我们如何建立稳定灵活的系统,开闭原则只定义了对修改关闭,对扩展开放。其实只要遵循后面5种设计模式,设计出来的软件就是符合开闭原则的。回想起实际开发时,一个版本上线,下一次开发时需要增加新的功能,在接口里新添一个方法,导致所有实现了该接口的类都要修改,甚至其他引用了该类所在jar包的项目都要修改,而这个项目甚至有可能不是你所在小组负责的。那么这种设计就是不符合开闭原则的。

二、单一职责原则

单一职责原则的定义是:“There should never be more than one reason for a class to change.”,也就是有且仅有一个原因引起类的变更。这样可以降低类的复杂性,实现什么职责都有清晰明确的定义;可读性提高;可维护性提高;变更引起的风险降低。

这个应该比较好理解,一个类如果负责的功能太多,那么代码的耦合度可能会大幅度提升,不仅给代码可读性带来了巨大的难度,代码的维护性上也大幅度降低。想一想,当你接到一个任务去修改这个类或者与这个类有关的类的时候,发现由此而引发的“雪崩”效应,牵一发而动全身,引出了多个类需要修改,而这些类可能与你要实现的功能毫不相关,恰巧开发这个类的人已经离职了,那么可想而知会有多么头疼了。

三、依赖倒置原则

依赖倒置原则,模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的;

接口或者抽象类不依赖于实现类;

实现类依赖与接口或抽象类。

需要每个类尽量都有接口或者抽象类;

变量的表面类型尽量是接口或者抽象类;

任何类都不应该从具体类派生(超过两层), 尽量不要复写基类的方法。

采用依赖倒置原则可减少类间的耦合性,提高系统的稳定性,降低并行开发引起的风险,提高代码可读性和可维护性。依赖也可以称为注入,一般可以通过构造函数注入,属性注入,接口注入。

程序要依赖于抽象接口,不要依赖于具体实现。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。高层模块不应该依赖低层模块。两个都应该依赖抽象。

抽象不应该依赖细节。细节应该依赖抽象。

我曾给朋友举个这么个例子,在一个寝室中,很多人懒得去打饭。

所以一旦有一个人先说“我去食堂吃饭了!”那么其余的人一定会异口同声的说“给我也带一份”。

去食堂打饭的人会说,“好,那吃什么由我来决定!”

这个同学去食堂打饭选择了自己喜欢吃的炸鸡,有可能给其他几个同学也买了炸鸡,也有可能看到其他窗口排队的人少也给他们买了不一样的菜。

回到设计原则来说,去食堂打饭的同学就是抽象,买什么菜由这个同学决定而不是其他的人。其余的人每个人之间也没有联系,他们全都依赖这个去食堂打饭的人。这样,本身应该自己去买饭,变成了所有人依赖一个人去买饭,这样就依赖反转了。

记得刚学springIOC的时候,一直在讲依赖注入和控制反转。当时只是死记硬背这么个特点。当在后面接触了才明白,我们在一个类里面如果需要引入另一个对象 正常应该 new一个新对象,并赋值。但是这样做就产生了依赖。有了spring以后,spring就接管了这个创造对象的过程,依赖就变成了所有类依赖spring。具体如何接管实现请看相关文章。

四、接口隔离原则

接口隔离原则的定义是接口尽量细化,同时接口中的方法尽量少。需要一个接口只服务于一个子模块或者业务逻辑;通过业务逻辑尽量压缩接口中的方法;这样可以提高内聚,降低耦合,重用代码。

客户端不应该依赖它不需要的接口,类间的依赖关系应该建立在最小的接口上

五、迪米特法则(最少知道原则)

迪米特法则,也称为最少知识原则,也就是一个对象应该对其他对象有最少的了解。需要只和朋友交流;朋友间也是有距离的;自己的就是自己的;类间解耦,弱耦合。

这个面向对象开发中封装思想的体现,对应到自然界,就是一个个独立的个体,哪怕和其他人有了交集,但是这种交集也是很微弱的,不至于一个人受伤,多个人遭罪。在开发中也能降低耦合,这有点和单一职责有点像,但不完全一致。单一职责原则是说一个类负责一个职责,但是迪米特法则是说对象之间的关系。

六、里氏替换原则

里氏代换原则由2008年图灵奖得主、美国第一位计算机科学女博士Barbara Liskov教授和卡内基·梅隆大学Jeannette Wing教授于1994年提出。其严格表述如下:如果对每一个类型为S的对象o1,都有类型为T的对象o2,使得以T定义的所有程序P在所有的对象o1代换o2时,程序P的行为没有变化,那么类型S是类型T的子类型。这个定义比较拗口且难以理解,因此我们一般使用它的另一个通俗版定义:

里氏代换原则(Liskov Substitution Principle, LSP):所有引用基类(父类)的地方必须能透明地使用其子类的对象。

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

      例如有两个类,一个类为BaseClass,另一个是SubClass类,并且SubClass类是BaseClass类的子类,那么一个方法如果可以接受一个BaseClass类型的基类对象base的话,如:method1(base),那么它必然可以接受一个BaseClass类型的子类对象sub,method1(sub)能够正常运行。反过来的代换不成立,如一个方法method2接受BaseClass类型的子类对象sub为参数:method2(sub),那么一般而言不可以有method2(base),除非是重载方法。

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

介绍的比较好的文章:

https://www.cnblogs.com/vaiyanzi/p/6899402.html

https://www.cnblogs.com/chenxkang/p/6657384.html

原则:子类可以扩展父类的功能,但不能改变父类原有的功能。
     父类能出现的地方都可以用子类来代替,而且换成子类也不会出现任何错误或异常,而使用者也无需知道是父类还是子类,
     但反过来则不成立。总之,就是抽象。

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

优点:
     1. 提高代码的重用性,子类拥有父类的方法和属性;
     2. 提高代码的可扩展性,子类可形似于父类,但异于父类,保留自我的特性;

缺点:
     1. 继承是侵入性的,只要继承就必须拥有父类的所有方法和属性,在一定程度上约束了子类,降低了代码的灵活性;
     2. 增加了耦合,当父类的常量、变量或者方法被修改了,需要考虑子类的修改,所以一旦父类有了变动,很可能会造成
        非常糟糕的结果,要重构大量的代码。

里氏替换原则是开闭原则的具体实现手段之一。这也就是我们应该更多的依赖抽象,尽量少的依赖实现细节, 其实就是我们之前讲的依赖倒置原则(DIP)。

七、合成复用原则

经常又叫做合成复用原则。合成/聚合复用原则就是在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分;新的对象通过向这些对象的委派达到复用已有功能的目的。它的设计原则是:要尽量使用合成/聚合,尽量不要使用继承。

继承的问题:

通过继承来进行复用的主要问题在于继承复用会破坏系统的封装性,因为继承会将基类的实现细节暴露给子类,由于基类的内部细节通常对子类来说是可见的,所以这种复用又称“白箱”复用,如果基类发生改变,那么子类的实现也不得不发生改变;从基类继承而来的实现是静态的,不可能在运行时发生改变,没有足够的灵活性;而且继承只能在有限的环境中使用(如类没有声明为不能被继承)。
 

在面向对象设计中,我们可以通过两种方法在不同的环境中复用已有的设计和实现,即通过组合/聚合关系或通过继承,但首先应该考虑使用组合/聚合,组合/聚合可以使系统更加灵活,降低类与类之间的耦合度,一个类的变化对其他类造成的影响相对较少;其次才考虑继承,在使用继承时,需要严格遵循里氏代换原则,有效使用继承会有助于对问题的理解,降低复杂度,而滥用继承反而会增加系统构建和维护的难度以及系统的复杂度,因此需要慎重使用继承复用。

有人会讲合成复用不会增加类之间的耦合度而且违背了单一职责原则吗?但实际上我们开发不可能做到完全的解耦和单一职责。

在这种情况,如果想通过继承的方式引入其他类可能耦合度会更高而灵活性会降低。
 

 

参考文章

https://www.cnblogs.com/Johar/p/9028240.html 软件架构的六大设计原则

https://blog.csdn.net/liuzihaoboy/article/details/80830476 面向对象设计原则(六)合成复用原则

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值