迪米特(LoD)[又称最少知识(LKP)]原则

迪米特(LoD)[又称最少知识(LKP)]原则

介绍

迪米特原则迪米特原则 (Law of Demeter, LoD)又称最少知识原则(Least Knowledge Principle, LKP),是指一个软件实体应当尽可能少地与其他实体发生相互作用,即一个对象应该对其他对象有尽可能少的了解。这个原则的目的在于降低类与类之间的耦合度,提高系统的可维护性和可扩展性。

原则

迪米特原则的主要目标是降低系统的耦合度,使得各个模块之间相互独立,修改一个模块不会影响到其他模块。具体实现方式包括:

  1. 不要依赖于具体实现细节,而是应该尽可能地只调用公共接口的方法(比如中间类的抽象方法)。
  2. 类的依赖关系尽量减少,保持简单和独立,降低耦合。

迪米特原则的原则陈述为“只与你的直接朋友交谈,不跟陌生人说话”,这里的“直接朋友”指的是以下几个方面:

  1. 当前对象本身(this)
  2. 以参数形式传入方法中的对象
  3. 当前对象的实例变量
  4. 当前对象所创建的对象
门面(外观)模式

如果一个子系统内部的对象构成自己的朋友圈,而子系统外部 对象都属于陌生人的话,那么子系统外部的对象与内部的对象就不应当直接通信,而应当通过一个双方都认可的朋友,也就是门面对象进行通信,这就导致了门面模式。

中介者模式

这里一些对象形成一个中等规模的朋友圈,而圈内很多的对象都有排列组合般的交互作用。这时,可以通过创造一个大家共有的朋友对象,然后大家都通过这个朋友对象发生相互作用,而将相互之间的直接相互作用省略掉,这就导致了中介者模式。

正反案例

反例

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 的内部状态,这样就导致了 ClientShoppingCart 之间产生了直接的耦合,修改 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类直接交互,而不与ItemShoppingCart类直接交互。CustomerOrder作为其成员变量,并通过addOrder方法向其添加订单。Order类只与Item类直接交互,将Item作为其成员变量,并通过addItem方法向其添加商品。ShoppingCart类只与Item类直接交互,将Item作为其成员变量,并通过addItem方法向其添加商品,并通过checkout方法返回一个Order对象。

这种设计方式使得每个类都只与其直接朋友进行交互,从而避免了类之间的紧密耦合。这种松散耦合的设计使得代码更加灵活和易于维护。

特点

  1. 迪米特原则可以有效地降低系统的耦合度,提高系统的可维护性和可扩展性。
  2. 迪米特原则可以促进类的重用,因为一个类的变化对其他类的影响会减少。

注意事项(*重点)

  1. 迪米特原则虽然可以降低系统的耦合度,但也可能导致系统的复杂度增加,因为需要引入中间类来进行通信。一个准则:只要跳转不超过两次都是可以忍受的,反之则要考虑重构;
  2. 迪米特原则需要谨慎地应用,特别是在大型系统中,需要平衡系统的复杂度和耦合度。
  3. LoD法则要求解耦,但解耦是有限度的,原则仅供参考,严格执行就是“过犹不及”。

此外,迪米特原则还有以下注意事项:

  1. 避免在类中暴露过多的公共方法和属性,这可能会导致与其他类之间的耦合度增加。
  2. 尽量使用接口或抽象类来实现与其他类之间的通信,这可以减少系统的耦合度。
  3. 在设计系统时,要考虑到系统的可扩展性,避免修改现有类的同时影响其他类。
  4. 在实际应用迪米特原则时,需要考虑具体的业务场景和需求,有时候需要放宽对迪米特原则的要求,以便更好地满足业务需求。
  5. 在实践中,迪米特原则也需要与其他设计原则结合使用,如依赖倒置原则、单一职责原则等,以便更好地提高系统的可维护性和可扩展性。