为什么说二进制依赖一个组件时不准调用它的内部API? 比如说,对bean-biz.jar,应用层只准调用BeanService.getBean(),而不准调用BeanDAO.getBean()?
你会说这是为了“封装性”,以“避免不必要的耦合”。但直接耦合BeanDAO.getBean()和BeanDAO.deleteBean()有什么“具体”的坏处呢?
我的结论是:
1.
如果同时依赖BeanService.getBean()和BeanDAO.getBean(),就意味着对同一个操作形成了“调用上的冗余”; 当一个发生变化时,另一个也要跟着变,而且变得很尴尬。
2.
如果绕过BeanService直接调用BeanDAO.deleteBean(),就意味着依赖了组件的内部实现;而内部实现往往是不稳定的,当内部实现改变时,应用层也要发生改变,一种本可以避免的改变。
代码阐述:
1. 调用的冗余
//改变发生前 public class BeanService { public Bean getBean(Long id) { return beanDAO.getBean(id); } } public class WiseAction { public void showBeanDetail(){ Bean bean = beanService.getBean(id); } } public class RogueAction { public void showBeanDetail() { Bean bean = beanDAO.getBean(id); } }
//改变:后来出了规定,对Bean的获取必须走缓存 public class BeanService { public Bean getBean(Long id) { Bean bean = Cache.get(id); if (bean == null) { bean = beanDAO.getBean(id); } return bean; } } ////只依赖了BeanService的应用层代码无须改变 public class WiseAction { public void showBeanDetail(){ Bean bean = beanService.getBean(id); } } ////依赖了BeanDAO的应用层代码需要改变以加入缓存逻辑;同时,这也导致了缓存逻辑的重复 public class RogueAction { public void showBeanDetail() { Bean bean = Cache.get(id); if (bean == null) { bean = beanDAO.getBean(id); } return bean; } }
2. 依赖了不稳定的内部实现
//改变发生前 public class BeanService { public void deleteBean(Long id) { beanDAO.deleteBean(id); } } public class WiseAction { public void removeBean(){ beanService.deleteBean(id); } } public class RogueAction { public void removeBean() { beanDAO.deleteBean(id); } }
//改变:后来规定对Bean不能物理删除,只允许逻辑删除(把某个标志位设置为false) public class BeanService { public void deleteBean(Long id) { Bean bean = getBean(id); bean.setValid(false); beanDAO.updateBean(bean); } } //同时,为了防卫,BeanDAO.deleteBean()方法应该删除 ////只依赖了BeanService的应用层代码不用变 public class WiseAction { public void removeBean(){ beanService.deleteBean(id); } } ////依赖了BeanDAO的应用层代码必须改变,因为BeanDAO.deleteBean()已经不存在了 public class RogueAction { public void removeBean(){ beanService.deleteBean(id); //这时应用层代码也只好改成依赖BeanService,早知今日,何必当初? } }