这是我参与「掘金日新计划 · 2 月更文挑战」的第 10 天,点击查看活动详情
系列文章|源码
定义-是什么
迪米特法则(Law of Demeter,LoD)又叫作最少知识原则(Least Knowledge Principle,LKP),产生于 1987 年美国东北大学(Northeastern University)的一个名为迪米特(Demeter)的研究项目,由伊恩·荷兰(Ian Holland)提出,被 UML 创始者之一的布奇(Booch)普及,后来又因为在经典著作《程序员修炼之道》(The Pragmatic Programmer)提及而广为人知。
迪米特法则(Law of Demeter)又叫作最少知识原则(The Least Knowledge Principle),一个类对于其他类知道的越少越好,就是说一个对象应当对其他对象有尽可能少的了解,只和朋友通信,不和陌生人说话。英文简写为:LOD。
这意味着一个对象只应该与直接的朋友通信,而不应该与陌生人通信。在编程中,一个对象只应该引用其直接关联的对象,而不应该引用其他对象的内部状态。
核心:最少依赖,尽量降低类与类之间的耦合。
只与直接的朋友通信
- 朋友:有耦合(依赖、关联、组合、聚合)关系的对象;
- 直接朋友:成员变量,方法参数,方法返回值中的类;
思考-为什么
迪米特法则的好处是减少了对象之间的耦合,提高了系统的可维护性和可扩展性。如果遵循迪米特法则,当修改一个对象时,不会对其他对象产生影响,因此系统更加稳定。
优点
- 降低类之间的耦合度,提高模块的相对独立性。
- 由于亲和度降低,从而提高了类的可复用率和系统的扩展性。
缺点
过度使用迪米特法则会使系统产生大量的中介类,从而增加系统的复杂性,使模块之间的通信效率降低。所以,在釆用迪米特法则时需要反复权衡,确保高内聚和低耦合的同时,保证系统的结构清晰。
注意事项
- 在类的划分上,应当创建弱耦合的类,类与类之间的耦合越弱,就越有利于实现可复用的目标。
- 在类的结构设计上,每个类都应该降低成员的访问权限。
- 在类的设计上,只要有可能,一个类应当设计成不变的类。
- 在对其他类的引用上,一个对象对其他类的对象的引用应该降到最低。
- 尽量限制局部变量的有效范围,降低类的访问权限。
应用-怎么用
设计模式的门面模式(Facade)和中介模式(Mediator),都是迪米特法则的应用。
案例1:只和直接的朋友交流 --- 班长点名
需求:老师叫班长点名;
public class Student {
}
// 班长
public interface IGroupLeader {
// 清点人数
void count(List<Student> students);
}
public class GroupLeaderImpl implements IGroupLeader {
@Override
public void count(List<Student> students) {
if (null != students && students.size() > 0) {
System.out.println("上课的学生人数是:" + students.size());
}
}
}
// 老师
public interface ITeacher {
void command(IGroupLeader iGroupLeader);
}
public class TeacherImpl implements ITeacher {
@Override
public void command(IGroupLeader iGroupLeader) {
// 全班同学
List<Student> allStudent = new ArrayList<>();
allStudent.add(new Student());
allStudent.add(new Student());
allStudent.add(new Student());
allStudent.add(new Student());
allStudent.add(new Student());
// 班长清点人数
iGroupLeader.count(allStudent);
}
}
public class Client {
public static void main(String[] args) {
// 老师
TeacherImpl teacher = new TeacherImpl();
// 班长
GroupLeaderImpl groupLeader = new GroupLeaderImpl();
// 老师叫班长点名
teacher.command(groupLeader);
}
}
// 运行结果
上课的学生人数是:5
新建学生、班长、老师接口,并实现接口,实现既定需求。
此处老师实现类 TeacherImpl
有两个朋友,一个是班长接口 IGroupLeader
,一个是学生类 Student
; 两个朋友中,班长接口是直接朋友,因为他是入参,而学生类并不是直接朋友,因为他既不是成员变量也不是方法参数更不是方法返回值中的类。所以老师类的设计不符合迪米特法则!
根据迪米特法则进行修改:
public class Student {
}
// 班长
public interface IGroupLeader {
// 清点人数
void count(List<Student> students);
}
public class GroupLeaderImpl implements IGroupLeader {
private List<Student> students;
public GroupLeaderImpl(List<Student> students) {
this.students = students;
}
@Override
public void count() {
if (null != students && students.size() > 0) {
System.out.println("上课的学生人数是:" + students.size());
}
}
}
// 老师
public interface ITeacher {
void command(IGroupLeader iGroupLeader);
}
public class TeacherImpl implements ITeacher {
@Override
public void command(IGroupLeader iGroupLeader) {
// 班长清点人数
iGroupLeader.count();
}
}
public class Client {
public static void main(String[] args) {
// 全班同学
List<Student> allStudent = new ArrayList<>();
allStudent.add(new Student());
allStudent.add(new Student());
allStudent.add(new Student());
allStudent.add(new Student());
allStudent.add(new Student());
allStudent.add(new Student());
// 老师
TeacherImpl teacher = new TeacherImpl();
// 班长
GroupLeaderImpl groupLeader = new GroupLeaderImpl(allStudent);
// 老师叫班长点名
teacher.command(groupLeader);
}
}
// 运行结果
上课的学生人数是:6
将老师实现类进行调整,各司其职,只与自己的直接朋友交流,这样也有效地减少了耦合;
案例2:减少对朋友的了解
如何减少对朋友的了解?如果你的朋友是个话痨加大喇叭,那就算你不主动去问他,他也会在你面前说个不停,把他所有的经历都讲给你听。所以,要减少对朋友的了解,请换一个内敛一点的朋友吧~换作在一个类中,就是尽量减少一个类对外暴露的方法。
需求: 人用咖啡机煮咖啡,煮咖啡分三步:1.加咖啡豆;2.加水;3.制作咖啡:
仓库代码地址:github.com/tyronczt/de…
// 咖啡机接口
public interface ICoffeeMachine {
//加咖啡豆
void addCoffeeBean();
//加水
void addWater();
//制作咖啡
void makeCoffee();
}
// 咖啡机实现类
public class CoffeeMachine implements ICoffeeMachine{
@Override
public void addCoffeeBean() {
System.out.println("加咖啡豆");
}
@Override
public void addWater() {
System.out.println("加水");
}
@Override
public void makeCoffee() {
System.out.println("制作咖啡");
}
}
// 人接口
public interface IMan {
// 制作咖啡
void makeCoffee();
}
// 人实现类
public class Man implements IMan {
private ICoffeeMachine coffeeMachine;
public Man(ICoffeeMachine coffeeMachine) {
this.coffeeMachine = coffeeMachine;
}
/**
* 制作咖啡
*/
@Override
public void makeCoffee() {
coffeeMachine.addWater();
coffeeMachine.addCoffeeBean();
coffeeMachine.makeCoffee();
}
}
// 客户端类
public class Client {
public static void main(String[] args) {
ICoffeeMachine coffeeMachine = new CoffeeMachine();
IMan man = new Man(coffeeMachine);
man.makeCoffee();
}
}
// 输出结果
加水
加咖啡豆
制作咖啡
按照上述代码实现了既定的需求,CoffeeMachine
是 Man
的直接好友,但问题是 Man
对CoffeeMachine
了解的太多了,其实人根本不关心咖啡机具体制作咖啡的过程。所以我们可以作如下优化:优化后的咖啡机类,只暴露一个 work
方法,把制作咖啡的三个具体的方法 addCoffeeBean
、addWater
、makeCoffee
设为私有:
// 咖啡机接口
public interface ICoffeeMachine {
//咖啡机工作
void work();
}
// 咖啡机实现类
public class CoffeeMachine implements ICoffeeMachine{
//加咖啡豆
public void addCoffeeBean() {
System.out.println("加咖啡豆");
}
//加水
public void addWater() {
System.out.println("加水");
}
//制作咖啡
public void makeCoffee() {
System.out.println("制作咖啡");
}
@Override
public void work() {
addWater();
addCoffeeBean();
makeCoffee();
}
}
// 人接口
public interface IMan {
// 制作咖啡
void makeCoffee();
}
// 人实现类
public class Man implements IMan {
private ICoffeeMachine coffeeMachine;
public Man(ICoffeeMachine coffeeMachine) {
this.coffeeMachine = coffeeMachine;
}
/**
* 制作咖啡
*/
@Override
public void makeCoffee() {
coffeeMachine.work();
}
}
// 客户端类
public class Client {
public static void main(String[] args) {
ICoffeeMachine coffeeMachine = new CoffeeMachine();
IMan man = new Man(coffeeMachine);
man.makeCoffee();
}
}
// 输出结果
加水
加咖啡豆
制作咖啡
这样修改后,通过减少 CoffeeMachine
对外暴露的方法,减少 Man
对 CoffeeMachine
的了解,从而降低了它们之间的耦合。
在实践中,只要做到只和直接的朋友交流和减少对朋友的了解,就能满足迪米特法则。