简介:什么是依赖倒置原则?
软件设计中,多层次之间相互依赖关系需要倒置为抽象类或接口,而不是直接依赖于具体的实现。 具体表现为: 1、上层模块不应该直接依赖下层实现,而应该依赖下层的抽象 2、每一个单独的层次,抽象不应该依赖于细节,而细节应该依赖于抽象。
现在有一个用户类UserBean我们要进行操作:(相当于将用户信息从顶层传到服务层,再从服务层传到底层,由底层逻辑具体去实现操作细节,这里的规则是,界面层(顶层)调用服务层(业务层)接口,服务层调用底层(持久层,也称数据访问层)接口)
package com.work.test.bean; import java.io.Serializable; public class UserBean implements Serializable { private static final long serialVersionUID = -2243170754006229032L; private Long id;//用户id private String userName;//用户姓名 public UserBean() { super(); // TODO Auto-generated constructor stub } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } @Override //复写toString方法 public String toString() { return "UserBean [id=" + id + ", userName=" + userName + "]"; } }
代码实例:
底层(持久层,也称数据访问层,主要实现细节逻辑,具体的操作)
接口代码
IUserDao接口定义:
package com.work.test.reverse; import com.work.test.bean.UserBean; //数据交流层接口定义 public interface IUserDao { //插入用户数据 void insertUserBean(UserBean user); //修改用户数据 void updateUserBean(UserBean user); }接口实现类定义:
IUserDao接口实现类UserDaoImpl类
package com.work.test.reverse; import com.work.test.bean.UserBean; //数据交流层接口实现,方法不实现功能,只是演示方法的可用性 public class UserDaoImpl implements IUserDao{ //添加用户数据方法 public void insertUserBean(UserBean user) { System.out.println(user); } //更改用户数据方法 public void updateUserBean(UserBean user) { System.out.println(user); } }
服务层(主要调用底层的接口)
接口代码:
IUserService接口定义
package com.work.test.reverse; import com.work.test.bean.UserBean; //服务层接口定义 public interface IUserService { //保存用户数据 void saveUserBean(UserBean user); //更改用户数据 void updateUserBean(UserBean user); }接口实现类定义:
IUserService接口实现类UserServiceImpl(注意:在这个服务层实现类里面我们用一个底层的接口引用实现对底层的调用,但是为什么没用直接在接口引用后new一个实例对象出来呢,这是因为要满足依赖倒置原则,多层次之间相互依赖关系需要导致为抽象类或接口,而不是直接依赖于具体的实现,假如我们对UserDaoImpl类的类名进行了修改,那么我们还要在这个类中修改相应的代码)
package com.work.test.reverse; import com.work.test.bean.UserBean; //服务层接口实现,方法不实现功能,只是演示方法的可用性 public class UserServiceImpl implements IUserService{ private IUserDao dao;//定义底层接口,因为服务层方法要调用底层接口实现的方法 //保存用户数据实现 public void saveUserBean(UserBean user) { dao.insertUserBean(user); } //修改用户数据实现 public void updateUserBean(UserBean user) { dao.updateUserBean(user); } }顶层代码:也就是界面层(用于调用业务层接口)这里是测试
下面代码演示了:如何利用xml配置文件对服务层实现类UserServiceImpl,给其内部底层IUserService接口引用进行对象实例化;和顶层中关于服务层IUserService接口引用对象的实例化
package com.work.test.reverse; import java.io.File; import java.lang.reflect.Field; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.io.SAXReader; import com.gezhi.ooad.design.principle.bean.UserBean; public class MainEnter { private static IUserService userServiceImpl;//顶层调用服务层接口 /** *该方法是利用xml文档里的内容读取出来,再利用反射实现实例对象的生成 */ public static IUserService buildUserServiceImpl() { String filePath = System.getProperty("user.dir") + File.separator + "beans.xml"; SAXReader sax = null;//定义使用sax读取xml文档内容 Document document = null; try { sax = new SAXReader(); document = sax.read(new File(filePath));//得到文档对象 Element root = document.getRootElement();//得到文档的根节点 Iterator<Element> it = root.elementIterator();//得到迭代器,可获取根节点下的子节点 Map<String,String> classInfo = new HashMap<>();//保存一级子节点中类名及其地址的映射关系 Map<String,String> propertyInfo = new HashMap<>();//保存服务接口中与需要实例对象的关系,也就是xml中二级子节点之间映射关系 while(it.hasNext()) { Element ele = it.next(); String id = ele.attributeValue("id"); String className = ele.attributeValue("class"); classInfo.put(id, className);//将xml中读出来的id与class地址进行键值对映射关系存储 //判断一级子节点下有没有子节点 Iterator<Element> it2 = ele.elementIterator(); while(it2.hasNext()) { Element ele2 = it2.next(); String ref = ele2.attributeValue("ref");//读取出二级子节点中ref属性包含的内容,也就是UserServiceImpl类中需要为IUserDao类接口引用创建实例对象的类 propertyInfo.put(id, ref);//创建一级id与ref内容之间的映射关系 } } //先各自创建对象 Map<String,Object> objInfo = new HashMap<>();//保存id与类对象之间的关系的映射 Set keys = classInfo.keySet();//返回一级子节点中类名及其地址的映射关系中的节点 for (Object obj : keys) { String key = (String) obj; String className = classInfo.get(key);//通过节点名获取对应的class对象的地址 Class<?> cls = Class.forName(className);//得到类对象,并加载该类 Object object = cls.newInstance();//实例化对象 objInfo.put(key, object);//装入新的映射中 } //然后再使用对象去建立关系 keys = propertyInfo.keySet(); Object master = null; for (Object obj : keys) { String key = (String) obj; String value = propertyInfo.get(key);//获得userDaoImpl这个属性名,也就是ref的内容 //userServiceImpl ---- userDaoImpl之间的使用关系(userServiceImpl类中使用了userDaoImpl) master = objInfo.get(key);//通过id值获得上面实例化的对象 Object slave = objInfo.get(value);//通过ref的内容,也就是userDaoImpl这个字符串名称可以在objInfo这个映射中获取到userDaoImpl这个类的实例对象 Field f = master.getClass().getDeclaredField("dao");//通过这个方法可以去操控属性,私有的也可以,操控userServiceImpl下dao这个属性 f.setAccessible(true); f.set(master, slave);//进行赋值操作,也就是将dao需要的实例对象进行绑定 } return (IUserService) master; } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } return null; } public static void main(String[] args) { userServiceImpl = buildUserServiceImpl(); UserBean user = new UserBean(); user.setId(1L); user.setUserName("小马"); userServiceImpl.saveUserBean(user); } }xml代码:
<?xml version="1.0" encoding="UTF-8"?> <beans> <bean id="userServiceImpl" class="com.work.test.reverse.UserServiceImpl"> <property name="dao" ref="userDaoImpl"></property> </bean> <bean id="userDaoImpl" class="com.work.test.reverse.UserDaoImpl"></bean> </beans>