迪米特法则(Law of Demeter)又叫作最少知道原则(The Least Knowledge Principle),一个类对于其他类知道的越少越好,就是说一个对象应当对其他对象有尽可能少的了解,只和朋友通信,不和陌生人说话。
降低类与类之间的耦合度
迪米特法则又叫最少知道原则,即一个类对自己依赖的类知道的越少越好,对于依赖的类不管有多复杂,都尽量将逻辑封装在类的内部,对外除了提供public方法,不泄漏任何信息。
更简单的说法:只与直接朋友通信。
直接朋友:每个对象都会与其它对象有耦合关系,耦合的方式有很多,依赖、关联、组合、聚合等。我们称出现在成员变量,方法参数,方法返回值中的类称为直接朋友,而出现在局部变量中的类不能称为直接朋友,也就是说,陌生的类不要以局部变量的形式出现在类的内部。
过分的使用迪米特原则,会产生大量这样的中介和传递类,类之间需要通信就通过第三方转发的方式,就会造成系统的不同模块之间的通信效率降低、使系统的不同模块之间不容易协调等缺点,同时大大增加了系统的复杂度。所以在釆用迪米特法则时需要反复权衡,确保高内聚和低耦合的同时,保证系统的结构清晰。
通过模拟开发部经理、项目经理、员工之间的关系的例子来说明迪米特法则。
员工负责一个项目中某个模块的设计、开发、测试;有姓名、项目、模块、进度、计划完成时间属性。
package com.guor.beanutil.principle.dimit;
import lombok.Data;
@Data
public class Programmer {
// 姓名
private String name;
// 项目
private String project;
// 模块
private String module;
// 进度
private String schedule;
// 计划完成时间
private String completePlanTime;
public Programmer(String name, String project, String module, String schedule, String completePlanTime) {
this.name = name;
this.project = project;
this.module = module;
this.schedule = schedule;
this.completePlanTime = completePlanTime;
}
}
在项目经理类中初始化了项目人员信息,同时提供了简单的接口。
package com.guor.beanutil.principle.dimit;
import java.util.ArrayList;
import java.util.List;
public class ProjectManager {
// 姓名
private String name;
// 项目
private String project;
// 进度
private String schedule;
// 计划完成时间
private String completePlanTime;
// 项目开发人员
private static List<Programmer> programmerList;
static {
programmerList = new ArrayList<>();
programmerList.add(new Programmer("哪吒","哪吒闹海","技术选型,框架搭建","50%","2022-08-31"));
programmerList.add(new Programmer("妲己","哪吒闹海","前端","30%","2022-08-31"));
programmerList.add(new Programmer("敖丙","哪吒闹海","权限项目","20%","2022-08-31"));
programmerList.add(new Programmer("申公豹","哪吒闹海","管理模块","20%","2022-08-31"));
programmerList.add(new Programmer("二郎神","哪吒闹海","数据迁移","10%","2022-08-31"));
}
public static List<Programmer> getProgrammerList(){
return programmerList;
}
}
可以获取项目开发人员信息、开发进度、预计交付时间等。
类中通过Stream对代码进行了简化,我觉得Stream是java8新特性中最好用,也是最简洁的,有兴趣的可以体验一下【Java8 新特性 5】Java 8 stream的详细用法
package com.guor.beanutil.principle.dimit;
import java.util.*;
import java.util.stream.Collectors;
public class DepartmentManager {
/**
* 获取项目信息
*/
public static Map<String, Object> getProjectInfo(){
Map<String, Object> map = new HashMap<>();
String project = ProjectManager.getProgrammerList().get(0).getProject();
Double schedule = getSchedule3();
Date completePlanTime = getCompletePlanTime2();
map.put("项目",project);
map.put("项目进度",schedule);
map.put("预计完成时间",completePlanTime);
return map;
}
/**
* 项目列表
*/
public static List<String> getProjects(){
List<String> list = new ArrayList<>();
List<Programmer> programmerList = ProjectManager.getProgrammerList();
for (Programmer programmer : programmerList){
String project = programmer.getProject();
list.add(project);
}
return list;
}
/**
* 获取项目进度
* 写法1
*/
public static double getSchedule1(){
List<Programmer> programmerList = ProjectManager.getProgrammerList();
double sum = 0;
for (Programmer programmer : programmerList){
double schedule = programmer.getSchedule();
sum += schedule;
}
double avg = sum/programmerList.size();
return avg;
}
/**
* 获取项目进度
* 写法2
*/
public static Double getSchedule2(){
List<Programmer> programmerList = ProjectManager.getProgrammerList();
List<Double> scheduleList = new ArrayList<>();
for (Programmer programmer : programmerList){
scheduleList.add(programmer.getSchedule());
}
return scheduleList.stream().collect(Collectors.averagingDouble(Double::doubleValue));
}
/**
* 获取项目进度
* 写法3
*/
public static Double getAvgSchedule3(){
return ProjectManager.getProgrammerList().stream().mapToDouble( Programmer::getSchedule ).average().orElse(0d);
}
/**
* 项目预计完成时间
*
* 获取项目组内开发人员最晚完成时间即可
* 写法1
*/
public static Date getCompletePlanTime1(){
List<Programmer> programmerList = ProjectManager.getProgrammerList();
List<Date> dateList = new ArrayList<>();
for (Programmer programmer : programmerList){
dateList.add(programmer.getCompletePlanTime());
}
return dateList.stream().max(Date::compareTo).get();
}
/**
* 项目预计完成时间
*
* 获取项目组内开发人员最晚完成时间即可
* 写法2
*/
public static Date getCompletePlanTime2(){
return ProjectManager.getProgrammerList().stream().map(Programmer::getCompletePlanTime).max(Date::compareTo).get();
}
}
通过部门经理管理所有程序员,项目经理只提供了非常简单的信息,都是由部门经理类来操作,也就是说,一切事宜都是部门经理和程序员沟通,那,要项目经理干啥?部门经理管理10多个项目,上百号人,岂不是得累死…是的,你违反了迪米特法则~~
部门经理直接调用项目经理类中的方法,获取项目名称,项目进展,预计完成时间等信息。
这样一来,整个功能逻辑就非常清晰了。
package com.guor.beanutil.principle.dimit;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
public class ProjectManager {
// 姓名
private String name;
// 项目
private String project;
// 进度
private String schedule;
// 计划完成时间
private String completePlanTime;
// 项目开发人员
private static List<Programmer> programmerList;
static {
programmerList = new ArrayList<>();
programmerList.add(new Programmer("哪吒","哪吒闹海","技术选型,框架搭建",0.5,Programmer.getDate("2022-08-20")));
programmerList.add(new Programmer("妲己","哪吒闹海","前端",0.3,Programmer.getDate("2022-08-25")));
programmerList.add(new Programmer("敖丙","哪吒闹海","权限项目",0.2,Programmer.getDate("2022-08-31")));
programmerList.add(new Programmer("申公豹","哪吒闹海","管理模块",0.2,Programmer.getDate("2022-08-31")));
programmerList.add(new Programmer("二郎神","哪吒闹海","数据迁移",0.1,Programmer.getDate("2022-09-05")));
}
public static List<Programmer> getProgrammerList(){
return programmerList;
}
/**
* 项目列表
*/
public static List<String> getProjects(){
return ProjectManager.getProgrammerList().stream().map(Programmer::getProject).collect(Collectors.toList());
}
/**
* 获取项目进度
*/
public static Double getAvgSchedule(){
return ProjectManager.getProgrammerList().stream().mapToDouble( Programmer::getSchedule ).average().orElse(0d);
}
/**
* 项目预计完成时间
*
* 获取项目组内开发人员最晚完成时间即可
*/
public static Date getCompletePlanTime(){
return ProjectManager.getProgrammerList().stream().map(Programmer::getCompletePlanTime).max(Date::compareTo).get();
}
}
package com.guor.beanutil.principle.dimit;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class DepartmentManager {
public static Map<String, Object> getProjectInfo(){
Map<String, Object> map = new HashMap<>();
String project = ProjectManager.getProjects().get(0);
Double schedule = ProjectManager.getAvgSchedule();
Date completePlanTime = ProjectManager.getCompletePlanTime();
map.put("项目",project);
map.put("项目进度",schedule);
map.put("预计完成时间",completePlanTime);
return map;
}
}
迪米特法则虽然看似简单,但如果想在实际项目开发中,将各模块、功能规划的井井有条,运用的炉火纯青、恰到好处,真的很难。反复阅读,仔细体会。
设计模式系列文章:
java设计模式1,单一职责原则
java设计模式2,开闭原则
java设计模式3,里氏替换原则