The fifth of the six principles of "Design Patterns": Summary of the Dependency Inversion Principle


"Design Pattern" Six Principles Series Links: "Design Pattern" One of the Six Principles: Summary of Single
Responsibility Summary of Principles Four of the Six Principles of "Design Patterns": Summary of the Principle of Interface Segregation Five of the Six Principles of "Design Patterns": Summary of the Principle of Dependency Inversion




The six principles reflect the underlying logic of many programming: high cohesion, low coupling, object-oriented programming, interface-oriented programming, abstract-oriented programming, and ultimately achieve readability, reusability, and maintainability.

The six principles of design patterns are:

  • Single Responsibility Principle: Single Responsibility Principle
  • Open Closed Principle: Open and Closed Principle
  • Liskov Substitution Principle: Liskov Substitution Principle
  • Law of Demeter: Law of Demeter (least known principle)
  • Interface Segregation Principle: Interface Segregation Principle
  • Dependence Inversion Principle: The principle
    of dependency inversion combines the initials of these six principles (L is counted as one) is SOLID (solid, stable), which means the benefits of combining these six principles: establishing a stable, flexible , robust design.

This article introduces the fifth principle in SOLID: the Dependency Inversion Principle.

1. Definition of Dependency Inversion Principle (DIP)

The English translation of the Dependency Inversion Principle is Dependency Inversion Principle, abbreviated as DIP.

Chinese translation is sometimes called the dependency inversion principle .

In order to trace the source, I will first give the most original English description of this principle:

High-level modules shouldn’t depend on low-level modules. Both modules should depend on abstractions. In addition, abstractions shouldn’t depend on details. Details depend on abstractions.

We translate it into Chinese, which probably means:

  • High-level modules (high-level modules) do not depend on low-level modules (low-level).
  • High-level modules and low-level modules should rely on each other through abstractions.
  • In addition, abstractions (abstractions) do not depend on specific implementation details (details), and specific implementation details (details) depend on abstractions (abstractions).

The so-called division of high-level modules and low-level modules simply means that in the call chain, the caller belongs to the high-level, and the callee belongs to the low-level.

In the Java language, abstraction refers to interfaces or abstract classes, both of which cannot be instantiated directly, details are implementation classes, and classes generated by implementing interfaces or inheriting abstract classes are details. Its characteristic is that it can be instantiated directly, that is, the new keyword can be added.

The high-level module is the calling end, and the low-level module is the concrete implementation class.

The embodiment of the dependency inversion principle in Java is:

The dependencies between modules occur through abstraction, and there is no direct dependency between implementation classes, and the dependencies are generated through interfaces or abstractions.

To understand it simply, we want to program to the interface, or to program to the abstract .

4. Example of DIP

How to flexibly replace the third-party image loading library in the project, or coexist at the same time, the user chooses to use the third-party library according to the mobile phone.

First version, using only one Picasso loader

private void loadImage(String url, ImageView imageView) {
    
    
    Picasso.get().load(url).resize(50, 50).centerCrop().into(imageView)
}

The second version of the image loading library needs to be changed. Picasso is replaced by Glide, and only this tool class needs to be modified.

public class PicassoUtils {
    
    
    //从网络加载
    public static void loadByUrl(String url, IamgeView imageView){
    
    
        Picasso.get().load(url).resize(50, 50).centerCrop().into(imageView)
    }

    //从文件加载
    public static void loadByPath(String path,ImageView imageView) {
    
    

    }
}

In the third version, the requirements change, and both loading libraries are required, which can be adapted according to different mobile phones. (Similar: choose different push services according to different mobile phones, so as to improve the arrival rate of messages)

At this time, it is necessary to apply the idea of ​​​​interface-oriented programming.

interface ImageLoader {
    
    
    //从网络加载
    void loadByUrl(String url, ImageView imageView);
    //从文件加载
    void loadByPath(String path, ImageView imageView);
}

//使用 Picasso
public class PicassoLoader implements ImageLoader {
    
    
    ...
}

//使用 Glide
public class GlideLoader implements ImageLoader {
    
    
    ...
}

//业务层使用,imageLoader是什么就看需求,随便创建
//也可以定义一个全局管理类,里面视需求自定义全局 PicassoLoader 或 GlideLoader
// 调用方法
ImageLoader imageLoader = new PicassoLoader() 
  //ImageLoader imageLoader = new GlideLoader()
imageLoader.loadByUrl(url, imageView);

You can see the benefits of interface orientation: it can reduce coupling, the code is easy to maintain, and it is easy to expand. Because the interface is abstract, you don’t have to worry about the specific logic. , it is easier to expand and more flexible.

2. How to understand Inversion of Control (IOC)

The English translation of Inversion of Control is Inversion Of Control, abbreviated as IOC.

Inversion of control is a relatively general design idea, not a specific implementation method, and is generally used to guide the design at the framework level. The "control" mentioned here refers to the control of the program execution flow, and "reversal" refers to the fact that the programmer controls the execution of the entire program before using the framework. After using the framework, the execution flow of the entire program is controlled by the framework. Control of the process is "reversed" from the programmer to the framework.

3. How to understand Dependency Injection (DI)

Dependency injection is the exact opposite of inversion of control, it's a specific coding technique.

The English translation of dependency injection is Dependency Injection, abbreviated as DI.

So what exactly is dependency injection?

Let's sum it up in one sentence: instead of creating dependent class objects inside the class through new(), after the dependent class objects are created externally, pass (or inject) to them through constructors, function parameters, etc. class use.

In this example, the Notification class is responsible for message push, relying on the MessageSender class to push messages such as product promotions and verification codes to users. Let's implement it in two ways: dependency injection and non-dependency injection. The specific implementation code is as follows:

// 非依赖注入实现方式
public class Notification {
    
    
  private MessageSender messageSender;
  
  public Notification() {
    
    
    this.messageSender = new MessageSender(); //此处有点像hardcode
  }
  
  public void sendMessage(String cellphone, String message) {
    
    
    //...省略校验逻辑等...
    this.messageSender.send(cellphone, message);
  }
}

public class MessageSender {
    
    
  public void send(String cellphone, String message) {
    
    
    //....
  }
}
// 使用Notification
Notification notification = new Notification();

// 依赖注入的实现方式
public class Notification {
    
    
  private MessageSender messageSender;
  
  // 通过构造函数将messageSender传递进来
  public Notification(MessageSender messageSender) {
    
    
    this.messageSender = messageSender;
  }
  
  public void sendMessage(String cellphone, String message) {
    
    
    //...省略校验逻辑等...
    this.messageSender.send(cellphone, message);
  }
}
//使用Notification
MessageSender messageSender = new MessageSender();
Notification notification = new Notification(messageSender);

The dependent class objects are passed in through dependency injection, which improves the scalability of the code, and we can flexibly replace the dependent classes. This point was also mentioned when we talked about the "opening and closing principle".

Of course, the above code still has room for further optimization. We can also define MessageSender as an interface, based on the interface rather than implementing programming. The modified code is as follows:

public class Notification {
    
    
  private MessageSender messageSender;
  
  public Notification(MessageSender messageSender) {
    
    
    this.messageSender = messageSender;
  }
  
  public void sendMessage(String cellphone, String message) {
    
    
    this.messageSender.send(cellphone, message);
  }
}

public interface MessageSender {
    
    
  void send(String cellphone, String message);
}

// 短信发送类
public class SmsSender implements MessageSender {
    
    
  @Override
  public void send(String cellphone, String message) {
    
    
    //....
  }
}

// 站内信发送类
public class InboxSender implements MessageSender {
    
    
  @Override
  public void send(String cellphone, String message) {
    
    
    //....
  }
}

//使用Notification
MessageSender messageSender = new SmsSender();
Notification notification = new Notification(messageSender);

The above example is a typical usage of dependency injection.

4. Summary

The connection between "programming based on interfaces rather than implementation" and "dependency injection" is that both of them pass in dependent objects from the outside instead of new ones inside.

The difference is that "programming based on interface rather than implementation" emphasizes "interface", and emphasizes that the object of dependence is the interface, not the specific implementation class;

And "dependency injection" does not emphasize this, classes or interfaces are fine, as long as it is passed in from the outside and not new inside, it can be called dependency injection.

reference:

"The Beauty of Design Patterns"

"Re-learning Java Design Patterns"

"Android source code design pattern analysis and actual combat"

The foundation of design patterns - six design principles

Guess you like

Origin blog.csdn.net/jun5753/article/details/126878824