依赖倒置原则 Dependence Inversion Principle
定义
高层模块不应该依赖低层模块,两者都应该依赖其抽象 , High level modules should not depend upon low level modules. Both should depend upon abstractions.
抽象不应该依赖细节,Abstractions should not depend upon details.
细节应该依赖抽象,Details should depend upon abstractions.
传统的过程性系统的设计方法倾向于使抽象层次依赖于具体层次。倒置原则就是要把这个错误的依赖关系倒转过来。
通俗来讲就是:
1、每个类尽量都有接口或抽象类,或者抽象类和接口两者都具备
2、变量的显示类型尽量是接口或者是抽象类
3、任何类都不应该从具体类派生
4、尽量不要覆写基类的方法
5、结合里氏替换原则使用
举一个例子。
假设我们开发一个登录系统。需要完成界面+业务逻辑代码+数据存储。我们先只说业务逻辑代码里面的注册用户和密码功能,他目前有两种方式,一、是网页注册的,二是手机注册,如下
传统的写法是创建注册类,然后创建数据保存类。如下
1、不符合DIP写法:
//数据库操作代码
public class DBSave {
//这里有人可能会说不符合单一职责,但是对于数据库来说,保存插入一条新数据,
// 里面包含多个字段其实是可以用一句jobc解决的,视为一个单一功能
public void saveMsgFromWeb(String Name, String Pwd)
{
Log.e("ldy","用户名:"+Name+";密码"+Pwd+";注册类型:网页,"+"保存成功,不符合DIP");
}
public void saveMsgFromPhone(String name, String pwd) {
Log.e("ldy","用户名:"+name+";密码"+pwd+";注册类型:手机,"+"保存成功,不符合DIP");
}
}
//注册
public class Register {
public void registerFromWeb(DBSave dbSave,String Name,String Pwd) {
dbSave.saveMsgFromWeb(Name,Pwd);
}
public void registerFromPhone(DBSave dbSave,String Name,String Pwd) {
dbSave.saveMsgFromPhone(Name,Pwd);
}
}
//测试代码
Register register=new Register();
DBSave dbSave=new DBSave();
register.registerFromWeb(dbSave,"张三","123456");
register.registerFromPhone(dbSave,"李四","654321");
2、如果要符合依赖倒置,首先要创建接口。然后创建抽象类,然后继承抽象类继承不同的功能。如下:
/**
* 抽象层
**/
//先创建接口
public interface IDb {
void saveMsg(String Name, String Pwd);
}
public interface IRegister {
void register(DbSave dbSave, String Name, String Pwd) ;
}
//在创建抽象类,这里的抽象类可有可无,为了演示,我还是把他写出来了。
public abstract class DbSave implements IDb{
}
public abstract class RegisterAbstract implements IRegister {
}
/**
* 底层模块
**/
public class DbSaveFromPhone extends DbSave{
@Override
public void saveMsg(String Name, String Pwd)
{
Log.e("ldy","用户名:"+Name+";密码"+Pwd+";注册类型:手机"+"保存成功,符合DIP");
}
}
public class DbSaveFromWeb extends DbSave{
@Override
public void saveMsg(String Name, String Pwd)
{
Log.e("ldy","用户名:"+Name+";密码"+Pwd+";注册类型:网页"+"保存成功,符合DIP");
}
}
public class RegisterUser extends RegisterAbstract {
@Override
public void register(DbSave dbSave, String Name, String Pwd) {
dbSave.saveMsg(Name,Pwd);
}
}
上面例子展示了细节应该依赖于抽象。
另外如果还有高层模块,也不应该依赖底层模块而应该依赖抽象层,
还有任何类都不应该从具体类派生 ,如果手机注册还要细分成苹果和安卓,要不就新增两个实现类苹果和安卓去依赖注册抽象类,要不就是把手机注册实现类改成抽象类,给苹果和安卓实现类去依赖。不建议苹果实现类去依赖手机注册实现类。
简单代码结果如下
依赖的三种写法
构造函数传递依赖对象
Setter方法传递依赖对象
接口声明依赖对象
接口的方法中声明依赖对象