设计模型的基本原则

(1)开闭原则
说起开闭原则,大家基本都听说过,开指的是对扩展是开放的,闭指的是对修改是关闭的。

言下之意就是对于某个代码块或者功能方法,在原有的基础上进行扩展(个人理解可以为继承并在子类中新增方法,如果有不对,还望大佬们评论区指点一下)是推荐的,而直接对原有代码段或方法进行修改是不推荐的,面向对象强调高内聚低耦合,

至于内聚与耦合如何理解,参照前辈文章案例通俗易懂。

因此为了实现高内聚低耦合,我们就需要做到开闭原则,降低系统的耦合度,做出来的软件设计才算好。

(2)单一职责原则

简单的说,一个类只做一件事或者说只有一个引起它变化的原因。

与内聚耦合相联系便可以理解,一个类如果同时负责两件或多件事务,那么他的耦合度会变高,当出现一个事务使这个类发生变化时,这个类可能会在变化的同时,导致他的其他事务无法正常运行,也就是所谓的bug就会出现,当进行模块化程序设计时,都要遵守但一职责原则。

(3)里氏替换原则
俗称“老鼠的儿子会打洞”原则。

之所以这么叫它,是因为这里涉及到一个继承问题。

当A类中有a方法,B类继承了A类并有c方法,c方法结合了a方法以及在B类中写入的新的b方法。这时,在完成b方法时,B类中的a方法可能会出现bug。

分析原因:子类型没有替换掉父类型。里氏替换原则表示子类型必须能够替换掉父类型。

意思就是父类型中的方法被继承到子类中的时候我们要注意的点是:

子类可以实现父类的抽象方法,子类可以增加自己的新方法,子类不可覆盖父类的非抽象方法,在重载父类方法时,子类方法的形参必须比父类的方法的输入参数更宽松。当子类在实现父类的抽象方法时,子类的方法的返回值必须比父类的更严格。

(4)迪米特法则

又称最少知道原则。

意思是,一个对象应该对其他对象保持最少的了解。

一个类应该隐藏自己,只对外开放public方法,减少与其他类的直接接触,这样可以降低类与类之间的耦合度,还是高内聚低耦合的原则。

这里引入一个新的名词叫做直接朋友,有关系的类即有耦合的类多称为朋友,当一个类a中的方法出现一个类b作为成员变量、方法形参、方法返回值的时候,我们称类b是类a的直接朋友,也就是直接接触的两个类,但要注意的是局部变量类不作为直接朋友。

为了减少类与类之间的直接接触,降低耦合度,迪米特法则采用了引入中介或者传递类作为桥梁来进行类与类之间的通讯。

但要注意的是,引入了中介或传递类过多会使得程序变得复杂,可能会适得其反,因此一定要仔细分析,反复衡量使用的利与弊,既要做到结构清晰,又要高内聚低耦合。

(5)依赖倒置原则

中心思想是面向接口编程。

高层模块类a依赖低层模块类b时,如果类a突然需要依赖低层模块类c,则需要对类a进行代码修改,根据开闭原则,大家都知道对修改应该是关闭的,否则对程序可能会造成问题,也就是bug。因此要说到依赖倒置原则。

高层模块不应该依赖低层模块,而是应该去实现一个接口,当类a依赖类b中的接口,而突然需要对类c依赖时,可以依赖类c的接口,这样可以降低代码修改的几率。在编程中需要注意几点:

1.低层模块尽量使用接口或抽象类,或者两者都具备;
2.变量声明类型最好是抽象类或者接口;
3.继承时遵循里氏替换原则;

理解依赖倒置原则,需要理解面向接口编程,理解了,就都理解了,哈哈。

面向接口编程的一个例子,通俗易懂,可以看看:

(6)接口隔离原则

通俗的说,类a通过接口i依赖类b,但是类a只需要类b中的某一个或多个,但不是全部方法,但当类a通过接口i实现类b的方法时,却需要实现一些自己并不需要的方法,则称接口i对类a来说,不是最小接口。

这时候需要将接口i拆分成几个接口(实际编程时,某个接口可能会有很多类在使用,因此这里不说把类a不需要的方法作为另外接口进行拆分,而是说拆分成多个接口,具体问题具体分析),各个类分别于需要的接口建立关系,这就体现了接口隔离原则。

但是我们要注意的是,并不是说把一个大的接口拆分成多个小的接口就是好事,和迪米特法则一样,接口隔离原则是有利与弊的,如果接口太多,会使程序设计复杂化,所以一定要适度,因此在设计接口时要仔细分析思考。

(7)合成\聚合原则

又名合成\复用原则。

这个原则意思是:能合成\聚合的地方,就不要用类继承;

合成\聚合的意思是:在一个新的对象中使用已有的对象,使之成为新的对象的一部分;新的对象通过这些对象的委派达到复用已有功能的目的,合成是值得聚合,聚合是引用的聚合。

放串代码瞅瞅一目了然:

class Student {
}

class Classes{
          privateStudent student;
          publicClasses(Student student){
                  		 this.student=student;
          }
}

类Classes合成了类Student,那么接下来类Classes就可以复用类Student的功能了。

合成\聚合理解入口:

为什么能合成\聚合的地方,就不用了类继承?

原因:

  1. 对象的继承关系在编译时就定义好了,所以无法在运行时改变从父类继承的子类的实现;
  2. 子类的实现和它的父类有非常紧密的依赖关系,以至于父类实现中的任何变化必然会导致子类发生变化;
  3. 当你复用子类的时候,如果继承下来的实现不适合解决新的问题,则父类必须重写或者被其它更适合的类所替换,这种依赖关系限制了灵活性,并最终限制了复用性。

遵循设计模型原则,妈妈再也不用担心我的代码复用性差,耦合度高了,还能提高系统的扩展性!!