依赖倒转原则:抽象不应该依赖于细节,细节应当依赖于抽象,换言之,要针对接口编程,而不是针对实现编程。
依赖倒转原形要求程序代码中传递参数时或在关联关系中,进来引用层次高的抽象层类,即使用接口和抽象类进行变量类型声明、参数类型声明、方法返回类型声明,以及数据类型的转换等,而不要用具体类来做这些事情。为了确保该原则的应用,一个具体类应当只实现接口或抽象中声明过的方法,而不要给出多余的方法,否则将无法调用到在子类中增加的新方法。
在引入抽象层后,系统将具有很好的灵活性,在程序中尽量使用抽象层来进行编程,而将具体类写在配置文件中,这样一来,如果系统行为发生变化,只需要对抽象层进行扩展,并修改配置文件,而无须修改原有系统的源代码,就能扩展系统的功能,满足开闭原则的要求。
在实现一来倒转原则时,需要针对抽象层编程,而将具体类的对象通过依赖注入的方式注入到其他对象中。依赖注入是指当一个对象要与其他对象发生依赖关系时,通过抽象来注入所依赖的对象。常用的注入方式有3种:构造注入,设置注入和接口注入。构造注入是指通过构造函数来传入具体类的对象,设值注入是指通过set方法来传入具体类的对象,而接口注入是指实现该接口中声明的业务方法来传入具体类的对象。这些方法在定义时使用的是抽象类型,在运行时再传入具体类型的对象。这些方法在定义时使用的是抽象类型,在运行时再传入具体类型的对象,由子类对象来覆盖父类对象。
代码示例:
1 //具体Jim人类 2 public class Jim 3 { 4 public void eat(Apple apple) 5 { 6 Console.WriteLine("Jim eat " + apple.getName()); 7 } 8 } 9 //具体苹果类 10 public class Apple 11 { 12 public String getName() 13 { 14 return "apple"; 15 } 16 } 17 public class Client 18 { 19 public static void main(String[] args) 20 { 21 Jim jim = new Jim(); 22 Apple apple = new Apple(); 23 jim.eat(apple); 24 } 25 }
上面的例子比较简单,Jim吃苹果,看起来没什么问题,但此刻需要改变,小李吃苹果,小王吃香蕉呢?这个时候我们需要根据依赖倒转重新设计一下。
1 //人接口 2 public interface People 3 { 4 public void eat(Fruit fruit);//人都有吃的方法,不然都饿死了 5 } 6 //水果接口 7 public interface Fruit 8 { 9 public String getName();//水果都是有名字的 10 } 11 //具体Jim人类 12 public class Jim : People 13 { 14 public void eat(Fruit fruit) 15 { 16 Console.WriteLine("Jim eat " + fruit.getName()); 17 } 18 } 19 //具体苹果类 20 public class Apple : Fruit 21 { 22 public String getName() 23 { 24 return "apple"; 25 } 26 } 27 //具体香蕉类 28 public class Banana : Fruit 29 { 30 public String getName() 31 { 32 return "banana"; 33 } 34 } 35 36 class Program 37 { 38 static void Main(string[] args) 39 { 40 People jim = new Jim(); 41 Fruit apple = new Apple(); 42 Fruit Banana = new Banana();//这里符合了里氏替换原则 43 jim.eat(apple); 44 jim.eat(Banana); 45 46 Console.ReadLine(); 47 } 48 }
这个时候定义了把高层接口抽象化了,只需要对新增的实现类进行修改就可以了。