SOLID原则

最近在准备面试,看到了这个题目,参考了诸多的博客总结如下。

 

S:single Responsibility Principle 单一职责原则

一个类只负责一个职责,当且仅当这个职责需求发生变化时,才需要修改这个类。如果有超出这个职责之外的事情,应当重新将职责分离出去。或通过has-a的方式使用新的职责。
单一职责要求一个类只能有一个发生变化的理由,不允许持有超过自身的其他功能,因为如果一个类承担了太多的事情,就会导致抽象体系崩溃,无法知道这个类究竟是什么。更重要的是,一旦有一个职责需要修改,就对这个类的其他功能产生影响,这给软件系统带来很大的不稳定性。
在工厂模式中,当工厂试图承担超过自身能力的类的实例化功能时,就必须要修改代码,增加判断或者是其他操作,一旦有需求需要增加,就需要不断增加判断,这一点也违背了开闭原则,因此就需要抽象工厂来对工厂进行抽象。从而分离出具体工厂的职责。降低耦合度。
 
O open close Principle 开闭原则
一个类或者软件系统,应当是对扩展开放,对修改关闭的。
对于一个类来说,如果需要修改或者增加新功能,那必须是扩展原有代码,而不是修改原有代码来实现。因为当项目足够大,且多人合作的时候,我们很难确定某一个类被多少其他类使用过,也不知道这个类中的方法究竟承担了怎样的职责,如果贸然修改它,将会对软件系统造成不可预知的影响。而且,当有需求频繁变更的时候,我们也不可能每一次都修改方法,并且评估这个修改对软件系统的影响。如果有修改回退,我们还要找到之前的代码再改回来。(当然对于这种修改来说,抽象是一种非常有效的办法。就是增加一个抽象层,抽象层定义行为,而子类去实现具体的逻辑,这个抽象对客户端是透明的,客户端不需要知道怎么实现,是需要使用即可)
对一个软件系统来说,也是对open close的。比如我们提供了一个工具类api,api要保证在每一个客户机上的功能都是不能被修改的,当需要修改或者增加功能时,就需要api开放一些接口,用于客户机实现具体的定制需求,而这些接口就是一个行为上固定的抽象层。
 
L 里氏替换原则
  在软件领域内,is-a的关系并不一定是现实中的关系,软件内只有当所有子类出现的地方都可以代替为父类而对软件没有影响时,才能说子类与父类有is-a的关系。
  这个典型的反例就是:生物学上企鹅是鸟,但是软件系统内,如果要对鸟抽象一个会飞的功能,那么企鹅就不是鸟,因为企鹅不会飞。
  java中经常会有父类型的引用指向子类型的对象,这样我们就可以用统一的父类型,作为抽象层对外的表现,而使用具体的子类实现具体的功能。当需要增加功能时,也只需要增加一个子类,客户端调用的代码甚至都不需要改变。很大程度上实现了代码的复用。对于接口来说,它只需要约定行为,这样就实现了抽象和解耦。
  但是这种特性是需要父类和子类拥有is-a的关系,而不仅仅是一个extends关键字。假如在子类型中用了父类型没有的方法,在做里氏替换时就会出错,如此类继承体系就会出问题,既然子类not is a 父类了,
 
上面说法有问题,之前没有理解清楚。
严格定义:
1.如果对每一个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有的对象o1都换成o2时,程序P的行为没有变化,那么类型T2是类型T1的子类型。
2.如果对每一个类型为 T1的对象 o1,都有类型为 T2 的对象o2,使得以 T1定义的所有程序 P 在所有的对象 o1 都代换成 o2 时,程序 P 的行为没有发生变化,那么类型 T2 是类型 T1 的子类型。
通俗定义:所有引用基类的地方必须能透明地使用其子类的对象。
理解:java中,经常有通过父类型的引用指向子类型的对象,在做抽象层的时候往往也会只操作抽象类或者接口,但是在实际运行的时候,操作的是真正的子类型。也就是说,运行时这个子类型写成父类型是没有问题的。可以在编译期只用父类型。这样我们在使用中就不需要考虑抽象类和接口究竟是怎么实现的,实现了多态。
但是想用这种特性,就必须要符合子类is a 父类,里氏替换原则。表现上就是,子类型不要修改重写父类型已经实现的方法,因为如果这样做,父类型和子类型的行为就会出现偏差,从而不能构建is a关系。本来继承是紧耦合的方式,想要使用继承也是处于代码复用了抽象的角度考虑的,那么父类型给定义的办法,一定是一种对子类型都有效的约束,子类型随意篡改父类型的内容是不允许的。也就是说父类型有的子类型也一定要有,且行为一样,子类型可以有自己的办法。比如子类型企鹅如果希望自己is a 鸟的话,那么必须要实现鸟的fly方法,但是企鹅实际上不会飞,所以,软件层面上考虑,企鹅不能是鸟。
博客说,里氏替换是实现开闭的重要原则,可能指的是,当又需要修改或者增加功能时,可以通过增加一个子类来实现,而软件表现上仍然可以用父类型的对象操作。这就是多态,而想要实现这种便利,就必须要产生is a的关系。
参考资料:
 
I interface Segregation Principle 接口隔离原则
这个和单一职责有些类似,意思是一个接口表示一个抽象,它的实现类就是子类,子类要拥有单一职责,因此接口也要符合单一职责,当然这里的单一职责并不是说只有一个方法。
一个接口不应该有太多的内容,如果子类没有用到其中的某些方法,但还是需要实例化它们,就会显得多余而容易出错,如果接口方法要发生某种变更,也会导致无关的子类也被影响。既然要重用,那么保证接口粒度足够小,才能被很好的重用。
 
D dependence Inversion Principle 依赖倒置原则
定义:高层模块不应该依赖于底层模块,二者都应该依赖于抽象,抽象不应该依赖于细节,细节应该依赖于抽象。
其实就是软件分层和抽象的内容,高层模块一般指涉及到具体业务,容易变更的模块,底层模块是指更加底层一点,变化较少,一变化会对许多高层模块产生影响的模块。也就是说
具体的业务要依赖于提供底层服务的模块,而不能自己玩自己的。无论是高层还是底层,都不应该直接用具体的实现类操作,而需要依赖于抽象,抽象在高层底层和更底层之间架设了一个中间层,架构师抽象这个中间层,提供一个相对稳定的功能,和可以扩展的接口。这样就保证了每一层之间尽量少地耦合。
 
 
开闭原则是目标,里氏代换原则是基础,依赖倒转原则是手段
 
 
 
posted @ 2017-02-17 17:26  open_sesame  Views(209)  Comments(0Edit  收藏  举报