Designing An Object-Oriented Program
Animal ->
Cat ->
HouseCat
Tiger
Bird ->
Parrot
There are two keywords that are essential for OOP with classes in JavaScript. These keywords are extends and super.
The extends keyword allows me to inherit from an existing class. Based on the above hierarchy, I can code the Animal class like this:
class Animal {
// ... class code here ...
}
Then I can code, for example, the Cat sub-class, like this:
class Cat extends Animal {
// ... class code here ...
}
This is how the extends keyword is used to setup inheritance relationships.
The super keyword allows me to “borrow” functionality from a super-class, in a sub-class.
Now I can start thinking about how to implement my OOP class hierarchy.
Before I even begin, I need to think about things like: * What should go into the base class of Animal? In other words, what will all the sub-classes inherit from the base class? * What are the specific properties and methods that separate each class from others? * Generally, how will my classes relate to one another?
Once I’ve thought it through, I can build my classes.
So, my plan is as follows:
- The
Animalclass’ constructor will have two properties:colorandenergy The
Animalclass’ prototype will have three methods:isActive(),sleep(), andgetColor().The
isActive()method, whenever ran, will lower the value ofenergyuntil it hits0. TheisActive()method will also report the updated value ofenergy. Ifenergyis at zero, the animal object will immediately go to sleep, by invoking thesleep()method based on the said condition.The
getColor()method will just console log the value in thecolorproperty.The
Catclass will inherit fromAnimal, with the additionalsound,canJumpHigh, andcanClimbTreesproperties specific to theCatclass. It will also have its ownmakeSound()method.The
Birdclass will also inherit fromAnimal, but is own specific properties will be quite different fromCat. Namely, theBirdclass will have thesoundand thecanFlyproperties, and themakeSoundmethod too.The
HouseCatclass will extend theCatclass, and it will have its ownhouseCatSoundas its special property. Additionally, it will override themakeSound()method from theCatclass, but it will do so in an interesting way. If themakeSound()method, on invocation, receives a single option argument - set totrue, then it will runsuper.makeSound()- in other words, run the code from the parent class (Cat) with the addition of running theconsole.log(this.houseCatSound). Effectively, this means that themakeSound()method on theHouseCatclass’ instance object will have two separate behaviors, based on whether we pass ittrueorfalse.The
Tigerclass will also inherit fromCat, and it will come with its owntigerSoundproperty, while the rest of the behavior will be pretty much the same as in theHouseCatclass.- Finally, the
Parrotclass will extend theBirdclass, with its owncanTalkproperty, and its ownmakeSound()method, working with two conditionals: one that checks if the value oftruewas passed tomakeSoundduring invocation, and another that checks the value stored insidethis.canTalkproperty.
class Animal {
// constructor: color, energy
// isActive()
// if energy > 0, energy -=20, console log energy
// else if energy <= 0, sleep()
// sleep()
// energy += 20
// console.log energy
}
class Cat extends Animal {
// constructor: sound, canJumpHigh, canClimbTrees, color, energy
// makeSound()
// console.log sound
}
class Bird extends Animal {
// constructor: sound, canFly, color, energy
// makeSound()
// console.log sound
}
class HouseCat extends Cat {
// constructor: houseCatSound, sound, canJumpHigh, canClimbTrees, color, energy
// makeSound(option)
// if (option)
// super.makeSound()
// console.log(houseCatSound)
}
class Tiger extends Cat {
// constructor: tigerSound, sound, canJumpHigh, canClimbTrees, color, energy
// makeSound(option)
// if (option)
// super.makeSound()
// console.log(tigerSound)
}
class Parrot extends Bird {
// constructor: canTalk, sound, canJumpHigh, canClimbTrees, color, energy
// makeSound(option)
// if (option)
// super.makeSound()
// if (canTalk)
// console.log("talking!")
}
Coding the Animal class
First, I’ll code the base Animal class.
class Animal {
constructor(color = 'yellow', energy = 100) {
this.color = color;
this.energy = energy;
}
isActive() {
if(this.energy > 0) {
this.energy -= 20;
console.log('Energy is decreasing, currently at:', this.energy)
} else if(this.energy == 0){
this.sleep();
}
}
sleep() {
this.energy += 20;
console.log('Energy is increasing, currently at:', this.energy)
}
getColor() {
console.log(this.color)
}
}
Each animal object, no matter which one it is, will share the properties of color and energy. Now I can code the Cat and Bird classes:
class Cat extends Animal {
constructor(sound = 'purr', canJumpHigh = true, canClimbTrees = true, color, energy) {
super(color, energy);
this.sound = sound;
this.canClimbTrees = canClimbTrees;
this.canJumpHigh = canJumpHigh;
}
makeSound() {
console.log(this.sound);
}
}
class Bird extends Animal {
constructor(sound = 'chirp', canFly = true, color, energy) {
super(color, energy);
this.sound = sound;
this.canFly = canFly;
}
makeSound() {
console.log(this.sound);
}
}
If I didn’t use the super keyword in our sub-classes, once I’d run the above code, I’d get the following error: Uncaught ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor.
And now I can code the three remaining classes: HouseCat, Tiger, and Parrot.
class HouseCat extends Cat {
constructor(houseCatSound = "meow", sound, canJumpHigh, canClimbTrees, color,energy) {
super(sound,canJumpHigh,canClimbTrees, color,energy);
this.houseCatSound = houseCatSound;
}
makeSound(option) {
if (option) {
super.makeSound();
}
console.log(this.houseCatSound);
}
}
class Tiger extends Cat {
constructor(tigerSound = "Roar!", sound, canJumpHigh, canClimbTrees, color,energy) {
super(sound,canJumpHigh,canClimbTrees, color,energy);
this.tigerSound = tigerSound;
}
makeSound(option) {
if (option) {
super.makeSound();
}
console.log(this.tigerSound);
}
}
class Parrot extends Bird {
constructor(canTalk = false, sound, canFly, color,energy) {
super(sound,canFly, color,energy);
this.canTalk = canTalk;
}
makeSound(option) {
if (option) {
super.makeSound();
}
if (this.canTalk) {
console.log("I'm a talking parrot!");
}
}
}
Now that we’ve set up this entire inheritance structure, we can build various animal objects.
For example, I can build two parrots: one that can talk, and the other that can’t.
var polly = new Parrot(true); // we're passing `true` to the constructor so that polly can talk
var fiji = new Parrot(false); // we're passing `false` to the constructor so that fiji can't talk
polly.makeSound(); // 'chirp', 'I'm a talking parrot!'
fiji.makeSound(); // 'chirp'
polly.color; // yellow
polly.energy; // 100
polly.isActive(); // Energy is decreasing, currently at: 80
var penguin = new Bird("shriek", false, "black and white", 200); // setting all the custom properties
penguin; // Bird {color: 'black and white', energy: 200, sound: 'shriek', canFly: false }
penguin.sound; // 'shriek'
penguin.canFly; // false
penguin.color; // 'black and white'
penguin.energy; // 200
penguin.isActive(); // Energy is decreasing, currently at: 180
Also, I can build a pet cat:
var leo = new HouseCat();
Now I can have leo purr:
// leo, no purring please:
leo.makeSound(false); // meow
// leo, both purr and meow now:
leo.makeSound(true); // purr, meow
Additionally, I can build a tiger:
var cuddles = new Tiger();
My cuddles tiger can purr and roar, or just roar:
cuddles.makeSound(false); // Roar!
cuddels.makeSound(true); // purr, Roar!
Here’s the complete code:
class Animal {
constructor(color = 'yellow', energy = 100) {
this.color = color;
this.energy = energy;
}
isActive() {
if(this.energy > 0) {
this.energy -= 20;
console.log('Energy is decreasing, currently at:', this.energy)
} else if(this.energy == 0){
this.sleep();
}
}
sleep() {
this.energy += 20;
console.log('Energy is increasing, currently at:', this.energy)
}
getColor() {
console.log(this.color)
}
}
class Cat extends Animal {
constructor(sound = 'purr', canJumpHigh = true, canClimbTrees = true, color, energy) {
super(color, energy);
this.sound = sound;
this.canClimbTrees = canClimbTrees;
this.canJumpHigh = canJumpHigh;
}
makeSound() {
console.log(this.sound);
}
}
class Bird extends Animal {
constructor(sound = 'chirp', canFly = true, color, energy) {
super(color, energy);
this.sound = sound;
this.canFly = canFly;
}
makeSound() {
console.log(this.sound);
}
}
class HouseCat extends Cat {
constructor(houseCatSound = "meow", sound,canJumpHigh,canClimbTrees, color,energy) {
super(sound,canJumpHigh,canClimbTrees, color,energy);
this.houseCatSound = houseCatSound;
}
makeSound(option) {
if (option) {
super.makeSound();
}
console.log(this.houseCatSound);
}
}
class Tiger extends Cat {
constructor(tigerSound = "Roar!", sound,canJumpHigh,canClimbTrees, color,energy) {
super(sound,canJumpHigh,canClimbTrees, color,energy);
this.tigerSound = tigerSound;
}
makeSound(option) {
if (option) {
super.makeSound();
}
console.log(this.tigerSound);
}
}
class Parrot extends Bird {
constructor(canTalk = false, sound,canFly, color,energy) {
super(sound,canFly, color,energy);
this.canTalk = canTalk;
}
makeSound(option) {
if (option) {
super.makeSound();
}
if (this.canTalk) {
console.log("I'm a talking parrot!");
}
}
}
var fiji = new Parrot(false); // we're passing `false` to the constructor so that fiji can't talk
var polly = new Parrot(true); // we're passing `true` to the constructor so that polly can talk
fiji.makeSound(); // undefined
fiji.makeSound(true); // chirp
polly.makeSound(); // I'm a talking parrot!
polly.makeSound(true); // chirp, I'm a talking parrot!
polly.color; // yellow
polly.energy; // 100
polly.isActive(); // Energy is decreasing, currently at: 80
var penguin = new Bird("shriek", false, "black and white", 200); // setting all the custom properties
penguin; // Bird {color: 'black and white', energy: 200, sound: 'shriek', canFly: false }
penguin.sound; // 'shriek'
penguin.canFly; // false
penguin.color; // 'black and white'
penguin.energy; // 200
penguin.isActive(); // Energy is decreasing, currently at: 180
var leo = new HouseCat();
// leo, no purring please:
leo.makeSound(false); // meow
// leo, both purr and meow now:
leo.makeSound(true); // purr, meow
var cuddles = new Tiger();
cuddles.makeSound(false); // Roar!
cuddles.makeSound(true); // purr, Roar!