This article is a summary of the study of “the Beauty of design Mode”

Open Closed Principle

The Open Closed Principle is the Open Closed Principle, abbreviated as OCP. Software entities (module, classes,functions,etc.) should be open for extension, but closed for modification. Software entities (modules, classes, methods, etc.) should be “open for extension, closed for modification”.

This is a brief description, but if we were to elaborate, it would be that adding new functionality should extend code (adding modules, classes, methods, etc.) rather than modify existing code (modifying modules, classes, methods, etc.). There are two things to note. The first is that the open closed principle does not mean that changes are completely eliminated, but that new features are developed with minimal code changes. The second is that the same code change may be considered a “change” at coarse code granularity. At finer code granularity, it may be considered “extended.”

Does changing the code mean violating the open closed principle?

It is impossible to add a new function without “modifying” the code of any module, class, or method. Classes need to be created, assembled, and initialized to build a running program, and this part of the code is bound to change. What we need to do is try to make the changes more focused, less, and more high-level, and try to make the core, the most complex part of the logic code meet the open closed principle.

How to do “open for extension, closed for modification”?

In fact, the open closed principle is about the extensibility of the code, the “gold standard” to determine whether a piece of code is easy to expand. If a piece of code in response to future changes in demand, can do “open to extension, modify closed”, it shows that this piece of code scalability is good. So asking how to be “open to extension, closed to modification” is roughly equivalent to asking how to write code that extends well.

In order to write code that is as extensible as possible, we should always be aware of extension, abstraction, and encapsulation. These “subliminals” are probably more important than any development technique. When writing the code, we should spend more time on thinking about more, this code what are the possible future demand changes, how to design the code structure, keep good extension points in advance, to demand changes in the future, do not need to change the code structure, minimize code changes, new code flexibility to insert to the extension point, Do “open for extension, closed for modification”.

Also, after identifying the mutable and immutable parts of the code, we wrap the mutable parts to isolate the changes and provide an abstract immutable interface for the upper system to use. When the implementation changes, we simply extend a new implementation based on the same abstract interface and replace the old one, with little change to the upstream code.

Among the many design principles, ideas, and patterns most commonly used to improve code extensibility are: polymorphism, dependency injection, programming based on interfaces rather than implementations, and most design patterns (for example, decorations, policies, templates, chains of responsibility, states, etc.).

For example, our code sends asynchronous messages over Kafka. To develop such a feature, we need to learn to abstract it down to a set of asynchronous message interfaces independent of the specific message queue (Kafka). All upper-level systems program on this set of abstract interfaces and are called by means of dependency injection. When replacing a new message queue, such as Kafka with RockeMQ, it is easy to unplug the old message queue implementation and insert the new one. The specific code is as follows:

// This part reflects abstract consciousness
public interface MessageQueue { / /... }
public class KafkaMessageQueue implements MessageQueue { / /... }
public class RocketMessageQueue implements MessageQueue { / /... }
  
public interface MessageFormatter { / /... }
public class JsonMessageFormatter implements MessageFormatter { / /... }
public class ProtoBufMessageFormatter implements MessageFormatter { / /... }
  
public class Demo {
  private MessageQueue msgQueue; // Programming for implementations based on interfaces
  public Demo(MessageQueue msgQueue) { // Dependency injection
    this.msgQueue = msgQueue;
  }
  
  // msgFormatter: polymorphism, dependency injection
  public void sendNotification(Notification notification, MessageFormatter msg){
    // ...}}Copy the code