迪米特法则
什么是迪米特法则
迪米特法则(Law of Demeter,简称 LoD)也称为最少知识原则(The Least Knowledge Principle),其核心思想是:
每个模块只应该了解那些与它关系密切的模块的有限知识。或者说,每个模块只和自己的朋友"说话",不和陌生人"说话"。
具体来说,迪米特法则包含两个核心要点:
- 不该有直接依赖关系的 类之间,不要有依赖
- 有依赖关系的类之间,尽量只依赖必要的接口
如何实现"高内聚、松耦合"
迪米特法则的主要目标是实现代码的"高内聚、松耦合":
高内聚
- 相近的功能应该放到同一个类中
- 不相近的功能不要放到同一个类中
- 功能修改时,修改点比较集中,容易维护
松耦合
- 类与类之间的依赖关系简单清晰
- 一个类的修改对其他类的影响较小
- 修改时只需要考虑有限的依赖关系
代码示例
1. 违反迪米特法则的示例
// 网络传输类不应该直接依赖特定的请求类型
public class NetworkTransporter {
public byte[] send(HtmlRequest htmlRequest) {
String address = htmlRequest.getAddress();
byte[] data = htmlRequest.getContent().getBytes();
// 发送数据...
return null;
}
}
public class HtmlDownloader {
private NetworkTransporter transporter;
public Html downloadHtml(String url) {
HtmlRequest request = new HtmlRequest(url);
byte[] rawHtml = transporter.send(request);
return new Html(rawHtml);
}
}
2. 符合迪米特法则的重构
// 网络传输类只依赖通用的参数
public class NetworkTransporter {
public byte[] send(String address, byte[] data) {
// 发送数据...
return null;
}
}
public class HtmlDownloader {
private NetworkTransporter transporter;
public Html downloadHtml(String url) {
HtmlRequest request = new HtmlRequest(url);
byte[] rawHtml = transporter.send(
request.getAddress(),
request.getContent().getBytes()
);
return new Html(rawHtml);
}
}
3. 使用接口实现功能隔离
// 定义序列化接口
public interface Serializable {
String serialize(Object object);
}
// 定义反序列化接口
public interface Deserializable {
Object deserialize(String text);
}
// 实现类同时实现两个接口
public class Serialization implements Serializable, Deserializable {
@Override
public String serialize(Object object) {
// 序列化实现...
return null;
}
@Override
public Object deserialize(String text) {
// 反序列化实现...
return null;
}
}
// 客户端代码只依赖需要的接口
public class Client1 {
private Serializable serializer;
public Client1(Serializable serializer) {
this.serializer = serializer;
}
}
public class Client2 {
private Deserializable deserializer;
public Client2(Deserializable deserializer) {
this.deserializer = deserializer;
}
}
实践建议
-
合理划分类的职责
- 避免类之间不必要的依赖
- 保持类的功能单一和内聚
-
使用接口进行隔离
- 通过接口定义有限的访问边界
- 客户端只依赖必要的接口
-
注意依赖关系
- 减少类之间的直接依赖
- 使用依赖注入等方式管理依赖
-
平衡设计
- 不要过度设计,导致代码过于复杂
- 在实际应用中灵活把握原则