单一职责原则
不要存在多于一个导致类变更的原因,如果一个类承担的责任过多,就等于把这些责任的风险也承担了过来。
一开始只是人类,学习是本能
public class Person{
public void study(){
}
}
接下来分职业
public class Person{
public void study(String type){
if("doctor".equals(type)){
//学医
}else if("teacher".equals(type)){
//教育
}
}
}
不只是学习,吃饭也很重要
public class Person{
public void study(String type){
if("doctor".equals(type)){
//学医
}else if("teacher".equals(type)){
//教育
}
}
public void eat(String type){
if("doctor".equals(type)){
//点外卖
}else if("teacher".equals(type)){
//食堂吃
}
}
}
如果职业多了厨师,助理,行为多了走,玩,休息等呢?这样的话,Person类就会变得很复杂。
为什么会出现这种情况呢?因为类似于吃饭,学习,玩,休息等行为都属于具体个人的行为,应该细分到具体的人身上。而Person
这个类的最初的目的是为了定义人类的基本行为。
public class Person{
public void study(){
//学习 不关注具体学习行为
}
public void walk(){
}
}
public class Teacher extend Person{
public void study(){
//教育
}
}
这样一划分的话,Person
类 就只需要关注有什么行为,具体什么行为划分到具体的人上。
开放封闭原则
对于拓展是开放的, 对于更改是封闭的。
简单来说就是原来的一些定义类型的代码(诸如接口,实体类,抽象类等)不要改了,要想加功能,就往上加,改代码是不行的。因为你根本不知道你的改动会造成什么不可预期的变化。
有这么一个需求设置时钟的时间
public class Clock{
public void setClock(int hour){
}
}
这时需求改动,需要设置分针,这时不应该修改代码中的 setClock(int hour)
函数,因为你并不知道有多少的地方调用它,而是增加一个函数setClock(int hour,int minute)
public class Clock{
public void setClock(int hour){
}
public void setClock(int hour,int minute){
}
}
里氏替换原则
定义一:子类对象能够替换父类对象,而程序逻辑不变。
定义二:子类可以扩展父类的功能,但不能改变基类原有的功能。
疑问:里氏替换原则中提到不能修改基类的方法,而多态的条件却是要求子类重写基类的方法,多态是不是违背了里氏替换原则。
不管是里氏替换原则还是多态都依赖继承。这时你得分清楚,继承的目的是什么?
继承是为了代码复用,共享方法,这可以说是继承的最大优势。比如
public class Person{
private void walk(){
System.out.println("走");
}
}
public class Teacher extends Person{
}
public class Doctor extends Person{
}
以上述的代码为例.
问:人你会干点什么
答:走
问:老师你会干点什么,
答:走
问:医生你会干点什么,
答:走
这样的情景下,老师和医生这两个类不需要额外的工作就能回答会干点什么这个提问。而且把人替换成任何一个子类 老师或者医生,结果都不会变。这便符合了里氏替换原则。
继承是为了多态
public class Person{
public void walk(){
System.out.println("走");
}
}
public class Teacher extends Person{
public void walk(){
System.out.println("慢慢走");
}
}
public class Doctor extends Person{
public void walk(){
System.out.println("走的很快");
}
}
问:人你会干点什么
答:走
问:老师你会干点什么,
答:慢慢走
问:医生你会干点什么,
答:走的很快
这种场景下,用子类老师或者医生来替换基类人就会发生歧义,你刚才说的是走?现在怎么就变慢慢走了?这样情况就不符合里氏替换原则了。
如果是这种情况,正确的写法应该是
public abstract class Person{
public abstract void walk();
}
public class Teacher extends Person{
public void walk(){
System.out.println("慢慢走");
}
}
public class Doctor extends Person{
public void walk(){
System.out.println("走的很快");
}
}
问:人你会干点什么?什么,你不在? (抽象类,无法生成对象)
问:老师你会干点什么,
答:慢慢走
问:医生你会干点什么,
答:走的很快
因为基类是抽象类,无法生成对象,所以并不会违背里氏替换原则,从某种程度上来说,不违背原则,那就是遵循了里氏替换原则。
定义一:子类对象能够替换父类对象,而程序逻辑不变。
回到开头的问题,多态是不是违背了里氏替换原则?回答,不是。
总的来说,想省事就不要想着修改基类。想个性那就定义抽象基类。
依赖倒置原则
高层模块不应该依赖具体底层模块,两个都应该依赖接口。简单的说就是面向接口编程,而不是面向具体实现。
任何变量都不应该持有一个指向具体类的指针或引用。
实际上就是面向接口编程,主要思想跟上述里氏替换原则有点类似。只不过里氏替换原则重点放在继承上,而依赖倒置原则则是放在接口上,好处呢?java中单继承,多实现。
接口分离原则
客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。
接口应该是内聚的,应该避免“胖”接口。一个类对另外一个类的依赖应该建立在最小的接口上,不要强迫依赖不用的方法,这是一种接口污染。
public interface Teacher {
public void teachMath();
public void teachChinese();
}
public class MathTeacher implements Teacher{
public void teachMath() {
//教数学
}
public void teachChinese() {
}
}
由于数学老师并不会教语文,所以导致teachChinese()
这个方法是多余的,这也就造成了接口污染的情况。合适的方式就是接口隔离。
public interface Teacher {
public void teachMath();
}
public interface Teacher2{
public void teachChinese();
}
public class MathTeacher implements Teacher{
public void teachMath() {
//教数学
}
}
假设这个数学老师比较厉害,还会教英语,这时再加一个接口,这个厉害的数学老师实现两个接口,普通的数学老师实现一个接口,比如
public interface Teacher {
public void teachMath();
}
public interface Teacher2{
public void teachEnglish();
}
public class GoodMathTeacher implements Teacher,Teacher2{
public void teachMath() {
//教数学
}
public void teachEnglish() {
//厉害的数学老师教英语
}
}
public class MathTeacher implements Teacher{
public void teachMath() {
//普通的数学老师只能教数学
}
}
迪米特原则
迪米特法则:一个对象应该对其他对象保持最少的了解
简单来说就是对象与对象之间的关系越简单越好,最后能通过接口,继承,代理等方式关联。