05.迪米特原则 (LOD)

LOD全称

LOD, Law of Demeter, 迪米特原则 or LKP, Least Knowledge Principle, 最少知识原则

定义

一个对象应该对其他对象有最少的了解。一个类应该对自己需要耦合或调用的类知道的最少,类的内部如何实现与调用者或者依赖者没有关系,调用者或者依赖者只需知道它需要的方法即可。

只与直接的朋友发生通信。

优点

  1. 降低类之间的耦合度,提高了模块的相对独立性
  2. 耦合度降低,从而提高了类的可重用率和系统的扩展性

缺点

  • 过度使用迪米特原则,会产生大量的中介类,导致系统的复杂度提高。在釆用迪米特法则时需要反复权衡,确保高内聚和低耦合的同时,保证系统的结构清晰

实现

  • 问题由来: 类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大

  • 解决方案: 尽量降低类与类之间的耦合

  1. 需强调

    • 从依赖者的角度来说,只依赖应该依赖的对象。
    • 从被依赖者的角度说,只暴露应该暴露的方法。
  2. 需注意

    • 在类的划分上,应该创建弱耦合的类。类与类之间的耦合越弱,就越有利于实现可复用的目标。
    • 在类的结构设计上,尽量降低类成员的访问权限。
    • 在类的设计上,优先考虑将一个类设置成不变类。
    • 在对其他类的引用上,将引用其他对象的次数降到最低。
    • 不暴露类的属性成员,而应该提供相应的访问器(set 和 get 方法)。
    • 谨慎使用序列化(Serializable)功能。

实例

例子:有一个集团公司,下属单位有分公司和直属部门,现在要求打印出所有下属单位的员工ID。先来看一下违反迪米特法则的设计。

// 总公司员工
class Employee {
    private String id;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }
}

// 分公司员工
class SubEmployee {
    private String id;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }
}

// 子公司管理
class SubCompanyManager {
    public List<SubEmployee> getAllEmployee() {
        List<SubEmployee> list = new ArrayList<>();
        for (int i = 1; i < 100; i++) {
            SubEmployee subEmployee = new SubEmployee();
            // 给分公司人员顺序分配一个ID
            subEmployee.setId("分公司" + i);
            list.add(subEmployee);
        }
        return list;
    }
}

// 总公司管理
class CompanyManager {
    public List<Employee> getAllEmployee() {
        List<Employee> list = new ArrayList<>();
        for (int i = 1; i < 30; i++) {
            Employee employee = new Employee();
            // 给总公司人员顺序分配一个ID
            employee.setId("总公司" + i);
            list.add(employee);
        }
        return list;
    }

    public void printAllEmployee(SubCompanyManager subCompanyManager) {
        // 分公司员工
        List<SubEmployee> subEmployeeList = subCompanyManager.getAllEmployee();
        for (SubEmployee subEmployee : subEmployeeList) {
            System.out.println(subEmployee.getId());
        }

        // 总公司员工
        List<Employee> employeeList = getAllEmployee();
        for (Employee employee : employeeList) {
            System.out.println(employee.getId());
        }
    }
}

public class LODClient {
    public static void main(String[] args) {
        CompanyManager companyManager = new CompanyManager();
        companyManager.printAllEmployee(new SubCompanyManager());
    }
}

问题出现在CompanyManager类,根据迪米特法则,只与直接的朋友发生通信。而SubEmployee类并不是CompanyManager类的直接朋友(以局部变量出现的耦合不属于直接朋友),从逻辑上讲总公司只与他的分公司耦合就行了,与分公司的员工并没有任何联系,这样设计显然是增加了不必要的耦合。按照迪米特法则,应该避免类中出现这样非直接朋友关系的耦合。修改后的代码如下

// 总公司员工
class Employee {
    private String id;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }
}

// 分公司员工
class SubEmployee {
    private String id;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }
}

// 子公司管理
class SubCompanyManager {
    public List<SubEmployee> getAllEmployee() {
        List<SubEmployee> list = new ArrayList<>();
        for (int i = 1; i < 100; i++) {
            SubEmployee subEmployee = new SubEmployee();
            // 给分公司人员顺序分配一个ID
            subEmployee.setId("分公司" + i);
            list.add(subEmployee);
        }
        return list;
    }

    public void printSubCompany() {
        List<SubEmployee> subEmployeeList = this.getAllEmployee();
        for (SubEmployee subEmployee : subEmployeeList) {
            System.out.println(subEmployee.getId());
        }
    }
}

// 总公司管理
class CompanyManager {
    public List<Employee> getAllEmployee() {
        List<Employee> list = new ArrayList<>();
        for (int i = 1; i < 30; i++) {
            Employee employee = new Employee();
            // 给总公司人员顺序分配一个ID
            employee.setId("总公司" + i);
            list.add(employee);
        }
        return list;
    }

    public void printAllEmployee(SubCompanyManager subCompanyManager) {
        // 分公司员工
        subCompanyManager.printSubCompany();

        // 总公司员工
        List<Employee> employeeList = getAllEmployee();
        for (Employee employee : employeeList) {
            System.out.println(employee.getId());
        }
    }
}

public class LODClient {
    public static void main(String[] args) {
        CompanyManager companyManager = new CompanyManager();
        companyManager.printAllEmployee(new SubCompanyManager());
    }
}

修改后,为分公司增加了打印人员ID的方法,总公司直接调用来打印,从而避免了与分公司的员工发生耦合。

总结

  1. 迪米特法则的初衷是降低类之间的耦合,由于每个类都减少了不必要的依赖,因此的确可以降低耦合关系。
  2. 过分的使用迪米特原则,会产生大量这样的中介和传递类,导致系统复杂度变大。所以在采用迪米特法则时要反复权衡,既做到结构清晰,又要高内聚低耦合。
原文地址:https://www.cnblogs.com/lwcode6/p/13954954.html