A brief note on design patterns-the design principle's dependency inversion principle

3.5 Dependency reversal principle

3.5.1 Inversion of control (IOC)

  • Inversion Of Control, abbreviated as IOC
public abstract class TestCase {
  public void run() {
    if (doTest()) {
      System.out.println("Test succeed.");
    } else {
      System.out.println("Test failed.");
    }
  }
  
  public abstract boolean doTest();
}

public class JunitApplication {
  private static final List<TestCase> testCases = new ArrayList<>();
  
  public static void register(TestCase testCase) {
    testCases.add(testCase);
  }
  
  public static final void main(String[] args) {
    for (TestCase case: testCases) {
      case.run();
    }
  }

After introducing this simplified version of the test framework into the project, you only need to fill the specific test code in the extension point reserved by the framework, that is, the doTest () abstract function in the TestCase class. There is no need to write the main () function responsible for executing the process. The specific code is as follows:

public class UserServiceTest extends TestCase {
  @Override
  public boolean doTest() {
    // ... 
  }
}

// 注册操作还可以通过配置的方式来实现,不需要程序员显示调用register()
JunitApplication.register(new UserServiceTest();
  • This is a typical example of implementing “inversion of control” through a framework: the framework provides an extensible code skeleton for assembling objects and managing the entire execution process. When programmers use the framework for development, they only need to add code related to their own business to the reserved extension point, and they can use the framework to drive the execution of the entire program process.

  • "Control" refers to the control of the execution flow of the program, and "reverse" refers to the programmer's own control of the execution of the entire program before the framework is used. After using the framework, the execution flow of the entire program can be controlled by the framework. Control of the process "reversed" from the programmer to the framework.

  • Control reversal is not a specific implementation technique, but a more general design idea, generally used to guide the design of the framework level.

3.5.2 Dependency Injection (DI)

  • Dependency Injection, abbreviated as DI

  • Contrary to control reversal, it is a specific coding technique

  • To summarize in one sentence: instead of creating a dependent class object inside the class through new (), but after creating the dependent class object externally, pass (or inject) it to the class through the constructor, function parameters, etc.

  • Example: The Notification class is responsible for pushing messages, and relies on the MessageSender class to push messages such as product promotions and verification codes to users. Use dependency injection and non-dependency injection to achieve this. 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);
    

    optimization:

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

    In the above, the dependent class objects are passed in through dependency injection, which can flexibly replace the dependent classes and improve the scalability of the code .

3.5.3 Dependency Injection Framework (DI Framework)

  • There are many dependency injection frameworks, such as Google Guice, Java Spring, Pico Container, Butterfly Container, etc.
  • You only need to configure all the class objects that need to be created, the dependencies between classes and classes through the extension points provided by the dependency injection framework, and you can implement the framework to automatically create objects, manage the life cycle of objects, and dependency injection Things that need programmers to do.

3.5.4 Dependency inversion principle (DIP)

  • Dependency Inversion Principle, abbreviated as DIP

  • High-level modules do not depend on low-level modules. High-level modules and low-level modules should rely on each other through abstractions.

  • Abstraction (abstractions) do not rely on specific implementation details (details), specific implementation details (details) depend on abstraction (abstractions).

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

  • Example: Tomcat is a container for running Java Web applications. The web application code we write only needs to be deployed under the Tomcat container, and then it can be called and executed by the Tomcat container. According to the previous division principle, Tomcat is a high-level module, and the web application code we write is a low-level module. There is no direct dependency between Tomcat and the application code. Both rely on the same "abstraction", which is the Servlet specification. The Servlet specification does not depend on the implementation details of specific Tomcat containers and applications, while the Tomcat container and applications depend on the Servlet specification.

3.5.5 reviews

  • SmallFly:

    The concept of dependency inversion principle is that high-level modules do not depend on low-level modules. It seems that high-level modules are required, but it is actually regulating the design of low-level modules.

    The interface provided by the low-level module should be sufficiently abstract and general, and the use type and scenario of the high-level module need to be considered in the design .

    Obviously, the high-level module should use the low-level module and depend on the low-level module. On the contrary, low-level modules need to be designed according to high-level modules, and the appearance of "inverted" appears .

    This design has two advantages:

    1. Low-level modules are more versatile and more applicable
    2. The high-level module does not depend on the specific implementation of the low-level module, which facilitates the replacement of the low-level module

Guess you like

Origin www.cnblogs.com/wod-Y/p/12746794.html