SOLID之LOD

简介: 迪米特法则 Law of DemeterLOD最早出现于 1987年,由美国东北大学的伊恩·霍兰德(Ian Holland)提出。也叫做“最少知识原则”。Each unit should have only limited knowledge about other units: only units “closely” related to the current unit. Or: Each unit should only talk to its friends; Don’t talk to strangers.

迪米特法则 Law of Demeter

LOD

最早出现于 1987年,由美国东北大学的伊恩·霍兰德(Ian Holland)提出。也叫做“最少知识原则”。

Each unit should have only limited knowledge about other units: only units “closely” related to the current unit. Or: Each unit should only talk to its friends; Don’t talk to strangers.

高内聚低耦合

我们设计的目标就是高内聚低耦合,是否高内聚关键在于封装性

看段代码:


String name = book.getAuthor().getName();

可以推断出这行代码是获得一部作品作者的名字。

正常都能写出这段代码,因为在同一模块下大多数代码都是同一个人写的,所以Book和Author两个类都清楚里面的细节。但如果是不同人写的呢?至少得问下别人,作者名字在哪个类?或者翻阅类中实现细节。

从接口设计角度,外部只能知道Book对象,而Book中关联的对象细节是不需要知道的,这正是封装性的体现,降低认知负载。

而这也正好违背了LOD原则。

不该有直接依赖关系的类之间,不要有依赖;

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

迪米特法则是希望减少类之间的耦合,让类越独立越好。LOD功效正是指导我们能设计出高内聚、

所以上面的代码得改动一下Book类

class Book {
   public String getAuthorName(){
        return author.getName();
   }
}

谁是朋友?

既然LOD指出只跟自己的朋友交流,那得搞清楚谁是朋友?

对于一个类C,它拥有的一个方法称为M。方法M发送信息的目标对象必须为:

M方法的参数对象,还有对自身的引用(被方法M创建的实例对象、被方法M调用的方法生成的实例对象、全局对象)。类C的实例对象。

把上面的情况拆解开:

1.本身(this,self)2.方法M的参数对象(parameter)3.类C内实例变量引用的对象(instance variable)4.被方法M创建的对象 或 方法M调用的方法创建的对象5.如果实例变量是集合,集合中的对象(collection,aggregration)

示例

在正统的SOLID原则中,都不包含LOD原则,但它确实能很好地指导我们如何封装,达到高内聚的目标。

下面看两个常见到的问题作为示例,进一步理解这个原则。

流式API

上面提到的代码


String name = book.getAuthor().getName();

开始没有意识到违背LOD,但至少会有null情况,所以会改成


if(book != null) {
    Author author = book.getAuthor();
    if(author != null) {
        name = author.getName();
    }
}

如果有好几层,就会嵌套很多层。

但从JDK8之后,有了Optional,代码就变成了


name = Optional.ofNullable(book).map(Book::getAuthor).map(Author::getName);

虽然这样子很简洁地解决了null的问题,但是不是一样违背LOD呢?

延伸一下,是不是链式调用都违反了LOD,毕竟形式上的确很像,一个连接符接着一个连接符。

Report report = new ReportBuilder()
  .withBorder(1)
  .withBorderColor(Color.black)
  .withMargin(3)
  .withTitle("Law of Demeter Report")
  .build();

如我们常见的builder方式,每次返回的都是self。根据“谁是朋友”的定义是允许的。这是简单的示例,但像上面的Optional呢?

每次Optional.map之后,其实并不是最初的Optional对象了,Stream也一样,每次返回的都是一个新的Stream对象。

怎么解释这种问题呢?这个问题其实有很多人提出疑问,大概有这么几种回答:

1、Stream shows that you are using it as intended by the Java language designers[1]

2、any standard library objects should be exempt from the Law of Demeter

3、All these laws/principles are rarely absolute and more guidelines than dogmas. In this case it might be about balancing LoD vs. KISS.[2]

所以结论是什么呢?

我想重要的还是共识,团队内的共识。就像在JDK7~JDK8的过渡期,团队中有先行者引入了stream新特性,很多成员看得头大,制定了团队公约,不要使用stream。但现在fluent interface已经很亲民了,自然团队对stream有更高的认同,有了共识,谁还会抵制它。

RESTful API

再扩展一下,看到很长的RESTful风格的url,这是不是也有违背LOD原则的嫌疑?

在RESTful API场景下,实体只有客户端和API提供者,API内部的实现细节也被API层屏蔽了,所以并不会违背LOD原则。

总结

LOD原则指导我们更好地封装,达到内聚性目标。但通过fluent interface,也认识到任何原则都是指导性,不可教条。考虑原则的同时,还得考虑ROI,而且还得与其它原则权衡,达到最适合的设计。

References

[1] Stream shows that you are using it as intended by the Java language designers: https://stackoverflow.com/questions/45491555/can-multiple-operations-with-streaming-break-the-law-of-demeter

[2] All these laws/principles are rarely absolute and more guidelines than dogmas. In this case it might be about balancing LoD vs. KISS.: https://stackoverflow.com/questions/47347068/optional-monad-and-the-law-of-demeter-in-java

[3] The Genius of the Law of Demeter: https://dzone.com/articles/the-genius-of-the-law-of-demeter

[4] LOD原则的高明之处: https://www.zybuluo.com/XingdingCAO/note/913912

目录
相关文章
css3中的圆角border-radius
css3中的圆角border-radius
|
2月前
|
关系型数据库 开发者
|
5月前
|
前端开发
CSS中的边框属性:border、border-width、border-color、border-style 详解
CSS中的边框属性:border、border-width、border-color、border-style 详解
429 44
|
3月前
|
关系型数据库 测试技术
|
6月前
|
前端开发
如何将一个盒子模型变成border-box?
如何将一个盒子模型变成border-box?
39 0
|
前端开发
css:border-radius绘制边框圆角-全圆和椭圆
css:border-radius绘制边框圆角-全圆和椭圆
140 0
css:border-radius绘制边框圆角-全圆和椭圆
|
前端开发 开发者
怪异盒模型border-box真的“一无是处”吗?
虽然w3c制定了标准盒模型,但在 `box-sizing` 属性中却还是保留了怪异盒模型,可能就是希望开发者在不同的场景中适当地运用,前提是如何理解它。
|
算法 数据可视化 C++
OpenCASCADE Outline
OpenCASCADE Outline eryar@163.com      有网友反映blog中关于OpenCASCADE的文章比较杂乱,不太好找,最好能提供一个大纲,这样方便查找。于是决定将这些学习时写的文章整理下,方便对OpenCASCADE的学习理解。
3063 0
|
设计模式 架构师 Java
SOLID总结
之前已经把SOLID的每人原则都阐述过一遍,此篇主要是从全局角度复述一下SOLID,对于细节概念再做少许补充 SOLID原则的历史已经很悠久,早在20世纪80年代末期,都已经开始逐渐成型了 通常来讲,想构建一个好的软件系统,应该从写整洁的代码开始做起。毕竟如果建筑的砖头质量不佳,那么架构所能起到的作用也会很有限。反之亦然,如果建筑的架构设计不佳,那么其所用砖头质量再好也没用
323 0
SOLID总结
|
消息中间件 架构师 前端开发
SOLID之DIP
依赖反转原则 DIP, Dependency inversion principle 1. 高层模块不应该依赖于低层模块。二者都应该依赖于抽象 2. 抽象不应该依赖于细节。细节应该依赖于抽象
127 0
SOLID之DIP