面向对象六大基本原则的理解

在学习设计模式的时候,老是被推荐先学习一下面向对象的六大原则,学习后果真受益不浅。如下彻底是我对六大基本原则的理解,和官网解释可能有出路,并且我更可能是站在设计模式的角度,而不是面向对象的角度理解,若是有什么错误,敬亲谅解。css

1.开闭原则

不少教程都把开闭原则做为这六大原则中最基本的原则,也就是说他是各个原则的核心。开闭原则指的是,一个软件实体如类、模块和函数应该对扩展开放,对修改关闭html

至于这个具体怎么理解,我也看了不少教程,有些教程说当咱们遇到新的需求,就须要咱们对咱们模块继承的形式进行扩展,而不是修改代码。这样的解释貌似有道理,可是若是真的这样作了,程序结构只会更加复杂,业务逻辑只会更不清晰,彻底是一种做死的作法。当业务发生改变的时候,确定是要修改代码的,不须要的东西留着只会让程序臃肿,让维护者搞不清什么是有用的代码,什么是已通过时的代码。我不太相信开闭原则的真谛是让咱们走向这样一个死胡同。java

对于开闭原则,个人理解是,咱们在设计软件的时候,首先要搞清楚程序当中什么是将来可能变化的,什么是将来不会变化的。对于可能变化的东西,咱们要提早给与能够对应的扩展接口。固然实际开发中,即使是咱们认为这些不会变化的地方,将来仍是可能变化的,这种变化就只能改代码了,可是这种修改仅仅只是改变个别细节,总体架构每每不会变化。而对于可能变化的地方,咱们要给出能够足够扩展的空间,让其可以自由扩展,基本发生了重大的需求变动,总体架构也不会受影响。c++

例如:工厂模式中,咱们将建立对象的过程封装了起来,这样建立对象对的过程当中,建立的代码就和调用的代码尽量地解除了耦合。建立过程多是变化的,而调用过程每每是不变的。咱们建立一个对象以后,须要为其初始化,设定一些配置,这个过程须要我们给出能够扩展的余地,并且要求扩展的时候不能影响调用部分,因此须要使用工厂模式,将可变的建立过程封装起来,供不变的调用模块。程序员

这样说来,开闭原则的核心是解耦了?没错,我认为开闭原则讲的就是解构,可是他要求咱们在设计的时候,重点要预判出什么地方是会发生变化的,并要为变化的地方留出余地。他强调的是对于可变部分进行解耦,使用扩展的方式而不是修改的方式应对变化,这样能够保证程序总体不会发生大的变化。算法

开闭原则对于开发框架、能够被复用的组件(如jar、dll、js插件等待)尤其重要,由于这些组件必须留出足够的空间去让调用者去扩展本身的业务。因此咱们在开发这种组件的时候api才是最难设计的,由于咱们设计的api必须能知足调用者对他的所有扩展,这样才能实现调用者在不修改组件代码的状况下实现本身的需求。设计模式

2.里氏替换原则

这个原则挺简单,讲的就使用接口的时候,咱们必须确保子类可以替换父类所出现的任何地方。纯粹就字面的意思来说,就是父类接口必须确保全部子类均可以实现需求,而不是某一个子类。api

例如,java中HashMap和LinkedHashMap都是Map的子类。可是HashMap的顺序是随机的,而LinkedHashMap是固定的。当咱们须要使用一个map,此map不须要要求key顺序可控的话,咱们能够声明:架构

Map createMap(){
  return new HashMap();
}

但咱们要求顺序可控是,若是是这样:框架

Map createMap(){
  return new LinkedHashMap();
}

上述代码就不太好了,由于HashMap也是Map的一个子类,可是他不能知足咱们的需求,因此此处必须声明返回值类型为LinkedHashMap。例如咱们在设计接口的方法的时候,若是调用者须要的是一个LinkedHashMap,咱们就不能以HashMap类型作接口的声明。

固然里氏替换原则也能够从设计角度出发,他强调咱们设计的时候须要确保父类定义了的时候,就应该覆盖到这个接口抽象出的业务的全部方法,而不须要他的子类再添加额外的扩展,同时各个子类也都该实现父类中的全部接口,只有这样才能保证咱们设计出的东西是可扩展的。

开闭原则讲究扩展,里氏替换原则能够确保经过继承这种方式的扩展是可行的,不然就没法使用继承去扩展程序了。

例如:咱们使用模板模式,作了一个类做为父类算法模板,一个子类继承了这个模板,而且顺利地完成了运行;可是另外一个子类也继承了模板类,却没法运行,最终发现程序没法确保全部继承模板的子类能够替换父类,这就是个失败的模板模式。

从面相对象的角度来讲,里氏替换原则是子类能够替代父类,可是从面相组件的角度看,实际上是确保组件的api是完整的、不变的,子类和外界也是彻底解耦的,只有这样咱们开发出的扩展才能在不破坏原有框架的基础上运行。

3.依赖倒置原则

这个原则也是讲究解耦,他指的是让高层模块不要依赖低层模块。这个是个纯粹的面向接口,面向模块开发思路了,由于面向对象而言,各个对象本身的东西和外界是解耦的,由于封装特性把它们本身的属性都封装起来了,因此是不会和其余对象有耦合关系的(如c++的友元除外);可是各个对象仍然是相互耦合的,最强的耦合就属于继承耦合了,对象组合起码仍是轻耦合,继承是个高耦合呀。依赖倒置就是让各个对象耦合度下降,高层模块不能继承底层模块,须要底层的东西也是外界注入而不是本身建立获得;同时调用的时候也是使用接口调用,而不是依赖具体实现,而且由于是接口调用,具体实现模块能够被任意替换了。

这样作就能够下降各个模块的耦合,也能够确保里氏替换原则的实现。实现里氏替换原则有什么用?固然是能够方便扩展了。

456.职责单一原则、接口隔离原则、最小知道原则(迪米特原则)

这几个原则很像,就放到一块儿说吧。这几个类都是在强调解耦(固然开闭原则、里氏替换原则、依赖倒置原则也是)。这几个原则基本都是强调解耦,只是站的角度不同。职责单一原则指的是一个模块(接口)的功能尽可能是单一的,这样不一样功能的接口就不会耦合在一块儿了,同时维护起来也方便。接口隔离原则强调每一个类继承的接口必定要保存最少,不能继承无用的接口,保证接口隔离原则的前提是先要保证职责单一原则。最后一个最小知道原则指的是模块是全部的依赖都要保存最少,这一点和接口隔离原则有点重复,或者能够说接最小知道原则包含接口隔离原则,同时最小知道原则还有对外界影响最小的意味。这几个原则说的都是类和接口设计要尽可能下降耦合的问题。

其实不止面向对象开发,其余的不少都须要实现这六大基本原则。例如写css,这个样式表的开发和面向对象开发原本就是风马牛不相及,可是一样须要可以将需求抽象出来的思路是一致的,因此一样须要可以支持面向对象的6大原则。例:

咱们须要实现这样的需求,要能写一个table的样式,并且须要保证这个table样式可以复用。要保证复用,就须要保证这个table样式职责单一,不可以涉及到table之外的样式,这样维护起来简单,同时用户只须要table,咱们就不能输出用户不须要的样式,不然很容易和其余样式冲突,这就是职责单一原则的体现。

同时在实现这个table样式的时候,要将各个子元素的样式依赖到本身下面,例如写的tr样式要确保仅对应用了这个样式的table里的tr有效,对于没有使用这个样式的table的tr无影响,不能由于改变了咱们的tr样式而影响了其余table的tr样式,这体现了最小知道原则。

同时这个table所须要的其余依赖也是要越少越好,例如若是这个table依赖于bootstarp,就无法作了。由于若是咱们依赖bootstarp,其实也仅仅是依赖不多的一部分功能,绝大部分功能是咱们不依赖的,不过由于bootstarp没有单个table样式的css(类比面向对象的接口),我会引入不少没必要要的功能,这样会对其余部分形成污染。因此咱们须要引入职责单一的类库,不能引入不须要的功能,这也与接口隔离原则所阐述的吻合。

总结:和设计模式的关系

简单介绍了一下这几大基本原则,再说说我对于他们和设计模式的理解。咱们在学习设计模式的时候,有没有想过为何要学习这个,学习了设计模式有什么好处?我在工做中常常发现不少经验欠缺的程序员,为了学习设计模式而学习设计模式,为了使用设计模式而使用设计模式,而有经验的程序员则不会这样。其实程序员开发的每个程序都是从需求出发的,只有搞清楚咱们一个项目的根本需求才有使用设计模式的意义。

六大原则中,最重要的确定就是开闭原则了,我对开闭原则的理解是,你无须在每一个细节上都要作到对扩展开放,对修改关闭,而是应该为了面对扩展而扩展。当有这种需求的时候,例如一个项目业务、算法可能会有重大的变化,或者自己开发的就是一个组件,这是才须要扩展,而扩展的时候才应该使用设计模式来实现这种需求,也就是说使用设计模式和咱们平时开发同样,都是知足需求。

使用了设计模式不必定能真正地提升代码的结构和可维护性,因此有经验的程序员会按需求而使用设计模式。六大基本原则能够更好地帮咱们如何分析,这样才能肯定是否使用设计模式。事实上设计模式的使用上我建议无招胜有招,知足项目的额外需求(例如利于扩展、可复用、高可维护性)都是好招,无需管他是什么设计模式。

转载于:https://www.cnblogs.com/laden666666/p/5173116.html