Inheritance and gardening

3 min readPublished November 20, 2019Updated May 02, 2022

This post is the tenth of a series adapted from my conference talk, Fun, Friendly Computer Science. Links to other posts in the series can be found at the end of this post.

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.

Well-Rounded Dev

Liked this post? Subscribe to receive semi-regular thoughts by email.

    I won't send you spam. Unsubscribe at any time.