当前位置:   article > 正文

迪米特法则-最少知识原则_迪米特选择

迪米特选择

我们写代码时经常希望能应用面向对象的一些原则,希望我们的代码能合适的封装、松耦合、健壮和复用,也希望我们的应用可扩展和可维护。通常这些原则比较抽象难以直接应用,都是根据经验和直觉来应用他们。现在有一个法则可以帮我们做选择,这个法则是:迪米特法则(The Law of Demeter),简称LOD。它是面向对象编程中一个比较简单的一个法则,使用它能保障我们代码和应用满足上面提到一些抽象原则。

迪米特法则的定义

定义是:只与你的直接朋友交谈。定义是不是很简单,但是在面向对象编程中它意味着什么呢?在编程中,与一个对象交谈意思是调用一个对象的方法。根据迪米特法则,对象O的方法M 应该只能调用下面的方法:

  • 对象O自己的方法
  • 方法M 的参数的方法
  • 对象O 成员变量的方法
  • 方法M 里的临时变量的方法
  • 静态字段的方法

关于直接朋友有个图例,如下:
在这里插入图片描述
法则告诉我们不需要知道我们接收对象的内部实现细节,我们只需要关心方法的业务逻辑和一些其他对象被告知的信息,不需要主动询问或探索其他对象的内部实现细节。来看一个例子,我们有一个包含员工对象的集合,想从中获取员工的街区信息,代码如下:

class BasicExample {
 public String getStreetName(Employee employee) {
  Address address = employee.getAddress();
  return address.getStreetName();
 }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

这段代码,我们访问了 employee 对象的 address 字段,并从中获取 street 信息,这违反了迪米特法则。因为我们不需要知道 emplyee 对象有 address 字段,只需要从 employee 对象中获取信息而不用关心它是如何存储的,address 对象不是当前对象的直接朋友。现在修改上面的代码来满足迪米特原则:

class Employee {
 private Address address;
 public String getStreetName() {
  return address.getStreetName();
 }
}
class BasicExample {
 public String getStreetName(Employee emplyee) {
  employee.getStreetName();
 }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

上面这段代码,我们只需要从employee 对象获取 adress 信息,如果将来employee 对象中改变了address 信息的存储也不会影响我们获取信息的代码。

链式调用

我们经常会写出如下代码,o.getA().getB().getC() ,如果这些getter 方法返回不同的字段,就违反了 LOD 原则,如果一个直接朋友对象返回另外一个对象,我们不应该再调用这个另外的对象的方法。但是怎么写链式调用,才符合 LOD 原则呢,原则有一点说明是可以调用在方法内部创建的变量,例如:

class C {
 public M createM() {
  return new M();
 }
}
  • 1
  • 2
  • 3
  • 4
  • 5

c.createM().getO() 如此调用符合LOD 法则。在方法内部新创建一个对象,此对象不被其他对象引用,调用此对象的方法所造成的影响也是局部的。

Builder Pattern

有个创建披萨的构造器,如下:

public static class PizzaBuilder {
 private Integer size;
 private String topping;
 public Pizza build() {
  Pizza pizza = new Pizza();
  pizza.size = this.size;
  pizza.topping = this.topping;
  return pizza;
 }
 public PizzaBuilder setSize(Integer size) {
  this.size = size;
  return this;
 }
 public PizzaBuilder setTopping(String topping) {
  this.topping = topping;
  return this;
 }
}
public class Test {
 public Pizza createPizza() {
  PizzaBuilder pizzaBuilder = new PizzaBuilder();
  Pizza pizza = pizzaBuilder.setSize(30).setTopping(“Cheese”).build();
  return pizza;
 }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

从上代码可以看出,pizza 对象是build 方法内创建的,以为着我们可以调用它的方法,因此,builder pattern 看起来是违反LOD 原则,其实是不违反的,builder 模式的本质是方法链式调用。

例外

当我们的方法接收一些数据结构当参数时,例如集合,我们可以调用集合的方法并迭代它里面的对象,此操作符合LOD 法则,但是如果我们调用集合对象里的方法,此时违反LOD 法则吗?例如下代码:


public void m(List < Order > orders) {
 for (int i = 0; i < orders.size(); i++) { //可接受
  Order order = orders.get(i); // 可接受
  order.buy(); // 可接受吗?
 }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

List 本身是一个对象,我们调用它自身 size() 和 get() 方法,肯定符合法则,但是 order.buy() 呢? 从严格定义来说,违反了法则,但是我们把包含多个对象的集合作为参数传递,也可以把集合里的对象当做直接朋友对象,在这里是可以接受的。当然也可以把这个集合迁移到一个对象中去,但这往往没必要。

总结

迪米特法则不是面向对象编程所需要的银弹,但是我们编程时遵守它可以带来很多好处,可以保障我们的代码结构清晰、灵活、更易维护。
在我们日常工作中,经常会接手维护一些老项目,如果之前的代码设计耦合度很高,阅读性较差,维护起来你是不是感觉很痛苦,总有感觉牵一发动全身。如果当时的代码符合迪米特法则,写出的代码基本上是松耦合、高内聚、易维护。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/488510
推荐阅读
相关标签
  

闽ICP备14008679号