Deep understanding of the JavaScript series (8) The S.O.L.I.D principle of the Five Principles Lsp_javascript skills

Source: Internet
Author: User
Tags error handling
Objective
In this chapter, we will explain the 3rd of the S.o.l.i.d Five Principles JavaScript language implementation, the Richter replacement principle LSP (the Liskov substitution principle).

Original English: http://freshbrewedcode.com/derekgreer/2011/12/31/solid-javascript-the-liskov-substitution-principle/
Copy Code
The opening and closing principles are described as:

Subtypes must is substitutable for their base types.
A derived type must be able to replace its base type.
Copy Code
In object-oriented programming, inheritance provides a mechanism for the code of subclasses and shared base classes, this is done by encapsulating the generic data and behavior in the base type, and then declaring a more detailed subtype of the type, and in order to apply the Richter substitution principle, the inherited subtype needs to be semantically equivalent to the expected behavior in the underlying type.

For a better understanding, please refer to the following code:
Copy Code code as follows:

function Vehicle (my) {
var i = My | | {};
my.speed = 0;
My.running = false;

This.speed = function () {
return my.speed;
};
This.start = function () {
My.running = true;
};
This.stop = function () {
My.running = false;
};
This.accelerate = function () {
my.speed++;
};
This.decelerate = function () {
my.speed--;
}, This.state = function () {
if (!my.running) {
return "parked";
}
else if (my.running && my.speed) {
return "Moving";
}
else if (my.running) {
return "Idle";
}
};
}

The above code defines a vehicle function whose constructor provides some basic operations for the vehicle object, so let's think about if the current function is currently running on the product environment of the service customer, if you need to add a new constructor now to implement the vehicle that accelerates the move. After thinking about it, we wrote the following code:
Copy Code code as follows:

function Fastvehicle (my) {
var i = My | | {};

var that = new Vehicle (my);
That.accelerate = function () {
My.speed + 3;
};
return to that;
}

In the browser console we have tested, all the functions are our expectations, no problem, fastvehicle speed 3 times times faster, and inherit his method is also according to our expected work. After that, we started deploying this new version of the class library to the production environment, but we received a new constructor that didn't support the execution of the existing code, and the following code snippet revealed the problem:
Copy Code code as follows:

var maneuver = function (vehicle) {
Write (Vehicle.state ());
Vehicle.start ();
Write (Vehicle.state ());
Vehicle.accelerate ();
Write (Vehicle.state ());
Write (Vehicle.speed ());
Vehicle.decelerate ();
Write (Vehicle.speed ());
if (Vehicle.state ()!= "Idle") {
Throw "The vehicle is still moving!";
}
Vehicle.stop ();
Write (Vehicle.state ());
};

Based on the above code, we see that the exception thrown is "The vehicle is still moving!" because the author of this code has always thought that the number of acceleration (accelerate) and deceleration (decelerate) is the same. But Fastvehicle's code and vehicle's code are not entirely replaceable. Therefore, fastvehicle violates the principle of the Richter substitution.

At this point, you might think, "but the client can't always assume that vehicle is doing this by the rules," and that the Richter substitution principle (LSP) is not an impediment to the implementation of LSP, and is not based on our thinking that the inheriting subclass should be sure to update the code in the behavior. It is whether such updates can be implemented in the current expectation.

In this case, the solution to this incompatibility requires a little redesign of the vehicle class library or client invocation code, or both.

Reduce LSP obstruction
So how do we prevent LSP from interfering? Unfortunately, it is not always possible to do so. We have a few strategies here to deal with this matter.

Contract (contracts)
One strategy for dealing with LSP's undue hindrance is to use a contract, which has 2 forms: the Executive Instruction (executable specifications) and error handling, and in the executive instruction, a detailed class library contract also includes a set of automated tests, and error handling is handled directly in the code , such as preconditions, post conditions, constant checks, and so on, can be viewed from Bertrand Miller's masterpiece contract design. Although automation testing and contract design are not within the scope of this text, when we use it, I recommend the following:

Check the use of test-driven development (Test-driven Development) to guide the design of your code
Design a reusable class library by using contract design technology at will
For your own code to maintain and implement, using contract design tends to add a lot of unnecessary code, if you want to control input, adding tests is very necessary, if you are a class library author, use the contract design, you should pay attention to the incorrect use of the method and let your users as a test tool.

Avoid inheritance
Another test to avoid the LSP impediment is: if possible, try not to inherit, in Gamma's masterpiece "Design patterns–elements of reusable object-orineted Software", we can see the following recommendations:

Favor object composition over class inheritance
Try to use an object combination rather than a class inheritance
Copy Code
In some books it is discussed that the only effect of a combination is a static type, a class based language (for example, you can change behavior at run time), and a problem related to JavaScript is coupling, and when using inheritance, the inherited subtypes are coupled with their base types. That is, the type of change affects the inherited subtype. The combination tends to be smaller in object and easier to maintain in static and dynamic language languages.

Related to behavior rather than inheritance
By now, we have discussed the Richter substitution principle, including the inheritance context, indicating the object-oriented reality of JavaScript. However, the essence of the Richter replacement principle (LSP) is not really about inheritance, but behavior compatibility. JavaScript is a dynamic language, and the contractual behavior of an object is not determined by the type of object, but by the desired function of the object. The initial concept of the Richter substitution principle is a guide to the principles of inheritance, equivalent to an implicit interface in object design.

For example, let's take a look at Robert C. Martin's masterpiece, "Agile Software development principles, patterns and practices" in a rectangular type:

Rectangular Example
Consider that we have a program that uses a rectangular object such as the following:
Copy Code code as follows:

var rectangle = {
length:0,
width:0
};
[Code]
Later, the program needs a square, because the square is a long (length) and wide (width) of the same special rectangle, so we feel that create a square instead of a rectangle. We added the length and width properties to match the rectangle's declaration, but we felt that using the getters/setters of the attribute generally we could keep the length and width synchronized to ensure that a square was declared:
[Code]
var square = {};
(function () {
var length = 0, width = 0;
Note that the DefineProperty mode is a new feature of version 262-5.
Object.defineproperty (square, "length", {
Get:function () {return length;},
Set:function (value) {length = width = value;}
});
Object.defineproperty (square, "width", {
Get:function () {return width;},
Set:function (value) {length = width = value;}
});
})();

Unfortunately, when we use a square instead of a rectangle to execute the code, we find the problem, one of the ways to compute the rectangular area is as follows:
Copy Code code as follows:

var g = function (Rectangle) {
Rectangle.length = 3;
Rectangle.width = 4;
Write (rectangle.length);
Write (rectangle.width);
Write (Rectangle.length * rectangle.width);
};

When the method is invoked, the result is 16, not the expected 12, our square squares object violates the LSP principle, and square's length and width attributes imply that it is not compatible with rectangle 100%, but we are not always so explicit. To solve this problem, we can redesign a Shape object to implement the program, according to the concept of polygons, we declare rectangle and square,relevant. In any case, our aim is to say that the Richter replacement principle is not just an inheritance, but any method (in which the behavior can be otherwise).

Summarize
The Richter Replacement principle (LSP) expresses the meaning of not inheriting relationships, but any method (as long as the behavior of the method can be realized by another behavior).

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.