迪米特法则又称之为最小认知法则(LKP) ,意思是一个类应该最自己耦合或者调用的类,知道越少越好,被调用的类内部如何复杂,调用方均不用理会,不关心其他的问题。迪米特法则还有一个比较有意思的英文别称: 只和自己的直接朋友通讯。 什么是直接朋友呢? 两个对象之间的耦合就成为朋友关系,这种关系的类型有很多,例如组合、聚合、依赖等。
5.1 点名示例
老师 Teacher 让组长 GroupLeader 去统计组内的学生的数量,直接写出的简单设计类图如下:
大致代码如下:
public class LKP {public static void main(String[] args) {Teacher teacher = new Teacher();teacher.command(new GroupLeader());}static class Teacher {public void command(GroupLeader leader) {List<Student> stuList = new ArrayList<>();// 增加学生int count = leader.count(stuList);System.out.println("学生有" + count + "个");}}static class GroupLeader {public int count(List<Student> stuList) {return stuList.size();}}static class Student {private String name;}}
5.2 代码改进
基于上面的分析,笔者认为,Teacher不应对Student类进行直接依赖,只需要将这部分内部逻辑交给 GroupLeader即可,修改后的类图如下:
代码如下,修改后的代码中可以明显的看到,Teacher 类不在对Student直接依赖。
public class LKP2 {public static void main(String[] args) {Teacher teacher = new Teacher();GroupLeader groupLeader = new GroupLeader();teacher.command(groupLeader);}static class Teacher {public void command(GroupLeader leader) {int count = leader.count();System.out.println("学生有" + count + "个");}}static class GroupLeader {private List<Student> stuList;public void setStuList() {List<Student> list = new ArrayList<>();// 调用其他服务查询学生列表this.stuList = list;}public int count() {return stuList.size();}}static class Student {private String name;}}
5.3 最佳实践
- 尽量少的依赖非必须的类
[x] 不要过多的调用直接朋友的方法,调用过多,可以将该方法封装在依赖的类中,比如下面的逻辑.
判断手机是否是一个好手机,需要判断价格是否便宜 & 质量好 & 新款手机
public class LKP3 {public static void main(String[] args) {Phone phone = new Phone();boolean result = !phone.isCheap() && phone.isGood() && phone.isNewVersion();System.out.println("手机是一款好手机吗?" + result);}static class Phone {public boolean isCheap() {return false;}public boolean isGood() {return true;}public boolean isNewVersion() {return true;}}}
事实上,上面的代码存在两个问题:
- 暴露过多的public方法
- 过多的依赖直接朋友,多次访问直接朋友的方法,修改后的代码如下
package com.zhoutao123.design.principle;public class LKP3 {public static void main(String[] args) {Phone phone = new Phone();System.out.println("手机是一款好手机吗?" + phone.check());}static class Phone {private boolean isCheap() {return false;}private boolean isGood() {return true;}private boolean isNewVersion() {return true;}public boolean check() {return !this.isCheap() && this.isGood() && this.isNewVersion();}}}
