【设计模式】九、面向对象设计原则之迪米特法则

178 阅读5分钟

这是我参与「掘金日新计划 · 2 月更文挑战」的第 10 天,点击查看活动详情

系列文章|源码

github.com/tyronczt/de…

定义-是什么

迪米特法则(Law of Demeter,LoD)又叫作最少知识原则(Least Knowledge Principle,LKP),产生于 1987 年美国东北大学(Northeastern University)的一个名为迪米特(Demeter)的研究项目,由伊恩·荷兰(Ian Holland)提出,被 UML 创始者之一的布奇(Booch)普及,后来又因为在经典著作《程序员修炼之道》(The Pragmatic Programmer)提及而广为人知。

迪米特法则(Law of Demeter)又叫作最少知识原则(The Least Knowledge Principle),一个类对于其他类知道的越少越好,就是说一个对象应当对其他对象有尽可能少的了解,只和朋友通信,不和陌生人说话。英文简写为:LOD。 这意味着一个对象只应该与直接的朋友通信,而不应该与陌生人通信。在编程中,一个对象只应该引用其直接关联的对象,而不应该引用其他对象的内部状态。
核心:最少依赖,尽量降低类与类之间的耦合。 只与直接的朋友通信

  • 朋友:有耦合(依赖、关联、组合、聚合)关系的对象;
  • 直接朋友:成员变量,方法参数,方法返回值中的类;

思考-为什么

迪米特法则的好处是减少了对象之间的耦合,提高了系统的可维护性和可扩展性。如果遵循迪米特法则,当修改一个对象时,不会对其他对象产生影响,因此系统更加稳定。

优点

  • 降低类之间的耦合度,提高模块的相对独立性。
  • 由于亲和度降低,从而提高了类的可复用率和系统的扩展性。

缺点

过度使用迪米特法则会使系统产生大量的中介类,从而增加系统的复杂性,使模块之间的通信效率降低。所以,在釆用迪米特法则时需要反复权衡,确保高内聚和低耦合的同时,保证系统的结构清晰。

注意事项

  • 在类的划分上,应当创建弱耦合的类,类与类之间的耦合越弱,就越有利于实现可复用的目标。
  • 在类的结构设计上,每个类都应该降低成员的访问权限。
  • 在类的设计上,只要有可能,一个类应当设计成不变的类。
  • 在对其他类的引用上,一个对象对其他类的对象的引用应该降到最低。
  • 尽量限制局部变量的有效范围,降低类的访问权限。

应用-怎么用

设计模式的门面模式(Facade)和中介模式(Mediator),都是迪米特法则的应用。

案例1:只和直接的朋友交流 --- 班长点名

需求:老师叫班长点名;

代码仓库:github.com/tyronczt/de…

public class Student {
}

// 班长
public interface IGroupLeader {
    // 清点人数
    void count(List<Student> students);
}

public class GroupLeaderImpl implements IGroupLeader {

    @Override
    public void count(List<Student> students) {
        if (null != students && students.size() > 0) {
            System.out.println("上课的学生人数是:" + students.size());
        }
    }
}

// 老师
public interface ITeacher {
    void command(IGroupLeader iGroupLeader);
}

public class TeacherImpl implements ITeacher {
    @Override
    public void command(IGroupLeader iGroupLeader) {
        // 全班同学
        List<Student> allStudent = new ArrayList<>();
        allStudent.add(new Student());
        allStudent.add(new Student());
        allStudent.add(new Student());
        allStudent.add(new Student());
        allStudent.add(new Student());
        // 班长清点人数
        iGroupLeader.count(allStudent);
    }
}

public class Client {
    public static void main(String[] args) {
        // 老师
        TeacherImpl teacher = new TeacherImpl();
        // 班长
        GroupLeaderImpl groupLeader = new GroupLeaderImpl();
        // 老师叫班长点名
        teacher.command(groupLeader);
    }
}

// 运行结果
上课的学生人数是:5

新建学生、班长、老师接口,并实现接口,实现既定需求。 此处老师实现类 TeacherImpl有两个朋友,一个是班长接口 IGroupLeader,一个是学生类 Student; 两个朋友中,班长接口是直接朋友,因为他是入参,而学生类并不是直接朋友,因为他既不是成员变量也不是方法参数更不是方法返回值中的类。所以老师类的设计不符合迪米特法则! 根据迪米特法则进行修改:

public class Student {
}

// 班长
public interface IGroupLeader {
    // 清点人数
    void count(List<Student> students);
}

public class GroupLeaderImpl implements IGroupLeader {
	private List<Student> students;

    public GroupLeaderImpl(List<Student> students) {
        this.students = students;
    }
    @Override
    public void count() {
        if (null != students && students.size() > 0) {
            System.out.println("上课的学生人数是:" + students.size());
        }
    }
}

// 老师
public interface ITeacher {
    void command(IGroupLeader iGroupLeader);
}

public class TeacherImpl implements ITeacher {
    @Override
    public void command(IGroupLeader iGroupLeader) {
        // 班长清点人数
        iGroupLeader.count();
    }
}

public class Client {
    public static void main(String[] args) {
        // 全班同学
        List<Student> allStudent = new ArrayList<>();
        allStudent.add(new Student());
        allStudent.add(new Student());
        allStudent.add(new Student());
        allStudent.add(new Student());
        allStudent.add(new Student());
        allStudent.add(new Student());

        // 老师
        TeacherImpl teacher = new TeacherImpl();
        // 班长
        GroupLeaderImpl groupLeader = new GroupLeaderImpl(allStudent);
        // 老师叫班长点名
        teacher.command(groupLeader);
    }
}

// 运行结果
上课的学生人数是:6

将老师实现类进行调整,各司其职,只与自己的直接朋友交流,这样也有效地减少了耦合;

案例2:减少对朋友的了解

如何减少对朋友的了解?如果你的朋友是个话痨加大喇叭,那就算你不主动去问他,他也会在你面前说个不停,把他所有的经历都讲给你听。所以,要减少对朋友的了解,请换一个内敛一点的朋友吧~换作在一个类中,就是尽量减少一个类对外暴露的方法。

需求: 人用咖啡机煮咖啡,煮咖啡分三步:1.加咖啡豆;2.加水;3.制作咖啡:

仓库代码地址:github.com/tyronczt/de…

// 咖啡机接口
public interface ICoffeeMachine {

    //加咖啡豆
    void addCoffeeBean();

    //加水
    void addWater();

    //制作咖啡
    void makeCoffee();
}

// 咖啡机实现类
public class CoffeeMachine implements ICoffeeMachine{
    @Override
    public void addCoffeeBean() {
        System.out.println("加咖啡豆");
    }

    @Override
    public void addWater() {
        System.out.println("加水");
    }

    @Override
    public void makeCoffee() {
        System.out.println("制作咖啡");
    }
}

// 人接口
public interface IMan {
    // 制作咖啡
    void makeCoffee();
}

// 人实现类
public class Man implements IMan {
    private ICoffeeMachine coffeeMachine;

    public Man(ICoffeeMachine coffeeMachine) {
        this.coffeeMachine = coffeeMachine;
    }

    /**
     * 制作咖啡
     */
    @Override
    public void makeCoffee() {
        coffeeMachine.addWater();
        coffeeMachine.addCoffeeBean();
        coffeeMachine.makeCoffee();
    }
}

// 客户端类
public class Client {
    public static void main(String[] args) {
        ICoffeeMachine coffeeMachine = new CoffeeMachine();

        IMan man = new Man(coffeeMachine);
        man.makeCoffee();
    }
}

// 输出结果
加水
加咖啡豆
制作咖啡

按照上述代码实现了既定的需求,CoffeeMachineMan的直接好友,但问题是 ManCoffeeMachine了解的太多了,其实人根本不关心咖啡机具体制作咖啡的过程。所以我们可以作如下优化:优化后的咖啡机类,只暴露一个 work方法,把制作咖啡的三个具体的方法 addCoffeeBeanaddWatermakeCoffee 设为私有:

// 咖啡机接口
public interface ICoffeeMachine {
    //咖啡机工作
    void work();
}

// 咖啡机实现类
public class CoffeeMachine implements ICoffeeMachine{
    //加咖啡豆
    public void addCoffeeBean() {
        System.out.println("加咖啡豆");
    }

    //加水
    public void addWater() {
        System.out.println("加水");
    }

    //制作咖啡
    public void makeCoffee() {
        System.out.println("制作咖啡");
    }

    @Override
    public void work() {
        addWater();
        addCoffeeBean();
        makeCoffee();
    }
}

// 人接口
public interface IMan {
    // 制作咖啡
    void makeCoffee();
}

// 人实现类
public class Man implements IMan {
    private ICoffeeMachine coffeeMachine;

    public Man(ICoffeeMachine coffeeMachine) {
        this.coffeeMachine = coffeeMachine;
    }

    /**
     * 制作咖啡
     */
    @Override
    public void makeCoffee() {
        coffeeMachine.work();
    }
}

// 客户端类
public class Client {
    public static void main(String[] args) {
        ICoffeeMachine coffeeMachine = new CoffeeMachine();

        IMan man = new Man(coffeeMachine);
        man.makeCoffee();
    }
}

// 输出结果
加水
加咖啡豆
制作咖啡

这样修改后,通过减少 CoffeeMachine对外暴露的方法,减少 ManCoffeeMachine的了解,从而降低了它们之间的耦合。 在实践中,只要做到只和直接的朋友交流和减少对朋友的了解,就能满足迪米特法则。

参考

迪米特法则是什么? 为什么叫做最少知识原则?【设计模式系列4】_哔哩哔哩_bilibili

设计模式六大原则(五)----迪米特法则 - 掘金