The open closed principle (OCP) is the most important principle in object-oriented programming. Most of the time, a program with good design, often shows that it is in accordance with the open closed principle. When it is necessary to change the function of a program or add new functions to the program, the method of adding code can be used, but it is not allowed to change the source code of the program.
The story background
Let’s say we’re the maintainer of a large Web project, and when we take over the project, we find that it already has over 100,000 lines of JavaScript code and hundreds of JS files. Soon I received a new requirement to print out the number of nodes in the page in the window.onload function. Of course it doesn’t bother us. So we open the text editor, search for the window.onload function’s location in the file, and add the following code inside the function
window.onload = function() {/ / original code slightly. The console log (document. GetElementsByTagName (The '*' ).length );
};
Copy the code
Apply the OCP principle
Function.prototype.after = function( afterfn ){
var __self = this;
return function(){
var ret = __self.apply( this, arguments );
afterfn.apply( this, arguments );
returnret; }}; window.onload = ( window.onload ||function(){} ).after(function(){
console.log( document.getElementsByTagName( The '*' ).length );
});
Copy the code
By decorating the function dynamically, we completely ignore the internal implementation of the previous window.onload function, even if we get a copy of the scrambled and compressed code. As long as it was a stable function before, it won’t go wrong in the future because of our new requirements. The new code and the original code can not affect each other.
Write methods that conform to OCP code
Excessive conditional branching is a common cause of programs violating the open closed principle. Every time a new if statement needs to be added, the original function is forced to change. In fact, whenever we see a bunch of if or Swtich-case statements, the first thing we should think about is whether we can refactor them using the polymorphism of the object.
Use the idea of polymorphism
It is a common technique to use object polymorphism to make programs adhere to the open and closed principle.
- Do not conform to the OCP
var makeSound = function( animal ){
if ( animal instanceof Duck ){
console.log( 'Quack quack' );
} else if ( animal instanceof Chicken ) {
console.log( 'Cluck cluck'); }}; var Duck =function() {}; var Chicken =function() {}; makeSound( new Duck() ); makeSound( new Chicken() ); // Add a dog to the animal world. MakeSound = var makeSound =function( animal ){
if ( animal instanceof Duck ){
console.log( 'Quack quack' );
} else if ( animal instanceof Chicken ) {
console.log( 'Cluck cluck' );
} else if ( animal instanceof Dog ) {
console.log(Woof woof woof); }}; var Dog =function() {}; // Add code to makeSound(new Dog()); // Add a dogCopy the code
Using the idea of polymorphism, we isolate the invariant parts of the program (animals all bark) and then seal up the variable parts (different types of animals make different noises), thus making the program scalable. When we want to make a dog bark, we just add a piece of code and don’t change the makeSound function
var makeSound = function( animal ){
animal.sound();
};
var Duck = function() {}; Duck.prototype.sound =function(){
console.log( 'Quack quack' );
};
var Chicken = function() {}; Chicken.prototype.sound =function(){
console.log( 'Cluck cluck'); }; makeSound( new Duck() ); // gaga makeSound(new Chicken()); / / luo luo luo / * * * * * * * * * increase animal Dog, don't need to change the original makeSound function * * * * * * * * * * * * * * * * / var Dog =function() {}; Dog.prototype.sound =function(){
console.log( Woof woof woof); }; makeSound( new Dog() ); / / aufCopy the code
Place link
Placing hooks is also a way to separate changes. We place a hook where the program is likely to change, and the result of the hook determines the next step in the program. As a result, there is a fork in the path of the original code execution, and the future execution of the program is buried in many possibilities.
Use the callback function
In JavaScript, functions can be passed as arguments to another function, which is one of the meanings of higher-order functions. In this case, we usually refer to this function as a callback function. In the JavaScript version of the design mode, policy mode, command mode, and so on can be easily implemented with callbacks. The callback function is a special kind of hook. We can encapsulate a portion of mutable logic in callback functions and pass the callback functions as arguments to a stable and closed function. When the callback function is executed, the program can produce different results because of the internal logic of the callback function.
conclusion
The open Closed principle may seem like an illusory principle, but we can find some rules for making programs adhere to the open closed principle as much as possible. The most obvious is to figure out what changes are going to happen in your program and encapsulate those changes. By encapsulating change, you can isolate the parts of the system that are stable from those that are prone to change. In the evolution of the system, we only need to replace the parts that are easy to change, which is relatively easy to replace if they are already packaged. And beyond the changing part is the stable part. In the evolution of the system, the stable parts do not need to change.
Series of articles:
JavaScript Design Patterns and Development Practices