介绍
迪米特原则迪米特原则 (Law of Demeter, LoD)又称最少知识原则(Least Knowledge Principle, LKP),是指一个软件实体应当尽可能少地与其他实体发生相互作用,即一个对象应该对其他对象有尽可能少的了解。这个原则的目的在于降低类与类之间的耦合度,提高系统的可维护性和可扩展性。
原则
迪米特原则的主要目标是降低系统的耦合度,使得各个模块之间相互独立,修改一个模块不会影响到其他模块。具体实现方式包括:
- 不要依赖于具体实现细节,而是应该尽可能地只调用公共接口的方法(比如中间类的抽象方法)。
- 类的依赖关系尽量减少,保持简单和独立,降低耦合。
迪米特原则的原则陈述为“只与你的直接朋友交谈,不跟陌生人说话”,这里的“直接朋友”指的是以下几个方面:
- 当前对象本身(this)
- 以参数形式传入方法中的对象
- 当前对象的实例变量
- 当前对象所创建的对象
门面(外观)模式
如果一个子系统内部的对象构成自己的朋友圈,而子系统外部 对象都属于陌生人的话,那么子系统外部的对象与内部的对象就不应当直接通信,而应当通过一个双方都认可的朋友,也就是门面对象进行通信,这就导致了门面模式。
中介者模式
这里一些对象形成一个中等规模的朋友圈,而圈内很多的对象都有排列组合般的交互作用。这时,可以通过创造一个大家共有的朋友对象,然后大家都通过这个朋友对象发生相互作用,而将相互之间的直接相互作用省略掉,这就导致了中介者模式。
正反案例
反例
public class Order {
private List<Item> itemList;
public void addItem(Item item) {
itemList.add(item);
}
public List<Item> getItemList() {
return itemList;
}
public double getTotalPrice() {
double totalPrice = 0;
for (Item item : itemList) {
totalPrice += item.getPrice();
}
return totalPrice;
}
}
public class Item {
private String name;
private double price;
public Item(String name, double price) {
this.name = name;
this.price = price;
}
public double getPrice() {
return price;
}
}
public class ShoppingCart {
private List<Item> itemList;
public void addItem(Item item) {
itemList.add(item);
}
public List<Item> getItemList() {
return itemList;
}
public double getTotalPrice() {
double totalPrice = 0;
for (Item item : itemList) {
totalPrice += item.getPrice();
}
return totalPrice;
}
}
public class Client {
public static void main(String[] args) {
Order order = new Order();
ShoppingCart cart = new ShoppingCart();
for (Item item : cart.getItemList()) {
order.addItem(item);
}
System.out.println("Total price: " + order.getTotalPrice());
}
}
在这个例子中,Client
直接访问了 ShoppingCart
的内部状态,这样就导致了 Client
和 ShoppingCart
之间产生了直接的耦合,修改 ShoppingCart
的实现细节就会影响到 Client
的代码。
正例
以下是一个符合迪米特原则的Java示例,其中一个类只与其它类的直接朋友(即成员变量,方法参数,局部变量等)进行交互:
public class Customer {
private String name;
private List<Order> orders;
public Customer(String name) {
this.name = name;
orders = new ArrayList<>();
}
public void addOrder(Order order) {
orders.add(order);
}
public void printOrders() {
for (Order order : orders) {
System.out.println(order);
}
}
}
public class Order {
private int id;
private List<Item> items;
public Order(int id) {
this.id = id;
items = new ArrayList<>();
}
public void addItem(Item item) {
items.add(item);
}
public String toString() {
return "Order #" + id;
}
}
public class Item {
private String name;
private double price;
public Item(String name, double price) {
this.name = name;
this.price = price;
}
public String toString() {
return name + ": $" + price;
}
}
public class ShoppingCart {
private List<Item> items;
public ShoppingCart() {
items = new ArrayList<>();
}
public void addItem(Item item) {
items.add(item);
}
public Order checkout() {
Order order = new Order(1);
for (Item item : items) {
order.addItem(item);
}
return order;
}
}
在这个示例中,Customer
只与Order
类直接交互,而不与Item
或ShoppingCart
类直接交互。Customer
将Order
作为其成员变量,并通过addOrder
方法向其添加订单。Order
类只与Item
类直接交互,将Item
作为其成员变量,并通过addItem
方法向其添加商品。ShoppingCart
类只与Item
类直接交互,将Item
作为其成员变量,并通过addItem
方法向其添加商品,并通过checkout
方法返回一个Order
对象。
这种设计方式使得每个类都只与其直接朋友进行交互,从而避免了类之间的紧密耦合。这种松散耦合的设计使得代码更加灵活和易于维护。
特点
- 迪米特原则可以有效地降低系统的耦合度,提高系统的可维护性和可扩展性。
- 迪米特原则可以促进类的重用,因为一个类的变化对其他类的影响会减少。
注意事项(*
重点)
- 迪米特原则虽然可以降低系统的耦合度,但也可能导致系统的复杂度增加,因为需要引入中间类来进行通信。一个准则:只要跳转不超过两次都是可以忍受的,反之则要考虑重构;
- 迪米特原则需要谨慎地应用,特别是在大型系统中,需要平衡系统的复杂度和耦合度。
- LoD法则要求解耦,但解耦是有限度的,原则仅供参考,严格执行就是“过犹不及”。
此外,迪米特原则还有以下注意事项:
- 避免在类中暴露过多的公共方法和属性,这可能会导致与其他类之间的耦合度增加。
- 尽量使用接口或抽象类来实现与其他类之间的通信,这可以减少系统的耦合度。
- 在设计系统时,要考虑到系统的可扩展性,避免修改现有类的同时影响其他类。
- 在实际应用迪米特原则时,需要考虑具体的业务场景和需求,有时候需要放宽对迪米特原则的要求,以便更好地满足业务需求。
- 在实践中,迪米特原则也需要与其他设计原则结合使用,如依赖倒置原则、单一职责原则等,以便更好地提高系统的可维护性和可扩展性。