[Demeter's Law] of design principles, don't approach evil

1. What is Demeter's Law

The Law of Demeter LoD (Law of Demeter LoD) means that an object should maintain the least understanding of other objects, also known as the Least Knowledge Principle (LKP), to minimize the coupling between classes. The Dimit principle mainly emphasizes only communicating with friends and not talking to strangers. Classes that appear in member variables, method input, and output parameters can be called member friend classes, while classes that appear in method bodies do not belong to friend classes.

Its meaning is: if two software entities do not need to communicate directly, then there should be no direct mutual call, and the call can be forwarded by a third party. Its purpose is to reduce the degree of coupling between classes and improve the relative independence of modules.

The "friend" in Dimit's law refers to: the current object itself, the member objects of the current object, the objects created by the current object, the method parameters of the current object, etc. These objects are associated, aggregated or combined with the current object, and can be Methods for direct access to these objects.

1. Understand the Law of Demeter

There should be no dependencies between classes that should not have direct dependencies; between classes that have dependencies, try to only rely on the necessary interfaces. Dimit's law is to reduce the coupling between classes, and make the classes as independent as possible. Each class should know little about other parts of the system. Once a change occurs, fewer classes need to know about the change.

2. How to understand "high cohesion, loose coupling"?

"High cohesion and loose coupling" is a very important design idea, which can effectively improve the readability and maintainability of the code, and reduce the scope of code changes caused by functional changes. "High cohesion" is used to guide the design of the class itself, and "loose coupling" is used to guide the design of dependencies between classes.

The so-called high cohesion means that similar functions should be placed in the same class, and dissimilar functions should not be placed in the same class. Similar functions are often modified at the same time, put them in the same class, and the modification will be more concentrated. The so-called loose coupling means that in the code, the dependencies between classes are simple and clear. Even if two classes have a dependency relationship, a code change in one class will not or rarely cause code changes in the dependent class.

Two, examples

1. Example 1

Now to design a permission system, TeamLeader needs to check the number of courses currently published online. At this time, the TeamLeader needs to find the Employee to make statistics, and the Employee then tells the TeamLeader the statistical results. Next, let's look at the code:

public class Course {
    
    
}

public class Employee{
    
    
	public void checkNumberOfCourses(List<Course> courseList){
    
    
		System.out.println("目前已发布的课程数量是:" + courseList.size());
	}
}

public class TeamLeader{
    
    
	public void commandCheckNumber(Employee employee){
    
    
		List<Course> courseList = new ArrayList<Course>();
		for (int i= 0; i < 20 ;i ++){
    
    
			courseList.add(new Course());
		}
		employee.checkNumberOfCourses(courseList);
	}
}

public static void main(String[] args) {
    
    
	TeamLeader teamLeader = new TeamLeader();
	Employee employee = new Employee();
	teamLeader.commandCheckNumber(employee);
}

Written here, in fact, the functions have been realized, and the code looks fine. According to the Dimit principle, TeamLeader only wants results, and does not need to communicate directly with Course. The Employee statistics need to refer to the Course object. TeamLeader and Course are not friends, as can be seen from the class diagram below:
insert image description here
let's modify the code:

public class Employee {
    
    
	public void checkNumberOfCourses(){
    
    
		List<Course> courseList = new ArrayList<Course>();
		for (int i= 0; i < 20 ;i ++){
    
    
			courseList.add(new Course());
		}
		System.out.println("目前已发布的课程数量是:"+courseList.size());
	}
}

public class TeamLeader {
    
    
	public void commandCheckNumber(Employee employee){
    
    
		employee.checkNumberOfCourses();
	}
}

Looking at the class diagram below again, Course and TeamLeader are no longer associated.
insert image description here
The core of Dimit's law is to reduce the coupling between classes.
But be careful: since each class reduces unnecessary dependencies, Dimiter's law only requires reducing the inter-class (inter-object) coupling relationship, not requiring no dependencies at all.

2. Example 2

We implement a simplified version of the search engine's ability to crawl web pages. The code contains three main classes. Among them, the NetUtil class is responsible for the underlying network communication and obtains data according to the request; the HtmlDownloader class is used to obtain web pages through URLs; Document represents web page documents, and subsequent web page content extraction, word segmentation, and indexing are all based on this.

public class NetUtil{
    
    
    // 省略属性和其他方法...
    public Byte[] send(HtmlRequest htmlRequest) {
    
    
      //...
    }
}
public class HtmlDownloader {
    
    
  private NetUtil netUtil;//通过构造函数或IOC注入
  
  public Html downloadHtml(String url) {
    
    
    Byte[] rawHtml = netUtil.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);
  }
  //...
}

Let's analyze these three classes. Although there seems to be no problem, there is still room for optimization.

First look at the NetUtil class. As a tool class, it should be as "general" as possible. The send method should not depend on HtmlRequest.

public class NetUtil {
    
    
    // 省略属性和其他方法...
    public Byte[] send(String address, Byte[] data) {
    
    
      //...
    }
}

We modified the NetUtil class, then HtmlDownloader should also be modified synchronously:

public class HtmlDownloader {
    
    
  private NetUtil netUtil;//通过构造函数或IOC注入
  
  public Html downloadHtml(String url) {
    
    
  	HtmlRequest htmlRequest = new HtmlRequest(url);
    Byte[] rawHtml = netUtil.send(htmlRequest.getAddress(), htmlRequest.getContent().getBytes());
    return new Html(rawHtml);
  }
}

The downloadHtml in the construction method of the Document class takes a long time, and should not be placed in the construction method, which will affect the testability of the code; the
HtmlDownloader object is created by new in the constructor, which violates the design idea of ​​​​based on interfaces rather than implementation programming, and also It will affect the testability of the code;
from the business point of view, the Document webpage document does not need to rely on the HtmlDownloader class, which violates Dimit's law.

ublic 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);
  }
}

Guess you like

Origin blog.csdn.net/A_art_xiang/article/details/130564936