面向对象软件系统要求的是 支持可维护性和可复用性。在这个理念下产生出7种常见的面向对象设计原则
7大常用的面向对象设计原则:单一职责原则,开闭原则,里氏替换原则,依赖倒转原则,接口隔离原则,合成复用原则,迪米特法则。
那么我们这里面一个一个的说一下。
开闭原则:
概念 软件实体应对扩展开放,而对修改关闭。
首先软件实体可以指一个软件模块,一个由多个类组成的局部结构或一个独立的类。
开闭原则是一个总的原则,一切其他的原则都基于此原则来的。我们怎么实现开闭原则呢?如果要修改一个需求的话,肯定会对代码进行修改,我们怎么只扩展不修改呢?设计原则告诉我们一个比较普通的办法就是 程序里面几乎所有的调用方法都需要用抽象的接口对象,这样我需要改哪一块只需要把这个接口的实现类替换成我新的实现类,就像一个机器换了个零件一样,这样我就实现了软件的扩展,而减少了修改。 基于这个思想,所以我们要
单一原则啊---这样才能保证,我的类的功能比较少;
依赖倒转原则---这样才能我的实现类替换时候,不应该影响上层的调用啊;
接口隔离原则---我的接口这个抽象出来的方法简单,利于我组合使用;
合成复用原则---我新的实现类,可以更好的扩展以前的实现类,不至于两个实现类牵扯太多;
最少知识原则---这样我的实现类的影响才能减少。 这样,我们在达到程序实现需求的之后,对单个的具体的细节实现类进行替换或者扩展之后 实现我们的维护和复用。实现产品提出的新需求。
单一职责原则:
概念 单一职责原则:一个类只负责一个功能领域中的相应的职责,或者可以定义为:就一个类而言,应该只有一个引起它变化的原因。
一个类的职责不是太多,我们才能保证这个类在需要进行修改的时候,才能好扩展,而这个扩展不影响其他的类。颗粒越小,需要修改时候,我们需要注意的东西越少。而且和人的思维结构也有关系,单一职责的类我们只需要完成这一个职责就ok了,不要管理其他的,并且在每个类上面加注释,这样我们需求变动时候,进行改进就很容易了。因为人脑也是这种思维,复杂的逻辑往往把脑袋挤炸了,我们通过单一原则,每个类就简单的一个功能,我们实现了多个这样的单一职责的类之后,我们的程序就写完了,不需要关注那么复杂的逻辑。
里氏替换原则
概念: 在软件中将一个基类对象转换成它的子类对象,程序将不会产生任何错误和异常,反过来则不成立,如果一个软件实体使用的是一个子类对象的话,那么它不一定能够使用基类对象。
在程序中尽量的使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类对象。这里面其实还有面向对象语言的一个牛逼的概念:后期绑定
前期绑定: 非面向对象语言 在编译的时候确定对一个具体函数的调用,运行时候这个调用直接拿到相应的被执行代码的绝对地址。也就是说 编译时候就确定了具体的调用的方法。
后期绑定:面向对象语言 在编译时候编译器确保被调用的方法存在,并且对调用参数和返回值执行类型检查,但是并不知道将执行的确切代码,直到运行时候,对象发送调用的消息,被调用的代码才能确定。、
基于java代码面向对象后期绑定,我们才能利用里氏替换原则。基类可以使用子类替换,但是子类不一定能够使用基类来替换。因为后期绑定,直到运行到的时候才能知道哪个对象的方法被调用。说白了 里氏替换原则,人家都给弄好了。
依赖倒转原则
概念: 依赖倒转原则:抽象不应该依赖细节,细节应当依赖抽象。换而言之,要针对接口编程,而不是针对实现编程。
依赖倒转原则是我认为最牛逼的原则也是最喜欢的。 我们抽象都抽象出接口来,那么我们哪个地方有问题直接用另一个实现类去拓展或者替换就可以了。整个程序的抽象逻辑是不变的。任何框架的底层抽象层的逻辑关系都是固定的,都是不可变动的,只有下层的实现层才会变动。这是整个框架的立身之根本,是其能做什么的根本。就像美国宪法大纲原则一样,美国成立到现在就小小的修改过几次,是 根本。这是 架构师必备设计原则,一定要明明白白的。一个程序的总的调用流程定了,很多人才能介入去搞一些细节的功能具体实现。
这是宏观上面去理解,微观方面去理解的话就是,依赖倒转可以帮助我们减少修改实现类,说白了哪个类不好用 替换了或者拓展一下就好了,不用到深入到代码细节中去找 然后替换所有的类。
接口分离原则
概念:使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口。
这个和单一原则有点像,不过单一原则是对类所讲的,那么接口分离原则是对对象讲的。每个根接口只有一到两个方法的话,我们实现起来很方便。而且你实现哪个接口,那么在抽象层面调用的时候,你这个实现类就可以注入到那个地方去,就可以当作那个接口来用。
合成复用原则
概念 合成复用原则:尽量使用对象组合,而不是继承来达到复用的目的。
合成复用原则就是在一个新的对象通过关联关系来使用一些已用的对象,实质成为新对象的一部分;新对象通过委派调用已有对象的方法达到复用功能的目的。简而言之:复用时要尽量的使用组合/聚合的关系,少使用继承。
使用合成原则的话,想调用已有对象哪个方法就调用,想拓展就拓展,而且和已有对象没有太大关系,即使已有对象的类有修改,我这个新的类的对象也不需要修改,减少耦合性。已有对象的类的逻辑是他的逻辑 我新的类的逻辑是我新的对象类的逻辑,我不需要知道原有的类咋改 咋变 咋拓展,我只需要知道,自己类调用了他就行了。我不需要知道原有类的细节,只是调用它的方法。 如果我继承了,那么原有类的细节可能会影响到我新的类,我新的类也拿过来一些对于我无用的逻辑,这样对于新类来说没啥大用,增加了耦合性。而且吧,知道的太多很不好,其他人进行扩展和修改时候,可能调用一些根本不需要调用的方法,可能导致一些必要的问题出现。
迪米特原则
概念:又称最少知识原则 一个软件实体应当尽可能少地与其他实体发生相互作用。
不要和陌生人说话,只与你的直接朋友通信。对于一个对象,其朋友包括:
(1)当前对象本身(this)
(2)以参数形式传入到当前对象方法中的对象
(3)当前对象的成员对象
(4)如果当前对象的成员对象是一个集合,那么集合中的元素也是朋友
(5)当前对象所创建的对象
一个对象只能与直接朋友发生交互,不要与“陌生人”发生直接交互,这样做可以降低系统的耦合度,一个对象的改变不会给太多其他对象带来影响。
最少知识原则要求我们在设计系统时,应该尽量减少对象之间的交互,如果两个对象之间不必彼此直接通信,那么这两个对象就不应当发生任何直接的相互作用,如果其中的一个对象需要调用另一个对象的某一个方法的话,可以通过第三者转发这个调用。简而言之,就是通过引入一个合理的第三者来降低现有对象之间的耦合度。 这个办法就是 设计模式中的中介者模式。
那么为什么要这样呢? 因为两个不相关的类如果你给直接通信的话,逻辑上面是说不通的,系统维护的时候,很难去维护,你这个功能的对象怎么能够拥有这个其他功能的对象,显然不合理的,很破坏逻辑。加入某一特定功能的对象A 含有另一个功能的对象B, 那么在A的类中 真的可以随便的调用B的公开的方法, 这样的话B对象出问题了,压根没出找问题原因,也没办法去维护,因为两者不是一个功能下的,我们不应该给A对象的类那么大的权力,没有直接联系的抽象调用 ,最好相互不能调用,这样能保护我们功能的内在逻辑。我们通过用一个中介者模式来调用的话,那么A对象就可以在中介类中进行调用B对象,那么如果之后出了问题 直接找到中介的类就可以了。
在类的划分上,应当尽量创建松耦合的类,类之间的耦合度越低,就越有利于服用,一个处于松耦合中的类一旦被修改,不会对关联的类造成太大的波及;在类的结构设计上,每一个类都应当尽量降低其成员变量和成员函数的访问权限,这样才能都本类进行保护,也是对调用者的负责。在类的设计上,只要有可能,一个类型应当设计成不变类;在对其他类的引用上,一个对象对其他对象的引用应当降到最低。
ok,就这样~