依赖倒转原则
基本介绍
依赖倒转原则(Dependence Inversion Principle)是指:
1) 高层模块不应该依赖低层模块,二者都应该依赖其抽象
2) 抽象不应该依赖细节,细节应该依赖抽象
3) 依赖倒转(倒置)的中心思想是面向接口编程
4) 依赖倒转原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架构比以细节为基础的架构要稳定的多。在 java 中,抽象指的是接口或抽象类,细节就是具体的实现类
5) 使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成
应用实例
请编程完成 Person 接收消息的功能
1) 实现方案 1 + 分析说明
1. public class DependecyInversion { 2. public static void main(String[] args) { 3. 4. } 5. } 6. class Email { 7. public String getInfo() { 8. return "电子邮件信息: hello,world"; 9. } 10. } 11. 12. class Person { 13. public void receive(Email email ) { 14. System.out.println(email.getInfo()); 15. } 16. }
方式 1 分析
1. 简单,比较容易想到
2. 如果我们获取的对象是 微信,短信等等,则新增类,同时 Perons 也要增加相应的接收方法
3. 解决思路:引入一个抽象的接口 IReceiver, 表示接收者, 这样 Person 类与接口 IReceiver 发生依赖,因为 Email, WeiXin 等等属于接收的范围,他们各自实现 IReceiver 接口就 ok, 这样我们就符号依赖倒转原则
2) 实现方案 2(依赖倒转) + 分析说明
1. public class DependecyInversion2 { 2. public static void main(String[] args) { 3. //客户端无需改变 4. Person person = new Person(); 5. person.receive(new Email2()); 6. person.receive(new WeiXin2()); 7. } 8. } 9. //定义接口 10. interface IReceiver { 11. public String getInfo(); 12. } 13. class Email2 implements IReceiver { 14. public String getInfo() { 15. return "电子邮件信息: hello,world"; 16. } 17. } 18. //增加微信 19. class WeiXin2 implements IReceiver { 20. public String getInfo() { 21. return "微信信息: hello,ok"; 22. } 23. } 24. //方式 2 25. class Person2 { 26. //这里我们是对接口的依赖 27. public void receive(IReceiver receiver ) { 28. System.out.println(receiver.getInfo()); 29. } 30. }
3)依赖关系传递的三种方式和应用案例
1) 接口传递==》应用案例代码
1. interface IOpenAndClose { 2. public void open(ITV tv); //抽象方法,接收接口 3. } 4. 5. interface ITV { //ITV 接口 6. public void play(); 7. } 8. 9. class ChangHong implements ITV { 10. 11. @Override 12. public void play() { 13. // TODO Auto-generated method stub 14. System.out.println("长虹电视机,打开"); 15. } 16. 17. } 18. // 实现接口 19. class OpenAndClose implements IOpenAndClose { 20. public void open(ITV tv) { 21. tv.play(); 22. } 23. }
2) 构造方法传递==》应用案例代码
1. // 方式 2: 通过构造方法依赖传递 2. interface IOpenAndClose { 3. public void open(); //抽象方法 4. } 5. 6. interface ITV { //ITV 接口 7. public void play(); 8. } 9. 10. class OpenAndClose implements IOpenAndClose { 11. public ITV tv; //成员 12. 13. public OpenAndClose(ITV tv) { //构造器 14. this.tv = tv; 15. } 16. 17. public void open() { 18. this.tv.play(); 19. } 20. }
3) setter 方式传递==》应用案例代码
1. // 方式 3 , 通过 setter 方法传递 2. interface IOpenAndClose { 3. public void open(); // 抽象方法 4. 5. public void setTv(ITV tv); 6. } 7. 8. interface ITV { // ITV 接口 9. public void play(); 10. } 11. 12. class OpenAndClose implements IOpenAndClose { 13. private ITV tv; 14. 15. public void setTv(ITV tv) { 16. this.tv = tv; 17. } 18. 19. public void open() { 20. this.tv.play(); 21. } 22. } 23. 24. class ChangHong implements ITV { 25. @Override 26. public void play() { 27. // TODO Auto-generated method stub 28. System.out.println("长虹电视机,打开"); 29. } 30. }
依赖倒转原则的注意事项和细节
1) 低层模块尽量都要有抽象类或接口,或者两者都有,程序稳定性更好.
2) 变量的声明类型尽量是抽象类或接口, 这样我们的变量引用和实际对象间,就存在一个缓冲层,利于程序扩展和优化
3) 继承时遵循里氏替换原则
里氏替换原则
OO 中的继承性的思考和说明
1) 继承包含这样一层含义:父类中凡是已经实现好的方法,实际上是在设定规范和契约,虽然它不强制要求所有的子类必须遵循这些契约,但是如果子类对这些已经实现的方法任意修改,就会对整个继承体系造成破坏。
2) 继承在给程序设计带来便利的同时,也带来了弊端。比如使用继承会给程序带来侵入性,程序的可移植性降低,增加对象间的耦合性,如果一个类被其他的类所继承,则当这个类需要修改时,必须考虑到所有的子类,并且父类修改后,所有涉及到子类的功能都有可能产生故障
3) 问题提出:在编程中,如何正确的使用继承? => 里氏替换原则
基本介绍
1) 里氏替换原则(Liskov Substitution Principle)在 1988 年,由麻省理工学院的以为姓里的女士提出的。
2) 如果对每个类型为 T1 的对象 o1,都有类型为 T2 的对象 o2,使得以 T1 定义的所有程序 P 在所有的对象 o1 都代换成 o2 时,程序 P 的行为没有发生变化,那么类型 T2 是类型 T1 的子类型。换句话说,所有引用基类的地方必须能透明地使用其子类的对象。
3) 在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类的方法
4) 里氏替换原则告诉我们,继承实际上让两个类耦合性增强了,在适当的情况下,可以通过聚合,组合,依赖 来解决问题。
一个程序引出的问题和思考
该看个程序, 思考下问题和解决思路
1. public class Liskov { 2. public static void main(String[] args) { 3. // TODO Auto-generated method stub 4. A a = new A(); 5. System.out.println("11-3=" + a.func1(11, 3)); 6. System.out.println("1-8=" + a.func1(1, 8)); 7. System.out.println("-----------------------"); 8. B b = new B(); 9. System.out.println("11-3=" + b.func1(11, 3));//这里本意是求出 11-3 10. System.out.println("1-8=" + b.func1(1, 8));// 1-8 11. System.out.println("11+3+9=" + b.func2(11, 3)); 12. } 13. } 14. 15. // A 类 16. class A { 17. // 返回两个数的差 18. public int func1(int num1, int num2) { 19. return num1 - num2; 20. } 21. } 22. 23. // B 类继承了 A 24. // 增加了一个新功能:完成两个数相加,然后和 9 求和 25. class B extends A { 26. //这里,重写了 A 类的方法, 可能是无意识 27. public int func1(int a, int b) { 28. return a + b; 29. } 30. 31. public int func2(int a, int b) { 32. return func1(a, b) + 9; 33. } 34. }
我们发现原来运行正常的相减功能发生了错误。原因就是类 B 无意中重写了父类的方法,造成原有功能出现错误。在实际编程中,我们常常会通过重写父类的方法完成新的功能,这样写起来虽然简单,但整个继承体系的复用性会比较差。特别是运行多态比较频繁的时候