迪米特原则,是一个很常见的原则,不像是SOLID KISS DRY那样人尽皆知,但是可以高效的提高代码的可读性和可维护性

那么,什么是迪米特原则,就是今天的话题

1.什么是高内聚,低耦合

一个重要的设计思想,能够有效的提高代码的可维护性和可读性,缩小功能改动导致的代码改动范围

很多的设计原则都是为了代码的高内聚 松耦合去实现的,比如单一职责原则,基于接口而非实现编程等

实际上,高内聚,松耦合是一种通用的设计思想,知道不同粒度的代码的设计开发

那么,什么是高内聚呢?

就是,功能相近,就应该放到一个类中,不相近的功能不要放到一个类中,修改起来集中,提高了可维护性,之前说的单一职责原则就是实现高内聚有效设计原则

那么,什么是松耦合呢?

就是类和类之间的关系,要清晰简单,避免过低的交互,在修改一个类的时候,尽量避免另一个类的修改,也就是依赖反转,接口隔离原则,基于接口而非实现编程,里氏替换原则等,都是去依赖于抽象的规范或者接口,从而提高代码的松耦合特性

高内聚,低耦合的具体实现,可以如下图

如果足够高内聚,低耦合,那么就会想左边图一样,一个类的修改,不会影响到其他类

但是耦合高了,就会像右图一样,一个类的修改,影响到了其他类,牵一发而动全身

2.迪米特法则的详解

英文翻译为 Law of Demter,名字很抽象,于是其还有一个额外信达雅的名字,叫最小知识原则

含义为:每个模块Unit,只了解那些和它关系密切的模块的有限知识

或者说,不该有依赖关系的类之间,不能有依赖关系,有依赖关系之间的类,尽量只依赖必要的接口

这个可以分为两个部分理解

没有必要依赖的类之间,不要有依赖

关于这一点,我们拿一个代码实现来说明

public class NetworkTransporter {

// 省略属性和其他方法…

public Byte[] send(HtmlRequest htmlRequest) {

//…

}

}

public class HtmlDownloader {

private NetworkTransporter transporter;//通过构造函数或IOC注入

public Html downloadHtml(String url) {

Byte[] rawHtml = transporter.send(new HtmlRequest(url));

return new Html(rawHtml);

}

}

public class Document {

private Html html;

private String url;

public Document(String url) {

this.url = url;

HtmlDownloader downloader = new HtmlDownloader();

this.html = downloader.downloadHtml(url);

}

//…

}

上面有三个类,三个类彼此有所依赖,首先看NetwordTransporter类,是一个发送信息的类,但是传入的参数是一个htmlRequest,对于这种可以复用的类,我们希望其不要依赖与具体的对象,将其变得更加通用性,我们可以将HtmlRequest对象,换为其组成部分,也就是其中的address和content对象,只传递这两个对象,可以提高复用性,符合迪米特原则

修改后的代码如下

public class NetworkTransporter {

// 省略属性和其他方法…

public Byte[] send(String address, Byte[] data) {

//…

}

}

再来看HtmlDownloader类

这个类,符合迪米特原则,只需要按照NetworkTransporter的修改进行修改即可

再来看最后一个Document类,这个类的构造函数中,我们进行了生成HtmlDownloader对象,不符合基于接口而非实现原则,可以考虑使用依赖注入

其次downloadHtml太过于耗时,毕竟是下载一个html,不应该放到构造函数中,而且也没有必要去依赖HtmlDownloader类,可以考虑加入一个工厂类,利用工厂类来进行下载,虽然没有提高总体时间,但是避免了其在初始化函数中过慢的问题

public class Document {

private Html html;

private String url;

public Document(String url, Html html) {

this.html = html;

this.url = url;

}

//…

}

// 通过一个工厂方法来创建Document

public class DocumentFactory {

private HtmlDownloader downloader;

public DocumentFactory(HtmlDownloader downloader) {

this.downloader = downloader;

}

public Document createDocument(String url) {

Html html = downloader.downloadHtml(url);

return new Document(url, html);

}

}

有依赖关系之间的类,尽量只依赖必要的接口

如何理解这个后半段呢?很简单,利用一个之前的类来进行描述

Serialization来进行负责对象的序列化和反序列化

public class Serialization {

public String serialize(Object object) {

String serializedResult = …;

//…

return serializedResult;

}

public Object deserialize(String str) {

Object deserializedResult = …;

//…

return deserializedResult;

}

}

这个类的使用来说,有一种情况,就是有些类只会进行序列化,而有的类只会进行反序列化

那么,如果按照迪米特原则来说,我们应该讲Serialization类拆为两个粒度更小的类,一个负责序列化,一个负责反序列化

public class Serializer {

public String serialize(Object object) {

String serializedResult = …;

return serializedResult;

}

}

public class Deserializer {

public Object deserialize(String str) {

Object deserializedResult = …;

return deserializedResult;

}

}

那么按照单一职责原则来说,这是不符合的,做的有点过了,导致聚合性降低了

那么,如何去做一个既符合迪米特法则,有不破坏内聚原则的实现呢?

那么就可以按照接口案例原则,进行拆分,拆分为两个接口

public interface Serializable {

String serialize(Object object);

}

public interface Deserializable {

Object deserialize(String text);

}

public class Serialization implements Serializable, Deserializable {

@Override

public String serialize(Object object) {

String serializedResult = …;

return serializedResult;

}

@Override

public Object deserialize(String str) {

Object deserializedResult = …;

return deserializedResult;

}

}

public class DemoClass_1 {

private Serializable serializer;

public Demo(Serializable serializer) {

this.serializer = serializer;

}

//…

}

public class DemoClass_2 {

private Deserializable deserializer;

public Demo(Deserializable deserializer) {

this.deserializer = deserializer;

}

//…

}

那么,这就既体现了迪米特法则,又没有降低内聚性

对于这个实现的思考延伸

如果只是上面的一个类中包含两个操作,有必要拆为两个接口吗,是否有点过度设计了呢?

只是为应用设计原则而应用设计原则,是否有点过于了呢?

但是,如果之后可能出现,为序列化,反序列化提供更多的需求,那么拆分就有必要了

最后,本章重点:

1.如何理解高内聚,低耦合

这是一种指导思想,能够有效的提高代码可读性和可维护性

高内聚用于设计一个类,低耦合用于阐述类和类之间的依赖关系

功能相近的类放到一个类中,不相近的功能不要放到一个类中,

而且,类和类之间的依赖关系简单清晰,做到类的改动不会导致其他类的改动

2.如何理解迪米特法则

不该有依赖关系的类,不要有依赖,有依赖关系的类,只依赖必要的接口

让类之间更加独立,每个类都少接触其他部分,减少类之间的改动

发表评论

邮箱地址不会被公开。 必填项已用*标注