A deep understanding of the JavaScript series (7) S.o.l.i.d the principle of opening and closing of the Five Principles Ocp_javascript skills

Source: Internet
Author: User
Objective
In this chapter, we are going to explain the 2nd of S.O.L.I.D's Five Principles JavaScript language implementation, the open and closed principle OCP (the open/closed principle).
The opening and closing principles are described as:
Software entities (classes, modules, functions, etc.) should is open for extension but the for closed.
Software entities (classes, modules, methods, etc.) should be open to extensions and closed to modifications, i.e., software entities should be extended without modification.
Copy Code
An open for extension (open to extended) means that when new requirements are present, you can extend the existing model to achieve the goal. The closing for modification (closed for modification) means that no modification to the entity is allowed, and plainly, that these entities that need to perform a variety of behaviors should be designed so that changes can be made without modification and that adherence to the open and closing principles facilitates project maintenance with minimal code.
Original English: http://freshbrewedcode.com/derekgreer/2011/12/19/solid-javascript-the-openclosed-principle/
Problem code
To visually describe, let's give an example to illustrate that the subordinate code is the code that dynamically shows the question list (without using the open and closed principle).
Copy Code code as follows:

Type of problem
var Answertype = {
choice:0,
Input:1
};
Problem entity
function question (label, Answertype, Choices) {
return {
Label:label,
Answertype:answertype,
Choices:choices//The choices here are optional parameters
};
}
var view = (function () {
Render a problem
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 ';
Show different code according to different types: drop-down menu and Input box, respectively
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 {
Iterate through the list of issues to show
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 ', Answertype.choice, [' Yes ', ' No ']),
Question (' What medications are you currently using? ', Answertype.input)
];
var questionregion = document.getElementById (' questions ');
View.render (questionregion, questions);

The above code, in the View object, contains a render method for displaying the question list, displaying the different display modes depending on the question type. A question contains a label and a problem type and choices options (if the type is selected). If the problem type is choice then produce a Drop-down menu based on the option, and if the type is input, simply show input input box.
The code has a limit, that is, if you add another question type, then you need to modify the renderquestion in the conditional statement, which is a clear violation of the opening and closing principle.
Refactoring code
Let's refactor this code to allow you to extend the render capability of the view object in the event of a new question type, without modifying the code inside the View object.
First, create a generic questioncreator function:
Copy Code code as follows:

function Questioncreator (spec, my) {
var that = {};
my = My | | {};
My.label = Spec.label;
My.renderinput = function () {
Throw "not implemented";
Here Renderinput is not implemented, the main purpose is to let the implementation code of the respective problem types to cover the entire method
};
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 coarse and reasonable code
The only difference is the above sentence my.renderinput ()
Because different types of problems have different implementations
Questionwrapper.appendchild (Questionlabel);
Questionwrapper.appendchild (answer);
return questionwrapper;
};
return to that;
}

If the combination of the code render a problem and provides an renderinput method that other functions can overwrite to use a different type of problem, we continue to look at the implementation code for each problem type:
Copy Code code as follows:

function Choicequestioncreator (spec) {
var my = {},
that = Questioncreator (spec, my);
Renderinput implementation of choice type
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 to that;
}
function Inputquestioncreator (spec) {
var my = {},
that = Questioncreator (spec, my);
Renderinput implementation of input type
My.renderinput = function () {
var input = document.createelement (' input ');
Input.type = ' text ';
return input;
};
return to that;
}

The Choicequestioncreator function and the Inputquestioncreator function correspond to the renderinput implementation of the Pull-down menu and input input box respectively, and the unified Questioncreator by internal call (spec, my And then returns that object (the same type, OH).
The code for the View object is fixed.
Copy Code code as follows:

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 just have to do it, OK:
Copy Code code as follows:

var questions = [
Choicequestioncreator ({
Label: ' Have you used tobacco products within ',
Choices: [' Yes ', ' No ']
}),
Inputquestioncreator ({
Label: ' What medications are you currently using? '
})
];

The final use of code, we can use this:
Copy Code code as follows:

var questionregion = document.getElementById (' questions ');
View.render (questionregion, questions);


The final code after refactoring
Copy Code code 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 to 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 to that;
}
function Inputquestioncreator (spec) {
var my = {},
that = Questioncreator (spec, my);
My.renderinput = function () {
var input = document.createelement (' input ');
Input.type = ' text ';
return input;
};
return to that;
}
var view = {
Render:function (target, questions) {
for (var i = 0; i < questions.length; i++) {
Target.appendchild (Questions[i].render ());
}
}
};
var questions = [
Choicequestioncreator ({
Label: ' Have you used tobacco products within ',
Choices: [' Yes ', ' No ']
}),
Inputquestioncreator ({
Label: ' What medications are you currently using? '
})
];
var questionregion = document.getElementById (' questions ');
View.render (questionregion, questions);

The above code uses some technical points, let's take a look at:
First, the creation of the Questioncreator method lets us use the template method pattern to delegat the function of handling the problem to the extended code renderinput for each problem type.
Second, we replaced the constructor properties of the previous question method with a proprietary spec attribute, because we encapsulated the render behavior and no longer needed to expose the attributes to the external code.
Third, we create an object for each problem type to implement the code, but each implementation must include a Renderinput method to overwrite the Renderinput code in the Questioncreator method, which is what we often call the strategy pattern.
By refactoring, we can remove an enumerated answertype of unnecessary problem types, and we can make choices a required parameter for the Choicequestioncreator function (the previous version is an optional argument).
Summarize
Refactoring a later version of the View object can be a very clear new extension of the different types of problems to extend the new object, and then declare the questions collection of the time to specify the type on the line, the view object itself no longer modify any changes, so as to achieve the opening and closing principle of the requirements.
Another: Understand C #, do not know if the above code and polymorphism of the implementation of some similar? In fact, the above code with a prototype can also be achieved, we can study it.
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.