This post is the tenth of a series adapted from my conference talk, Fun, Friendly Computer Science.
I have a black thumb. No matter how hard I try, I really struggle to keep plants alive, regardless of if they’re indoor or outdoor plants. Despite this, I do know that all garden plants need sun, soil, and water to survive. When I go to buy plants for my garden every spring, I spend a lot of time in the garden center reading those little care instruction cards that are stuck in the pot with each plant.
A plant's care instructions are the aggregate of its sun, soil, and water needs. The instructions describe how to help your plant stay healthy and thrive.
If we wanted to write an application to print those care instruction cards for a garden center, we can use inheritance to share the code that prints the instructions but also allows each plant class to implement its specific sun/soil/water needs.
Inheritance supports reusability in programming. A child class (also called a subclass) can inherit all of the fields, methods, and properties from another class (also called the base or superclass). The child class can then implement its own logic that differs from its parent or add logic in addition to what exists in the parent class.
Let’s use our garden plant example to see inheritance in action. I’ve abbreviated some of the code samples here for ease of reading but the full sample can be found in my Github repo (or if you prefer, I also have the samples in Ruby.
All plants' care instructions are created by combining their sun, soil, and water needs. So by putting that logic in the base Plant
class, we only define it once and share the logic.
export default class Plant {
// ... abbreviated code for blog post
get sun() {
return this[Sun];
}
get shade() {
return !this[Sun];
}
get plantingInstructions() {
return `Planting instructions: ${this.soilNeeds}`;
}
get careInstructions() {
return `Sunlight needs: ${this.lightNeeds}<br/>Watering instructions: ${this.waterNeeds}`;
}
learnHowToGarden() {
return `${this.name}\n
${this.plantingInstructions}\n
${this.careInstructions}`;
}
}
Then each child class, like Geranium
, can inherit from the Plant
base class. Geranium sets its sun, soil, and water needs in the constructor since the specific needs are the only part that is unique between the child classes.
export default class Geranium extends Plant {
constructor() {
super();
this.name = "Geranium";
this.sun = true;
this.wet = false;
this.lightNeeds = "4 - 6 hours of direct sunlight per day.";
this.soilNeeds = "Plant in a pot with soil-less potting mixture and good drainage.";
this.waterNeeds = "Water thoroughly and allow to soil to completely dry between waterings.";
}
}
Notice that we do not add any code for the learnHowToGarden
method in the Geranium
class. The extends
keyword in the Geranium
class tells our code to inherit everything from the Plant
class. Then, we can use the learnHowToGarden
method on all child classes that inherit from Plant
.
const Plants = [new Geranium(), new Begonia(), new Coleus()];
Plants.forEach(plant => plant.learnHowToGarden());
Inheritance keeps our code DRY and organized. We can model “is a” relationships in code that match our mental models of the world while supporting reusability in our codebase.
Top comments (0)