一、迪米特法则的定义
迪米特法则(Law of Demeter,LoD)也称为最少知识原则(Least Knowledge Principle,LKP),虽然名字不同,但描述的是同一个规则:
One object should have a minimum understanding of other objects
一个对象应该对其他对象有最少的了解
Only talk to your immediate friends ( 只与直接的朋友通信)
核心思想就是:一个对象应该对其他对象有最少的了解。
通俗地讲,一个类应该对自己需要耦合或调用的类知道得最少,你(被耦合或调用的类)的内部是如何复杂都和我没关系,那是你的事情,我就知道你提供的这么多public方法,我就调用这么多,其他的我一概不关心。
迪米特法则和类之间的关系息息相关,两个类之间是否具有依赖关系,一句话:凡是在类中用到了对方,那么他们之间就存在依赖关系(在代码中没有对方,编译都无法通过),例如一个类是另一个类的成员属性,类中某一个方法的返回值类型,某一个方法接收的参数类型,方法内部使用到,类中用到了对方等,类的六大关系中,剩余五种关系都是依赖关系的特例。
一个类对自己依赖的类知道的越少越好,类与类之间关系越密切,耦合度越大,也就是说,对于被依赖的类,不管多么复杂,都尽量将逻辑封装在类的内部,对外除了提供的public方法,不对外泄露任何信息。
迪米特法则还有另一个定义,也就是上面提到的,只与直接的朋友通信,那什么是直接的朋友呢?
每个对象都会与其他对象有依赖关系,只要两个对象之间有依赖关系,我们就说这两个对象之间是朋友关系,依赖的方式可以有很多种,我们上面提到过,其中,我们称出现成员变量,方法参数,方法返回值中的类为直接的朋友,而出现在局部变量中的类不是直接的朋友,也就是说,陌生的类最好不要以局部变量的形式出现在类的内部。
迪米特法则核心思想是降低对象之间的耦合性,使各个对象之间的依赖关系尽量简单。
二、举个例子
web开发中我在Contrlller层写了一个导出文件的方法,如下所示:
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@RestController
public class ExcelExportController {
@GetMapping("/export")
public void exportExcel(HttpServletResponse response) throws IOException {
// 创建工作簿
Workbook workbook = new XSSFWorkbook();
// 创建工作表
Sheet sheet = workbook.createSheet("Sheet1");
// 创建行并设置数据
Row row = sheet.createRow(0);
Cell cell = row.createCell(0);
cell.setCellValue("Hello, Excel!");
// 设置响应头信息
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setHeader("Content-Disposition", "attachment; filename=example.xlsx");
// 将Excel数据写入响应流
workbook.write(response.getOutputStream());
workbook.close();
}
}
问题:这个方法有什么问题吗?
要说运行,是没啥问题,但是在Controller层写这么多代码似乎显得很臃肿。。。不止如此,这里创建Excel并没有填充真正的业务数据,如果再加上填充业务数据的逻辑,这个方法将会变得非常庞大。迪米特法则核心思想是降低对象之间的耦合性,使各个对象之间的依赖关系尽量简单。根据迪米特法则,一个对象应该尽量减少与其他对象之间的直接交互,而是通过中介对象来进行通信。
在这里将所有业务代码都写在Controller层违反了迪米特法则。
改造方式很简单,将代码封装到Service层,Contrller层调用Service层代码实现文件导出即可。
这里要说下MVC架构和迪米特法则的关联关系以避免产生歧义:
MVC架构并不是根据迪米特法则(最少知识原则)专门设计的,但它可以与迪米特法则的原则相辅相成。MVC架构的设计目标是将应用程序的不同方面(Model、View、Controller)分离,以实现良好的代码组织、可维护性和可扩展性。在MVC架构中,Model层负责处理数据逻辑,View层负责展示用户界面,而Controller层负责协调和管理Model和View之间的交互。
虽然MVC架构没有直接提及迪米特法则,但它有助于实现迪米特法则的原则。通过将业务逻辑、数据处理和用户界面分离到不同的层级,MVC架构降低了各个模块之间的直接依赖关系,实现了松耦合的设计。这样,每个模块只需要关注自己的职责,不需要了解其他模块的内部细节,符合迪米特法则中的"最少知识原则"。
在MVC架构中,Controller层充当了协调者的角色,它负责接收用户的请求,调用适当的服务或领域层进行业务处理,并将结果返回给View层进行展示。Controller层通过与其他模块的松散耦合,遵循了迪米特法则的原则,减少了不必要的依赖关系。
因此,尽管MVC架构本身并非直接基于迪米特法则设计,但它与迪米特法则的原则相一致,
三、最佳实践
1、被依赖的类,不管多么复杂,都尽量将逻辑封装在类的内部
2、从依赖者的角度来说,只依赖应该依赖的对象
3、切忌不要为了用而用,正确使用迪米特法则是可以让程序保证低耦合的,因为避免了与非直接的朋友通信,但是想要通信,就需要用到直接的朋友, 过分的使用迪米特原则,会产生很多这样没有必要的直接的朋友,导致系统复杂度变大,所以,在釆用迪米特法则时要进行权衡,保证系统的结构清晰
要记住,迪米特法则的核心是降低类之间的耦合,迪米特法则只是要求降低类与类之间的耦合关系,并不是要求完全没有依赖关系。
END.
参考文档:
《设计模式之禅(第2版)》《第一部分第5章 迪米特法则》
https://developer.aliyun.com/article/780978?spm=a2c6h.13262185.profile.28.1d934622ugW4cG
注意:本文归作者所有,未经作者允许,不得转载