2. 里氏替换原则:LSP(Liskov Substitution Principle)里氏替换原则,定义为只要父类出现的地方子类就可以出现,而且用子类替换后,程序也不会出现问题,使用者根本不用关心是父类还是子类。这也是我们java继承特性的一个表现。
下面用个实际项目中遇到的一个简单的需求:现在有A类产品需要调用method1(),B类产品也需要调用method(),同样C类产品也需要调用method(),但是我们需要直接让D这个人去处理这个Method(),那么我们可以这样设计一个抽象类AbstractB(或者接口)。
AbstractB类代码如下:
1 public abstract class AbstractB { 2 //实现逻辑 3 public abstract void Method(); 4 }
下面A,B,C类分别继承抽象类AbstractB:
1 public class A extends AbstractB { 2 3 @Override 4 public void Method() { 5 System.out.println("A实现Method...."); 6 } 7 8 }
public class B extends AbstractB { @Override public void Method() { System.out.println("B实现Method...."); } }
public class C extends AbstractB { @Override public void Method() { System.out.println("C实现Method...."); } }
设计D类
1 @SuppressWarnings("unused") 2 public class D { 3 4 public AbstractB abstractObj; 5 6 public void setRealObj(AbstractB realObj){ 7 this.abstractObj = abstractObj; 8 } 9 10 public void invocateMethod(){ 11 //方法调用 12 abstractObj.Method(); 13 } 14 }
注意:在类中调用其他类对象的时候,必须要使用父类或者接口,如果没有的话,则说明违背了LSP原则。
写个测试类:
1 public class test { 2 3 public static void main(String[] aa){ 4 D d = new D(); 5 d.setRealObj(new A()); 6 /*d.setRealObj(new B()); 7 d.setRealObj(new C());*/ 8 d.invocateMethod(); 9 } 10 11 }
上面的setRealObj方法中,传递一个子类实体,这里制定的是父类类型,这里我们不用关心传递的是父类还是子类。相反如果参数类型为子类,传递的一个父类的类型,这时就不一定适用。就与里氏替换原则违背,如果现在有个E产品也需要实现Method方法,只需再实现抽象类AbstractB即可。
里氏替换原则的目的就在于增强程序的健壮性,便于系统的兼容性。及时增加子类也可以很好的满足要求继续执行。
这个其实与LSP挺相似的。都是尽量与上层交互。看段代码说话。
比如说司机开汽车,车子有宝马,有奔驰,有其他的型号的汽车。
定一个司机接口Driver
1 public interface IDriver { 2 public void drive();//开车 3 }
定义一个汽车接口Car
1 public interface ICar { 2 public void run();//汽车奔跑 3 }
定义宝马,奔驰实现汽车接口
1 public class Benz implements ICar { 2 3 @Override 4 public void run() { 5 System.out.println("奔驰跑起来...."); 6 } 7 8 }
public class Bmw implements ICar { @Override public void run() { System.out.println("宝马跑起来...."); }
}
实现司机接口:
public class Driver implements IDriver { public ICar car; //这里用构造函数传递依赖对象 public Driver(ICar _car){ this.car = _car; } @Override public void drive() { this.car.run(); } }
实现依赖注入还有另外两种方式。seter方法注入、接口声明传递。
依赖倒置本质就是通过抽象类或接口实现各个类或者模块彼此独立化,实现模块间的松耦合。我们尽量遵循以下几点:
第一:每个类尽量都有接口或者抽象类或者两者都有。
第二:变量的表面类型尽量是接口或者抽象类。
第三:尽量不要具体类派生,尽量不要复写基类的方法。
第四:结合里氏替换原则使用。个人感觉两者其实意义上有很大的相同。
通俗的讲,倒置的概念我认为就是用抽象代替细节实在,这就是倒置。