Polymorphism is just a fancy way of saying that we can use an inherited class in place of a base class. This is a very important concept in typed languages, but is automatically handled for you in dynamically typed languages like JavaScipt. One of the nicest features of JavaScipt is that you not only can use a child class in place of a parent class, but you can use any class that has the methods and properties that you want to use. This is known as duck typing and is called that from the saying "if it looks like a duck, walks like a duck, and quacks like a duck then it is probably a duck."
The power of polymorphism comes from the ability to call a function that takes a base class with any class that is inherited from that class. For example, lets say we create a function for playing with the pets that we created last section.
// Note - See previous section for the Pet classes
// function for pet class objects
function playWithPet(pet) {
printTestLine("Playing with " + pet.name);
pet.makeNoise();
}
// Pet polymorphism test code
printTestLine("Class polymorphism using inherited pets:");
var cat = new Cat("Abby")
var dog = new Dog("Smokie")
var fish = new Pet("Dora", "Fish")
playWithPet(cat);
playWithPet(dog);
playWithPet(fish);
Notice that we can call this function with any of the pet classes that we created. This is a very important concept as it means that we can create functions that use base classes and they will continue to work with classes that we create in the future giving us a lot of flexibility. Duck typing takes this a bit further by not even requiring the same base type. Lets test this by creating a non-pet duck class.
Duck = function(name) {
this.name = name;
this.makeNoise = function() {
printTestLine(this.name + " Quacks!");
}
}
// Testing duck-typing
printTestLine("---------");
printTestLine("Duck-typing test:");
var duck = new Duck("Daffy");
playWithPet(duck);
As you can see, the only requirement JavaScript has is that the method being called exists. The Duck class did not need to inherit from the pet class nor did it need to implement all the methods in the pet class, just the methods and properties that any function it was being called with requires.
For smaller programs, duck-typing is a very handy feature, but it can result in problems with larger projects. This is why interfaces are often used in place of duck-typing in languages like Java.
Saturday, January 26, 2019
Saturday, January 12, 2019
5.4 Inheritance
One of the most powerful and most over-abused features of object oriented programming is the use of inheritance. The basic idea here is that there are a lot of classes of objects that have a lot of features in common. Instead of re-writing the same code repeatedly for similar objects, a hierarchy of related classes can be created. You start with a base class, also known as the parent, that has the base functionality it's children will inherit. To demonstrate this, lets create a base class for pets.
The pet class will have a name for the pet and a species for the pet. Pets will be able to greet their owner, and make a noise. First, lets take a look at the more modern ECMAScript 6 way of doing this. This is set up just like any normal class that we would have created in the previous section.
class E6Pet {
constructor(name, species) {
this.name = name;
this.species = species
}
greet() {
printTestLine(this.name + ", the " + this.species + ", comes to greet you");
}
makeNoise() {
printTestLine(this.name + " makes a noise");
}
}
var fish = new E6Pet("Nemo", "Fish")
fish.greet();
fish.makeNoise();
The old way of creating the base class is also the same way you would create a class using older versions of ECMAScript.
function Pet(name, species) {
this.name = name;
this.species = species;
this.greet = function() {
printTestLine(this.name + ", the " + this.species + ", comes to greet you");
}
this.makeNoise = function() {
printTestLine(this.name + " makes a noise");
}
}
var oldfish = new Pet("Dora", "Fish")
oldfish.greet();
oldfish.makeNoise();
Once you have a class that can be used as a base-class, you can create a new class that inherits the methods and variables of it's parent. This effectively gives you all the code that you have written in the base class. You can then add additional methods and variables allowing the new class to have the functionality of the base class while adding new features to the class. This alone would make inheritance worth doing, but it gets even more powerful. It is possible to override an existing method and replace it with another method that is more appropriate for the class you are creating.
In our example, different pets make different sounds so by overriding the makeNoise method, we can create species specific noises. We will create a dog and a cat, with each of them having a unique noise and the dog being able to wag it's tail.
ECMAScript 6 makes inheritance very easy as you just use the extends keyword when you are creating a class. Sub-classes can have a different number of parameters, with the super method used to call the original code. Overriding the method is simply the matter of using a method with the same "signature." A method's signature is simply the method name and the particular parameters that make up the method. finally creating a new method is done by having a new method in the class.
class E6Cat extends E6Pet{
constructor(name) {
super(name, "Cat")
}
makeNoise() {
printTestLine(this.name + " Meows!");
}
}
class E6Dog extends E6Pet {
constructor(name) {
super(name, "Dog")
}
makeNoise() {
printTestLine(this.name + " Barks!");
}
wag() {
printTestLine(this.name + " wags his tail!");
}
}
var cat = new E6Cat("Lexi")
var dog = new E6Dog("Scruffy")
cat.greet();
cat.makeNoise();
dog.greet();
dog.makeNoise();
dog.wag();
The old way of inheritance is a bit more confusing as older versions of ECMAScript does not have a way of directly indicating inheritance so the programmer must manually set this up. JavaScript use prototype based inheritance where every class has a prototype that determines which methods it has. This prototype is used by the new operator to set up the class, but the prototype can be modified manually. The result is that there are many ways of setting up inheritance in JavaScript. The most common way is simply assigning the prototype of the class to the prototype of it's parent by using the new function.
function Cat(name) {
this.name = name;
this.species = "Cat";
this.makeNoise = function() {
printTestLine(this.name + " Meows!");
}
}
Cat.prototype = new Pet();
(Dog = function(name) {
this.name = name;
this.species = "Dog";
this.makeNoise = function() {
printTestLine(this.name + " Barks!");
}
this.wag = function() {
printTestLine(this.name + " wags his tail!");
}
} ).prototype = new Pet();
var oldcat = new Cat("Abby")
var olddog = new Dog("Smokie")
oldcat.greet();
oldcat.makeNoise();
olddog.greet();
olddog.makeNoise();
olddog.wag();
You will notice two slightly different styles for doing this. The cat way is the textbook way while the dog way is the way JavaScript code generated by Animate CC does things. You could also manually copy just the methods you are interested in, which can be a handy way of inheriting functionality from multiple different classes.
Inheritance is not just for reducing code duplication, but allows for a powerful technique known as polymorphism which we will be covering next.
The pet class will have a name for the pet and a species for the pet. Pets will be able to greet their owner, and make a noise. First, lets take a look at the more modern ECMAScript 6 way of doing this. This is set up just like any normal class that we would have created in the previous section.
class E6Pet {
constructor(name, species) {
this.name = name;
this.species = species
}
greet() {
printTestLine(this.name + ", the " + this.species + ", comes to greet you");
}
makeNoise() {
printTestLine(this.name + " makes a noise");
}
}
var fish = new E6Pet("Nemo", "Fish")
fish.greet();
fish.makeNoise();
The old way of creating the base class is also the same way you would create a class using older versions of ECMAScript.
function Pet(name, species) {
this.name = name;
this.species = species;
this.greet = function() {
printTestLine(this.name + ", the " + this.species + ", comes to greet you");
}
this.makeNoise = function() {
printTestLine(this.name + " makes a noise");
}
}
var oldfish = new Pet("Dora", "Fish")
oldfish.greet();
oldfish.makeNoise();
Once you have a class that can be used as a base-class, you can create a new class that inherits the methods and variables of it's parent. This effectively gives you all the code that you have written in the base class. You can then add additional methods and variables allowing the new class to have the functionality of the base class while adding new features to the class. This alone would make inheritance worth doing, but it gets even more powerful. It is possible to override an existing method and replace it with another method that is more appropriate for the class you are creating.
In our example, different pets make different sounds so by overriding the makeNoise method, we can create species specific noises. We will create a dog and a cat, with each of them having a unique noise and the dog being able to wag it's tail.
ECMAScript 6 makes inheritance very easy as you just use the extends keyword when you are creating a class. Sub-classes can have a different number of parameters, with the super method used to call the original code. Overriding the method is simply the matter of using a method with the same "signature." A method's signature is simply the method name and the particular parameters that make up the method. finally creating a new method is done by having a new method in the class.
class E6Cat extends E6Pet{
constructor(name) {
super(name, "Cat")
}
makeNoise() {
printTestLine(this.name + " Meows!");
}
}
class E6Dog extends E6Pet {
constructor(name) {
super(name, "Dog")
}
makeNoise() {
printTestLine(this.name + " Barks!");
}
wag() {
printTestLine(this.name + " wags his tail!");
}
}
var cat = new E6Cat("Lexi")
var dog = new E6Dog("Scruffy")
cat.greet();
cat.makeNoise();
dog.greet();
dog.makeNoise();
dog.wag();
The old way of inheritance is a bit more confusing as older versions of ECMAScript does not have a way of directly indicating inheritance so the programmer must manually set this up. JavaScript use prototype based inheritance where every class has a prototype that determines which methods it has. This prototype is used by the new operator to set up the class, but the prototype can be modified manually. The result is that there are many ways of setting up inheritance in JavaScript. The most common way is simply assigning the prototype of the class to the prototype of it's parent by using the new function.
function Cat(name) {
this.name = name;
this.species = "Cat";
this.makeNoise = function() {
printTestLine(this.name + " Meows!");
}
}
Cat.prototype = new Pet();
(Dog = function(name) {
this.name = name;
this.species = "Dog";
this.makeNoise = function() {
printTestLine(this.name + " Barks!");
}
this.wag = function() {
printTestLine(this.name + " wags his tail!");
}
} ).prototype = new Pet();
var oldcat = new Cat("Abby")
var olddog = new Dog("Smokie")
oldcat.greet();
oldcat.makeNoise();
olddog.greet();
olddog.makeNoise();
olddog.wag();
You will notice two slightly different styles for doing this. The cat way is the textbook way while the dog way is the way JavaScript code generated by Animate CC does things. You could also manually copy just the methods you are interested in, which can be a handy way of inheriting functionality from multiple different classes.
Inheritance is not just for reducing code duplication, but allows for a powerful technique known as polymorphism which we will be covering next.
Subscribe to:
Posts (Atom)