一、单一职责原则
意思是每个类都只负责单一的功能,切不可太多,并且一个类应当尽量的把一个功能做到极致。
违背该原则代码示例:
package com.whl.sixPrinciple;
import java.io.*;
/**
* Author heling on 2018/11/27
*/
public class Caculator {
public int add() throws IOException {
File file = new File("E:\\Caculator.txt");
BufferedReader br = new BufferedReader(new FileReader(file));
Integer a = Integer.valueOf(br.readLine());
Integer b = Integer.valueOf(br.readLine());
return a + b;
}
public static void main(String[] args) throws IOException {
Caculator caculator = new Caculator();
int result = caculator.add();
System.out.println(result);
}
}
存在问题:
当我们需要在计算器类添加减法,乘法,除法等其它算法的时候,我们必须要复制读取a和b数据的方法,并且新加算法,造成代码冗余,难以维护。
分析一下,这个类做了两件事情:1.获取文件里的数据、2.计算。下面我们根据单一职责原则重新设计一下:
数据读取类:
package com.whl.sixPrinciple;
import java.io.*;
/**
* 数据读取类
* Author heling on 2018/11/27
*/
public class NumReader {
private int a;
private int b;
public NumReader() throws IOException {
File file = new File("E:\\Caculator.txt");
BufferedReader br = new BufferedReader(new FileReader(file));
a= Integer.valueOf(br.readLine());
b= Integer.valueOf(br.readLine());
}
public int getA() {
return a;
}
public int getB() {
return b;
}
}
计算类:
package com.whl.sixPrinciple;
import java.io.*;
/**
* Author heling on 2018/11/27
*/
public class Caculator {
public int add() throws IOException {
NumReader numReader = new NumReader();
return numReader.getA() + numReader.getB();
}
public static void main(String[] args) throws IOException {
Caculator caculator = new Caculator();
int result = caculator.add();
System.out.println(result);
}
}
由此可见,将功能分开后,我们添加其他算法的时候,就不必重复复制数据读取方法,方便维护。
二、里式替换原则
意思是一个子类应该可以替换掉父类并且可以正常工作。
那么翻译成比较容易理解的话,就是说,子类一般不该重写父类的方法,因为父类的方法一般都是对外公布的接口,是具有不可变性的,你不该将一些不该变化的东西给修改掉。
上述只是通常意义上的说法,很多情况下,我们不必太理解里氏替换,比如模板方法模式,缺省适配器,装饰器模式等一些设计模式,就完全不搭理这个原则。
不过就算如此,如果你真的遇见了不得不重写父类方法的场景,那么请你考虑,你是否真的要把这个类作为子类出现在这里,或者说这样做所换来的是否能弥补你失去的东西,比如子类无法代替父类工作,那么就意味着如果你的父类可以在某一个场景里工作的很正常,那么你的子类当然也应该可以,否则就会出现下述场景。
比如我们有某一个类,其中有一个方法,调用了某一个父类的方法。
父类:
package com.whl.sixPrinciple;
/**
* Author heling on 2018/11/27
*/
public class Parent {
public void show() {
System.out.println("parent method");
}
}
子类:重写父方法
package com.whl.sixPrinciple;
/**
* Author heling on 2018/11/27
*/
public class Son extends Parent {
@Override
public void show() {
System.out.println(1/0);
}
}
某类:
package com.whl.sixPrinciple;
/**
* Author heling on 2018/11/27
*/
public class AClass {
public void show(Parent parent) {
parent.show();
}
public static void main(String[] args) {
AClass aClass = new AClass();
aClass.show(new Parent());
aClass.show(new Son());
}
}
这就相当于埋下了一个个陷阱,因为本来我们的原则是,父类可以完成的地方,我用子类替代是绝对没有问题的,但是这下反了,我每次使用一个子类替换一个父类的时候,我还要担心这个子类有没有给我埋下一个上面这种炸弹。
所以里氏替换原则是一个需要我们深刻理解的原则,因为往往有时候违反它我们可以得到很多,失去一小部分,但是有时候却会相反,所以要想做到活学活用,就要深刻理解这个原则的意义所在。
三、接口隔离原则
也称接口最小化原则,强调的是一个接口拥有的行为应该尽可能的小。
如果违背这个原则的话,实现类必须本来不该实现的方法,比如以下案例:
package com.whl.sixPrinciple;
/**
* Author heling on 2018/11/27
*/
public interface Mobile {
void call();
void sendMsg();
void map();//导航
}
比如如果某智能手机实现这个接口没问题,但是非智能手机实现的话就有问题,因为不具有导航功能,但却必须实现这个导航方法。下面是遵循接口隔离原则的设计:
package com.whl.sixPrinciple;
/**
* Author heling on 2018/11/27
*/
public interface SmartMobile extends Mobile {
void map();
}
这样的话就不存在之前的问题了。
四、依赖倒置原则
高层模块不该依赖于低层模块,二者都应该依赖于抽象,抽象不应该依赖于细节,细节应该依赖于抽象。
就是说接口思想,拿前面的计算器类来举例子:
代码是从文件里读取数字,那么现在我们想要从数据库,键盘录入等其它方式获取数据,该怎么办?
也许你这样解决复制前面的NumReader,然后改掉里面获取数据的方法,那么问题来了,计算器Caculator也要去改,将NumReader改为DbReader,这就违背了依赖倒置原则,下面这样设计:
package com.whl.sixPrinciple;
/**
* Author heling on 2018/11/28
*/
public interface Reader {
public int getA();
public int getB();
}
package com.whl.sixPrinciple;
import java.io.*;
/**
* Author heling on 2018/11/28
*/
public class FilesReader implements Reader {
private int a;
private int b;
public FilesReader() throws IOException {
File file = new File("E:\\Caculator.txt");
BufferedReader br = new BufferedReader(new FileReader(file));
a= Integer.valueOf(br.readLine());
b= Integer.valueOf(br.readLine());
}
@Override
public int getA() {
return a;
}
@Override
public int getB() {
return b;
}
}
package com.whl.sixPrinciple;
import java.io.*;
/**
* Author heling on 2018/11/27
*/
public class Caculator {
public int add(Reader reader) throws IOException {
// NumReader numReader = new NumReader();
// return numReader.getA() + numReader.getB();
// Reader reader = new FilesReader();
return reader.getA() + reader.getB();
}
public static void main(String[] args) throws IOException {
Caculator caculator = new Caculator();
int result = caculator.add(new FilesReader());
System.out.println(result);
}
}
这样就不用关心数据源哪里来,面向接口,只要得到a,b就行,不关注如何得到。
五、迪米特原则
也称最小知道原则,即一个类应该尽量不要知道其他类太多的东西,不要和陌生的类有太多接触。
这个原则的制定,是因为如果一个类知道或者说是依赖于另外一个类太多细节,这样会导致耦合度过高,应该将细节全部高内聚于类的内部,其他的类只需要知道这个类主要提供的功能即可。所谓高内聚就是尽可能将一个类的细节全部写在这个类的内部,不要漏出来给其他类知道,否则其他类就很容易会依赖于这些细节,这样类之间的耦合度就会急速上升,这样做的后果往往是一个类随便改点东西,依赖于它的类全部都要改。
六、开闭原则
对修改关闭,对扩展开放。
总结:
六大原则尽量遵循,不一定完全遵循,就像数据库不一定一定要按照三大范式设计,可以反范式。