DEV Community

Cover image for Why Santa Claus Can't Inherit from Human: A Festive OOP Lesson
Bruno Noriller
Bruno Noriller

Posted on • Originally published at Medium

Why Santa Claus Can't Inherit from Human: A Festive OOP Lesson

'Tis the season to be jolly, and what better way to get into the holiday spirit than with a technical blog post about object-oriented programming (OOP)? In this festive post, I'll explore the concept of inheritance and why it's important to ignore reality to use it appropriately.

Even if Santa Claus was real (which he totally is – I checked twice!), he wouldn't be able to inherit from the class Human.

What is Inheritance?

In OOP, inheritance is a way for one class to inherit the characteristics and behavior of another class. It allows us to create a hierarchy of classes and represent an "is-a" relationship. For example, a "Dog" class could inherit from a "Mammal" class, because a dog is a type of mammal.

Why Santa Claus Can't Inherit from Human

While it might seem logical at first to make Santa Claus inherit from Human, there are several reasons why this is not a good idea.

Inheritance is typically used to represent an "is-a" relationship, meaning that the subclass (in this case, Santa Claus) is a more specific type of the superclass (Human). However, Santa Claus is not a type of human – he has unique characteristics and abilities that do not apply to all humans. For example, Santa Claus has a list of naughty and nice children, a magical sleigh that can fly around the world in a single night, and the ability to deliver presents to every good boy and girl on Earth.

You might think, sure, that’s how it works, you have a more generic base with more specific things on top. But reality and programming work differently. For example, if we try to make Santa Claus inherit from Human, we might end up with methods such as "getAge()" or "getHeight()" that do not make sense for a character who is hundreds of years old and can shrink or grow to fit through chimneys.

A Better Approach

A better approach would be to create a separate class for Santa Claus and define his characteristics and behavior in that class. This way, we can represent him accurately and avoid any confusion or unnecessary checks all over the code. After all, we wouldn't want any naughty code sneaking its way into our nice list!

Square and Rectangle

Another example of inappropriate inheritance is trying to make a class "Square" inherit from a class "Rectangle". While a square is a type of rectangle in real life, it has its own unique characteristics (such as all sides being equal in length) that are not shared by all rectangles. In this case, for programming purposes, it would be better to create a separate class for Square and define its characteristics and behavior independently.

Here's an example of what can happen when we make Square inherit from Rectangle:

class Rectangle {
    constructor(public length: number, public width: number) {}

    getArea(): number {
        return this.length * this.width;
    }

    setLength(length: number): void {
        this.length = length;
    }

    setWidth(width: number): void {
        this.width = width;
    }
}

class Square extends Rectangle {
    constructor(sideLength: number) {
        super(sideLength, sideLength);
    }
}

const square = new Square(5);
console.log(square.getArea());  // 25

square.setLength(10);
console.log(square.getArea());  // 50

Enter fullscreen mode Exit fullscreen mode

In this code, we have a Rectangle class with a getArea() method for calculating the area of the rectangle, as well as setter methods for modifying the length and width of the rectangle. We also have a Square class that inherits from Rectangle, but overrides the constructor to set the length and width to be equal (since they are equal in a square).

At first, when we create a Square object and call the getArea() method, it works as expected and returns the correct value. However, when we use the setLength() setter method to change the length of the square, it breaks the expected behavior of a square.

In a square, all sides are equal, so changing the length of the square should also change the width. However, because we are inheriting from a Rectangle class and not accurately representing the characteristics of a square, this does not happen. As a result, we end up with an incorrect value for the area.

A better approach would be to create a separate class for Square and define its own setter methods that ensure that all sides remain equal when one of them is changed. This way, we can avoid any bugs or unrealistic behavior in our code.

class Square {
    constructor(public sideLength: number) {}

    getArea(): number {
        return Math.pow(this.sideLength, 2);
    }

    setSideLength(sideLength: number): void {
        this.sideLength = sideLength;
    }
}

const square = new Square(5);
console.log(square.getArea());  // 25

square.setSideLength(10);
console.log(square.getArea());  // 100

Enter fullscreen mode Exit fullscreen mode

In this revised code, we have a separate Square class with its own setter method for changing the side length of the square. When we use this setter method to change the side length, it correctly updates all sides of the square and maintains the expected behavior.

Additionally, if we tried to support a square as a subclass of the rectangle, we would need to include a lot of conditional statements in the code to account for the square or override them in the class square and even then we would still end up with methods like setLength() and setHeight().

This would result in a lot of extra code and complexity, making it harder to understand and maintain the codebase. Instead, it is much simpler and more effective to create a separate class for Square. This way, we can clearly represent the unique characteristics of a square and avoid any unnecessary complexity in the code.

Finally

In some cases, such as with Santa Claus and Square, it’s better to ignore conceptions from real life in favor of doing what will work better for programming concepts.

So, take this time and do like Santa, checking your list of classes and separating them into nice and naughty classes, and then checking twice.

Happy holidays, and happy coding!


Cover Photo by Mike Arney on Unsplash

Top comments (4)

Collapse
 
alxgrk profile image
Alexander Girke

While I mostly agree with your conclusions and those from the comments (composition over inheritance; duplication is cheaper than the wrong abstraction; ...), I find the example with Rectangle & Square quite misleading.
Your Rectangle class clearly has a problem with the abstraction level: why should it offer methods like setWidth & setLength? Make it immutable (get rid of the setters) & be happy with not having to implement getArea twice. I think that's one of the strengths of OOP, so promoting to not use it feels odd to me.
Otherwise, thanks for making people think twice about this topic and happy holidays as well 😊

Collapse
 
noriller profile image
Bruno Noriller

I also don't like most examples and this one is no different.

I personally prefer to go functional and not deal with classes but you can't always avoid it, not to mention that sometimes you do need to open the way to resize it (or the real world equivalent to whatever class you're dealing with).
When you do... you might find yourself with problems, or a lot of ifs here and there or even basically overriding the whole class to fit the needs.

I'm not saying not to use it, but that sometimes you can't just assume programming and real life abstract things the same way.

Collapse
 
fyodorio profile image
Fyodor

what better way to get into the holiday spirit than with a technical blog post about object-oriented programming

That’s just belissimo dude, ROFL 🤣👍

On a serious note, that’s why composition is always better than inheritance 💪

Merry Christmas 🎄

Collapse
 
noriller profile image
Bruno Noriller

I mostly agree, but there are probably situations where you want/need to use inheritance.

But more than that, I'm thinking here it might fit better to what Sandy Metz said "duplication is far cheaper than the wrong abstraction" than about the composition vs inheritance problem. (or you know... at least it's somewhat related to that)

Happy Holidays!