单一职责原则
单一职责原则(Single Responsibility Principle, SRP)简而言之就是对于一个类或者接口, 引起其改变的应该只能有一个原因. 比如要将负责属性和行为的类分开.
里氏替换原则
定义:所有引用基类的地方必须能透明地使用其子类的对象. 只要父类出现的地方, 子类就可以出现, 而且替换为子类不会产生任何错误或者一场. 但是反过来不一定可行.
- 子类中可以增加自己特有的方法。
- 当子类的方法重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。
- 当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。
前置条件
当子类的方法重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。
class Father {
public Collection doSomething(HashMap map){
StdOut.println("父类被执行...");
return map.values();
}
}
class Son extends Father{
public Collection doSomething(Map map) {
StdOut.println("子类被执行");
return map.values();
}
}
子类方法和父类方法, 方法名相同, 返回类型相同, 但是参数不同, 所以不是Override, 而是Overload. 在这种情况下, 如果传入HashMap
, 子类的doSomething()
不会被执行.
这是正确的, 因为子类并没有重写父类方法, 而是重载父类方法, 所以如果父类的前置条件(形参) 范围宽于子类则不正确.
public static void main(String[] args){
Father f=new Father();
HashMap map=new HashMap();
f.doSomething(map);
}
父类被执行
public static void main(String[] args){
//父类出现的地方都可以被子类代替, 且不会改变逻辑
Son f=new Son();
HashMap map=new HashMap();
f.doSomething(map);
}
父类被执行
public static void main(String[] args){
//子类出现的地方, 父类不一定可以代替
Son f=new Son();
Map map=new HashMap();
f.doSomething(map);
}
子类被执行
class Father {
public Collection doSomething(Map map){
StdOut.println("父类被执行...");
return map.values();
}
}
class Son extends Father{
public Collection doSomething(HashMap map) {
StdOut.println("子类被执行");
return map.values();
}
}
public static void main(String[] args){
Father f=new Father();
HashMap map=new HashMap();
f.doSomething(map);
}
父类被执行
public static void main(String[] args){
//父类出现的地方都可以用子类代替
Son f=new Son();
HashMap map=new HashMap();
f.doSomething(map);
}
子类被执行
可以注意到, 此时子类方法被执行了, 而子类并没有重写父类的相应的方法, 而是重载了父类的方法.
public static void main(String[] args){
Son f=new Son();
Map map=new HashMap();
f.doSomething(map);
}
父类被执行
后置条件
当子类的方法实现或覆写父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格或相同。
注意点
- 在类中调用其他类时务必使用其父类或者接口, 如果不能使用父类或者接口, 则说明类的设计已经违背了LSP原则.
- 如果子类不能完整地实现父类的方法, 或者父类的一些方法在子类中完全和父类业务逻辑不同, 则建议不使用继承关系, 而是用依赖, 聚集, 组合等关系代替.
依赖倒置原则
Dependence Inversion Principle
- 高层模块不应该依赖低层模块
- 抽象不应该依赖细节
- 细节应依赖抽象
名称 | 含义 |
---|---|
高层模块 | 原子逻辑再组装就是高层模块 |
低层模块 | 每一个逻辑的实现都由院子逻辑组成,不分割的原子逻辑就是低层模块 |
对于Java来说.
- 模块间的依赖通过抽象产生,实现类之间不发生直接依赖关系, 其依赖关系是通过接口或者抽象类产生的
- 接口或这抽象类不依赖于实现类
- 实现类依赖接口或抽象类
这样也有利于并行开发, 即使只完成了一部分工作, 仍可以进行单元测试.
这也符合现代开发的流程, 先写好单元测试类, 再写实现类.
规则
- 每个类尽量都有接口或抽象类, 或者抽象类和接口都具备
- 变量的表面类型尽量是接口或者是抽象类(如果使用类的clone方法, 就必须使用实现类)
- 任何类都不应该从具体类中派生(如果实在需要继承自具体类, 尽量不要超过两层的继承)
- 尽量不要覆写基类的方法
总体而言, 依赖倒置原则是六大原则中最难实现的, 也是实现开闭原则的重要途径. 总体而言, 把握住面向接口编程即可.