设计模式-迪米特法则

代码世界中类间的耦合关系会直接影响代码可复用性、可读性、可扩展性等。这种耦合关系就如同人之间朋友关系一样,志不同道不合不应相于谋,否则最终只会落得个互相伤害的下场。代码组织时也应如此,应按照一定的原则处理好类之间的关系,否则就会导致恶性耦合只会使得项目代码越写越烂,难以维护。这个原则就是迪米特原则。

一、迪米特原则概念

迪米特法则(Law of Demeter, LOD),也称之为最少知识原则,指的一个类应该对自己需要耦合或调用的类知道的最少。相应一些英文定义:

  • Each unit should have only limited knowledge about other units: only units “closely” related to the current unit.
    (每个单元对于其他的单元只能拥有有限的知识:只是与当前单元紧密联系的单元)
  • Each unit should only talk to its friends; don’t talk to strangers. (每个单元只能和它的朋友交谈:不能和陌生单元交谈)
  • Only talk to your immediate friends. (只和自己直接的朋友交谈) (来自维基百科)

不论是中文定义还是英文定义,我想根据自己的理解换一种说法,即“一个类应该与自己应该依赖的类产生依赖,而不应该与自己不该依赖的类产生依赖”。这样的说法似乎更容易被理解一些,那继而来的问题就是,哪些是这个类应该依赖的?哪些是不应该以来的呢?这些应该依赖的类在英文定义中称之为"朋友类"。
在“朋友类”的定义和判断上,一些参考资料给出的是出现在类成员变量、类方法的输入输出参数中的类称为朋友类,而出现在方法体内部的类不属于朋友类。个人对这个定义不是很赞同,这个定义可以用来评价现有代码是否符合迪米特原则时判断朋友类,但是不适用代码设计阶段。即,我们在设计代码时,哪些类应该是朋友类这个定义不能很好的给我们回答。我个人认为,在单一职责原则基础上,应该强耦合的朋友类之间所对应的业务逻辑也是强耦合的。所有原则、代码设计均是以业务逻辑为基础,只要这样才能更好的服务于业务。所以,判断类之间是否为朋友类,就看类所负责的业务之间是否强耦合,对于非强耦合业务类不要产生依赖,这也满足了类对自己调用的类知道的最少【业务都强耦合,代码类上必须依赖呀,没法子不依赖哈】。
举个栗子,在RPC服务中大概可以有接口层、业务层、数据层等,根据业务逻辑的耦合关系来看,接口层会依赖业务层提供业务能力,返回业务数据,但是不能够依赖数据层直接获取存储数据,这就不满足迪米特原则了。再举一个其他资料的案例,公司类(Company)应该仅依赖部门(Department),不应该直接依赖员工(Employee)。另外,我们常熟知的DO、BO、DTO等这种分层规范也是应用迪米特原则的一种体现。

二、应用实践

前面已经基本说清楚迪米特法则的基本概念了,这个章节将通过一个非常简单的案例演示下迪米特法则的应用。我们平常在零碎的时间里,喜欢看一些书籍,一般都是电子书,现在我们看书的操作是这样的:唤醒手机,打开阅读软件,选择书籍,然后阅读。总共 3 个步骤,涉及了 3 样东西:手机、软件、书籍。现在使用代码来描述下这个过程。

2.1 未应用迪米特法则

在未应用迪米特法则时,我们可能会写出下面这样的代码:
① 书籍

public class Book {
    private String title;

    public Book(String title) {
        this.title = title;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }
}

② 软件应用

public class App {
    public void read(Book book) {
        System.out.println(book.getTitle());
    }
}

③ Phone

public class Phone {
    App app = new App();
    Book book = new Book("设计模式");
    public void readBook() {
        app.read(book);
    }
}

④ 客户端

public class Client {
    public static void main(String[] args) {
        Phone phone = new Phone();
        phone.readBook();
    }
}

在这里插入图片描述
从类图上来看,首先电子书应用是由电子书籍构成,因此APP与Book的关系不应该是依赖关系,更应该是强依赖的组合关系。再者,手机和电子书之前不应该存在耦合(依赖)关系,没有电子书,手机一样能够作为他用。如果在之后的业务逻辑中,如果书籍业务扩增,如添加类型、版权等都会影响到本不该影响的手机Phone业务。由此可以看出,Phone依赖了不该依赖的类Book,这种依赖业务的变化十分不可控,对依赖方的影响也不可控,继而导致系统稳定性、可扩展性降低。

2.2 应用迪米特法则

基于以上问题,如果应用迪米特法则,那么Phone类应该就只能够依赖App类,而App类也只依赖Book类。代码修改如下:
① 书籍
书籍和$2.1节相同,不重复补充。
② 软件应用

public class App {
    private Book book;

    public App(Book book) {
        this.book = book;
    }

    public void read() {
        System.out.println(book.getTitle());
    }
}

③ Phone

public class Phone {
    private App app;

    public Phone(App app ) {
        this.app = app;
    }

    public void readBook() {
        app.read();
    }
}

④ 客户端

public class Client {
    public static void main(String[] args) {
        App app = new App(new Book("设计模式"));
        Phone phone = new Phone(app);
        phone.readBook();
    }
}

在这里插入图片描述
这样的代码类耦合关系看起来就十分舒服了,书籍相关业务变动只可能会影响其和App之间的关系,即便重构也不会影响到Phone相关业务,明显提高了代码稳定性和扩展性。
这里可能有人会有疑问,前后两个方案对比,那Client类不是依赖的更多了吗?本来只需要依赖一个Phone类,然而改造后依赖了三个类。没错,确实Client依赖更多,但是这是符合业务逻辑的,“唤醒手机,打开阅读软件,选择书籍”,作为执行者执行这一套动作,肯定需要知道什么书籍(Book具体对象),什么应用(App对象),什么手机(Phone对象),因此这种依赖是符合业务逻辑的。此外,之前文章中也说过,Client不属于设计模式考虑范围之内,Client中是处理类对象行为过程,非代码设计讨论范围之内的。

【参考资料】

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值