In-depth understanding of the open and closed principles of the JavaScript series (7) S.O.L. I. D. OCP

Source: Internet
Author: User

Preface
In this chapter, we will explain part 1 of The Five Principles of S.O.L. I. D: Implementation of The JavaScript language, and The Principle of Open and Closed OCP (The Open/Closed Principle ).
The opening/closing principle is described as follows:
Software entities (classes, modules, functions, etc.) shocould be open for extension but closed for modification.
Software entities (classes, modules, methods, etc.) should be open to extensions and closed to modifications, that is, software entities should be extended without modification.
Copy code
Open for extension (open for extension) means that when new requirements arise, you can expand the existing model to achieve the goal. Close for modification means that no modification can be made to the object. To put it bluntly, these entities that need to execute various behaviors should be designed to achieve various changes without modification. Adhering to the open and closed principles is conducive to project maintenance with the least amount of code.
Http://freshbrewedcode.com/derekgreer/2011/12/19/solid-javascript-the-openclosed-principle/.
Problem code
For an intuitive description, let's take an example to demonstrate that the subordinate code is the code that dynamically displays the question list (the open/closed principle is not used ). Copy codeThe Code is as follows: // question type
Var AnswerType = {
Choice: 0,
Input: 1
};
// Problem entity
Function question (label, answerType, choices ){
Return {
Label: label,
AnswerType: answerType,
Choices: choices // The choices here is an optional parameter.
};
}
Var view = (function (){
// Render
Function renderQuestion (target, question ){
Var questionWrapper = document. createElement ('div ');
QuestionWrapper. className = 'question ';
Var questionLabel = document. createElement ('div ');
QuestionLabel. className = 'Question-label ';
Var label = document. createTextNode (question. label );
QuestionLabel. appendChild (label );
Var answer = document. createElement ('div ');
Answer. className = 'Question-input ';
// Display different codes based on different types: drop-down menu and input box
If (question. answerType === AnswerType. Choice ){
Var input = document. createElement ('select ');
Var len = question. choices. length;
For (var I = 0; I <len; I ++ ){
Var option = document. createElement ('option ');
Option. text = question. choices [I];
Option. value = question. choices [I];
Input. appendChild (option );
}
}
Else if (question. answerType === AnswerType. Input ){
Var input = document. createElement ('input ');
Input. type = 'text ';
}
Answer. appendChild (input );
QuestionWrapper. appendChild (questionLabel );
QuestionWrapper. appendChild (answer );
Target. appendChild (questionWrapper );
}
Return {
// Traverse all problem lists for display
Render: function (target, questions ){
For (var I = 0; I <questions. length; I ++ ){
RenderQuestion (target, questions [I]);
};
}
};
})();
Var questions = [
Question ('Have you used tobacco products within the last 30 days? ', AnswerType. Choice, ['yes', 'no']),
Question ('What medications are you currently using? ', AnswerType. Input)
];
Var questionRegion = document. getElementById ('questions ');
View. render (questionRegion, questions );

In the above Code, the view object contains a render method to display the question list. Different display methods are used for different question types, A question contains a label, a question type, and choices options (if the type is selected ). If the problem type is Choice, a drop-down menu is generated based on the options. If the type is Input, the input box is displayed simply.
This code has a limit that if you add another question type, you need to modify the conditional statements in renderQuestion again, which obviously violates the open/closed principle.
Code Reconstruction
Let's refactor this code so that the render capability of the view object can be extended in case of a new question type, without modifying the internal code of the view object.
First, create a general questionCreator function:Copy codeThe Code is as follows: function questionCreator (spec, my ){
Var that = {};
My = my | {};
My. label = spec. label;
My. renderInput = function (){
Throw "not implemented ";
// The renderInput is not implemented here. The main purpose is to overwrite the entire method with the implementation code of the respective problem types.
};
That. render = function (target ){
Var questionWrapper = document. createElement ('div ');
QuestionWrapper. className = 'question ';
Var questionLabel = document. createElement ('div ');
QuestionLabel. className = 'Question-label ';
Var label = document. createTextNode (spec. label );
QuestionLabel. appendChild (label );
Var answer = my. renderInput ();
// The render method is the same rough and reasonable code
// The only difference is that the above sentence my. renderInput ()
// Different problem types have different implementations
QuestionWrapper. appendChild (questionLabel );
QuestionWrapper. appendChild (answer );
Return questionWrapper;
};
Return that;
}

The combination of functions of this Code is a render problem, and an unimplemented renderInput method is provided so that other functions can overwrite the code to use different problem types, let's continue to look at the implementation code for each problem type:Copy codeThe Code is as follows: function choiceQuestionCreator (spec ){
Var my = {},
That = questionCreator (spec, my );
// Choice-type renderInput implementation
My. renderInput = function (){
Var input = document. createElement ('select ');
Var len = spec. choices. length;
For (var I = 0; I <len; I ++ ){
Var option = document. createElement ('option ');
Option. text = spec. choices [I];
Option. value = spec. choices [I];
Input. appendChild (option );
}
Return input;
};
Return that;
}
Function inputQuestionCreator (spec ){
Var my = {},
That = questionCreator (spec, my );
// Input-type renderInput implementation
My. renderInput = function (){
Var input = document. createElement ('input ');
Input. type = 'text ';
Return input;
};
Return that;
}

The choiceQuestionCreator function and the inputQuestionCreator function correspond to the renderInput implementation in the drop-down menu and input box respectively. By calling the unified questionCreator (spec, my) internally, they return that object (of the same type ).
The view object code is fixed.Copy codeCode: var view = {
Render: function (target, questions ){
For (var I = 0; I <questions. length; I ++ ){
Target. appendChild (questions [I]. render ());
}
}
};

So when we declare the problem, we only need to do this and we will be OK:Copy codeCode: var questions = [
ChoiceQuestionCreator ({
Label: 'ave you used tobacco products within the last 30 days? ',
Choices: ['yes', 'no']
}),
InputQuestionCreator ({
Label: 'What medications are you currently using? '
})
];

The final code can be used as follows:Copy codeThe Code is as follows: var questionRegion = document. getElementById ('questions ');
View. render (questionRegion, questions );

Final code after reconstructionCopy codeThe Code is as follows: function questionCreator (spec, my ){
Var that = {};
My = my | {};
My. label = spec. label;
My. renderInput = function (){
Throw "not implemented ";
};
That. render = function (target ){
Var questionWrapper = document. createElement ('div ');
QuestionWrapper. className = 'question ';
Var questionLabel = document. createElement ('div ');
QuestionLabel. className = 'Question-label ';
Var label = document. createTextNode (spec. label );
QuestionLabel. appendChild (label );
Var answer = my. renderInput ();
QuestionWrapper. appendChild (questionLabel );
QuestionWrapper. appendChild (answer );
Return questionWrapper;
};
Return that;
}
Function choiceQuestionCreator (spec ){
Var my = {},
That = questionCreator (spec, my );
My. renderInput = function (){
Var input = document. createElement ('select ');
Var len = spec. choices. length;
For (var I = 0; I <len; I ++ ){
Var option = document. createElement ('option ');
Option. text = spec. choices [I];
Option. value = spec. choices [I];
Input. appendChild (option );
}
Return input;
};
Return that;
}
Function inputQuestionCreator (spec ){
Var my = {},
That = questionCreator (spec, my );
My. renderInput = function (){
Var input = document. createElement ('input ');
Input. type = 'text ';
Return input;
};
Return that;
}
Var view = {
Render: function (target, questions ){
For (var I = 0; I <questions. length; I ++ ){
Target. appendChild (questions [I]. render ());
}
}
};
Var questions = [
ChoiceQuestionCreator ({
Label: 'ave you used tobacco products within the last 30 days? ',
Choices: ['yes', 'no']
}),
InputQuestionCreator ({
Label: 'What medications are you currently using? '
})
];
Var questionRegion = document. getElementById ('questions ');
View. render (questionRegion, questions );

The above Code applies some technical points. Let's take a look at them one by one:
First, the creation of the questionCreator method allows us to use the template method mode to assign the delegat function to the extended code renderInput for each problem type.
Secondly, we replaced the constructor attribute of the previous question method with a private spec attribute. Because we encapsulate the render behavior for operations, we no longer need to expose these attributes to external code.
Third, we create an object for each problem type for code implementation, but each implementation must contain the renderInput method to overwrite the renderInput code in the questionCreator method, this is what we often call a policy model.
Through refactoring, we can remove unnecessary problem types of enumeration AnswerType, and make choices a required parameter for the choiceQuestionCreator function (the previous version is an optional parameter ).
Summary
After reconstruction, the view object of the later version can be clearly expanded, and the new object can be expanded for different problem types, then, when declaring the questions set, you can specify the type in it. The view object itself does not modify any changes, so as to meet the requirements of the open and closed principles.
In addition: If I understand C #, do I know whether the above Code is similar to the implementation of polymorphism? In fact, the above Code can also be implemented using a prototype. You can study it on your own.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.