JavaScript design patterns and development practices-JavaScript Polymorphism
"Polymorphism" is derived from polymorphism in the Greek text. The split is poly (plural) + morph (morphology) + ism, which can be literally understood as the plural form.
The actual meaning of polymorphism is: the same operation acts on different objects and can produce different interpretations and different execution results. In other words, when sending the same message to different objects, these objects will give different feedback based on the message.
It is not easy to understand polymorphism literally. The following is an example.
The master has two animals in his house: a duck and a chicken. When the master sends a "scream" command to them, the duck will scream, the chicken giggled. Both animals call each other in their own way. They are also "animals, and sound can be issued", but according to the instructions of their masters, they will each make a different cry.
In fact, it contains the idea of polymorphism. The following code gives a detailed introduction.
1.2.1 a piece of "polymorphism" JavaScript code
The above story is implemented using JavaScript Code as follows:
Var makeSound = function (animal) {if (animal instanceof Duck) {console. log ('ga ga ');} else if (animal instanceof Chicken) {console. log ('giggle ') ;}}; var Duck = function () {}; var Chicken = function () {}; makeSound (new Duck ()); // ga makeSound (new Chicken (); // giggling
This Code does reflect "polymorphism". When we send a "Call" message to the duck and chicken respectively, they make different responses based on the message. However, such a "polymorphism" is not satisfactory. If an animal, such as a dog, is added later, it is clear that the sound of the dog is "Wang". At this time, we must modify it.makeSound
Function. It is always dangerous to modify code. The more places you modify, the higher the possibility of program errors. When there are more and more animals,makeSound
It may become a huge function.
The idea behind polymorphism is to separate "what to do" from "Who to do and how to do, that is, the separation of "unchanged things" and "things that may change. In this story, animals are called, which remains unchanged, but how can different types of animals be called immutable. Isolate the unchanged part and encapsulate the variable part, which gives us the ability to expand the program. The program seems to be scalable and complies with the open-closed principle, compared with modifying the Code, adding only the code can accomplish the same function, which is obviously more elegant and secure.
1.2.2 object Polymorphism
The following is the modified Code. First, we isolate the unchanged part, that is, all animals will make a cry:
var makeSound = function( animal ){ animal.sound();};
Then we encapsulate the variables separately. The polymorphism we mentioned just now actually refers to the object polymorphism:
Var Duck = function () {} Duck. prototype. sound = function () {console. log ('ga ga ') ;}; var Chicken = function () {} Chicken. prototype. sound = function () {console. log ('giggling ');}; makeSound (new Duck (); // ga makeSound (new Chicken (); // giggling
Now we send a "call" message to both the duck and the chicken. They have different responses after receiving the message. If another dog is added to the animal world one day, simply append some code, instead of modifying the previousmakeSound
Function:
Var Dog = function () {} Dog. prototype. sound = function () {console. log ('wang Wangwang ') ;}; makeSound (new Dog (); // Wang Wangwang
1.2.3 type check and Polymorphism
Type check is a non-open topic before it shows object polymorphism, but JavaScript is a dynamic type language that does not need to perform type check. In order to truly understand the purpose of polymorphism, we need to make a transition from a static language.
We have explained in section 1.1 that the static type language will perform the type matching check during compilation. Taking Java as an example, a strict type check is required during code compilation, so different types of values cannot be assigned to variables. This type check sometimes makes the code stiff. The Code is as follows:
String str; str = abc; // No problem str = 2; // Error
Now we try to replace the above example with the Java code:
Public class Duck {// Duck class public void makeSound () {System. out. println (ga) ;}} public class Chicken {// public void makeSound () {System. out. println (GIGGLING) ;}} public class AnimalSound {public void makeSound (Duck duck) {// (1) duck. makeSound () ;}} public class Test {public static void main (String args []) {AnimalSound animalSound = new AnimalSound (); Duck duck = new Duck (); animalSound. makeSound (duck); // output: ga }}
We have successfully made the duck scream, but if we want the chicken to scream now, we find this is impossible. Because (1)AnimalSound
ClassmakeSound
Method, which can only be acceptedDuck
Type parameters:
Public class Test {public static void main (String args []) {AnimalSound animalSound = new AnimalSound (); Chicken chicken = new Chicken (); animalSound. makeSound (chicken); // an error is returned. Only Duck-type parameters are allowed }}
In some cases, while enjoying the security of static language type checks, we also feel bound.
To solve this problem, static object-oriented languages are generally designed to beUpward Transformation: When assigning values to a class variable, the type of the variable can either use the class itself or use the super class of the class. This is like when we describe a sparrow or magpie in the sky, we usually say "a sparrow is flying" or "a magpie is flying ". However, if you want to ignore their specific types, you can also say that "A bird is flying".
Similarly, whenDuck
Object andChicken
All object types are hidden in the supertypeAnimal
Behind,Duck
Object andChicken
Objects can be exchanged for use, which is the only way to make objects show polymorphism, and the presentation of polymorphism is the goal of achieving many design patterns.
1.2.4 use inheritance to obtain Polymorphism
Using inheritance to obtain the polymorphism effect is the most common means to make objects show polymorphism. Inheritance generally includes implementation inheritance and interface inheritance. This section describes implementation inheritance. For the examples of interface inheritance, see Chapter 21st.
First, createAnimal
Abstract class, and then letDuck
AndChicken
Are inherited fromAnimal
Abstract class. In the following code, the value assignment statements at (1) and (2) are obviously true, because ducks and chickens are also animals:
Public abstract class Animal {abstract void makeSound (); // abstract method} public class Chicken extends Animal {public void makeSound () {System. out. println (GIGGLING) ;}} public class Duck extends Animal {public void makeSound () {System. out. println (ga) ;}} Animal duck = new Duck (); // (1) Animal chicken = new Chicken (); // (2)
The rest isAnimalSound
ClassmakeSound
Method acceptanceAnimal
Type parameter, rather than the specificDuck
Type orChicken
Type:
Public class AnimalSound {public void makeSound (Animal animal) {// accept Animal parameters. makeSound () ;}} public class Test {public static void main (String args []) {AnimalSound animalSound = new AnimalSound (); Animal duck = new Duck (); animal chicken = new Chicken (); animalSound. makeSound (duck); // output the animalSound. makeSound (chicken); // The output Giggling }}
1.2.5 JavaScript Polymorphism
We learned from the previous explanation that the multi-state idea is actually separating "what" from "who" to achieve this, in the final analysis, the coupling relationship between types must be eliminated first. If the coupling relationship between types is not eliminatedmakeSound
The method specifies that the object for calling is of a certain type, and it cannot be replaced with another type. In Java, polymorphism can be achieved through upward transformation.
The JavaScript variable type is variable at runtime. A JavaScript Object that can representDuck
Type object, which can also representChicken
Type object, which means that the polymorphism of JavaScript objects is inherent.
This inherent polymorphism is not difficult to explain. JavaScript, as a dynamic type language, does not check the type of the created object or the passed parameter type during compilation. In the sample code in section 1.2.2makeSound
Pass in the functionduck
The object can also be passed as a parameter.chicken
Object as a parameter.
It can be seen that whether an animal can make a cry depends on whether it hasmakeSound
Method, regardless of whether it is a type object. There is no "type coupling" to any extent ". This is what we learned from the duck type in the previous section. In JavaScript, technologies such as upward transformation are not required to achieve polymorphism.
Role of 1.2.6 polymorphism in Object-Oriented Programming
Many people think that polymorphism is the most important technology in object-oriented programming languages. However, it is still difficult for us to see this. After all, most people do not care about how chickens are called or how ducks are called. Let the chicken and duck send different voices under the same message. What does this have to do with programmers?
Martin Fowler wrote in refactoring: improving the design of existing code:
The most fundamental benefit of polymorphism is that you don't have to ask the object "What type are you?" And then call an action of the object based on the obtained answer-you just need to call this action, all other polymorphism mechanisms will be arranged for you.
In other words, the most fundamental role of polymorphism is to eliminate these conditional branch statements by converting procedural conditional branch statements into object polymorphism.
Martin Fowler can be well interpreted using the following example:
At the filming scene, when the director shouted "action", the main character began to recite lines, the lighting engineer was responsible for lighting, and the masses behind the actors pretended to be shot and dumped to the ground, the prop artist sprinkled snow into the camera. When getting the same message, every object knows what to do. If we don't use object polymorphism, but use process-oriented methods to write this code, it is equivalent that after the Filming starts, the Director will come to everyone every time, confirm their job division (type) and tell them what to do. If it is mapped to a program, the program will be filled with conditional branch statements.
With the object polymorphism, the Director does not have to consider what each object should do after receiving the message when publishing the message. What the object should do is not a temporary decision, but has been agreed and rehearsed in advance. What each object should do has become a method of this object, installed inside the object, and each object is responsible for their own behavior. Therefore, these objects can perform their respective work in an orderly manner based on the same message.
It is the advantage of object-oriented design to distribute behaviors in various objects and let these objects take responsibility for their own behaviors.
Let's look at another example in real development. The idea of this example is very similar to the story of animal call.
Suppose we want to write a map application. Now there are two available map API providers for us to access our own applications. Currently, we chose Google Map, which is provided by Google map APIs.show
To display the entire map on the page. The sample code is as follows:
Var googleMap = {show: function () {console. log ('start rendering google map') ;}; var renderMap = function () {googleMap. show () ;}; renderMap (); // output: Start rendering google Map
For some reason, Google maps will be replaced with Baidu maps.renderMap
Functions maintain a certain degree of elasticity. We use some conditional branches to makerenderMap
Functions Support both Google Maps and Baidu maps:
Var googleMap = {show: function () {console. log ('start rendering google map') ;}}; var baiduMap = {show: function () {console. log ('start rendering the baidu ') ;}}; var renderMap = function (type) {if (type = 'Google') {googleMap. show ();} else if (type = 'baidu') {baiduMap. show () ;}}; renderMap ('Google '); // output: Start to render the google map renderMap ('baidu'); // output: Start to render the baidu Map
As you can see, althoughrenderMap
At present, functions maintain a certain degree of elasticity, but this elasticity is very fragile. If you need to replace it with search map, you must change it.renderMap
Function, continue to pile up the condition branch statement in it.
We should first abstract the same part of the program, that is, display a map:
Var renderMap = function (map) {if (map. show instanceof Function) {map. show () ;}}; renderMap (googleMap); // output: Start rendering google map renderMap (baiduMap); // output: Start rendering baidu Map
Now let's look for the polymorphism in this Code. When we send a "Map Display" message to the Google Maps object and Baidu map object respectively, they will callshow
Method to generate different execution results. The object polymorphism reminds us that "what to do" and "how to do" can be separated, even if a search map is added in the future,renderMap
The function still does not need to be changed, as shown below:
Var sosoMap = {show: function () {console. log ('start rendering soso map') ;}}; renderMap (sosoMap); // output: Start rendering soso Map
In this example, we assume that each map API provides a method namedshow
In actual development, it may not be so smooth. In this case, you can use the adapter mode to solve the problem.
1.2.7 design pattern and Polymorphism
The sub-title of "Design Patterns" by GoF is "the basis for reusable object-oriented software ". From the perspective of object-oriented design, this book extracts some reusable object-oriented design skills through repeated use of encapsulation, inheritance, polymorphism, and combination technologies. Polymorphism is the most important among them, and the implementation of most design patterns is inseparable from the idea of polymorphism.
In command mode 1, the request is encapsulated in some command objects, which allows the caller and receiver of the command to be completely decoupled.execute
Methods, different commands will do different things, and thus different execution results will be generated. The process of doing these things has long been encapsulated inside the command object. As the customer who calls the command, there is no need to worry about the specific process of command execution.
In composite mode 2, polymorphism allows the customer to ignore the difference between the composite object and the leaf node object, which is the biggest role of composite mode. When the composite object and leaf node object send the same message, they will do what they should do, and the composite object will forward the message to the leaf node object below, the leaf node object will provide real feedback on these messages.
In policy Mode 3, Context does not have the ability to execute algorithms, but delegates this responsibility to a policy object. Algorithms for each policy object have been encapsulated in the object. When we send "computing" messages to these policy objects, they return different computing results.
1. See chapter 9th "command mode" 2. See Chapter 10th "Combination Mode" 3. See chapter 5th "rule mode"
In JavaScript, a language that uses a function as a first-class object, a function itself is also an object. A function is used to encapsulate behavior and can be passed everywhere. When we send a "call" message to some functions, these functions return different execution results, which is a manifestation of "polymorphism, this is also why many design patterns can be replaced by higher-order functions in JavaScript.