---
title: 'Building Reusable Object-Oriented Systems: Inheritance'
teaser: Using inheritance to share common logic.
tags: web,ruby,good code
author: Joël Quenneville
published_on: 2016-10-31
---

When building software, we often come across special cases, specializations, and
shared logic. In object-oriented languages, **inheritance** is commonly used to
deal with these.

## Building an API client

Let's say you are writing a client to interact with a third-party API that lists
movies. It might look like:

```ruby
module MovieFacts
  class Client
    def initialize(client_id, client_secret)
      @client_id = client_id
      @client_secret = client_secret
    end

    def directors
      fetch_data("/directors").map { |director| Director.new(director) }
    end

    def director(name)
      Director.new(fetch_data("/directors/#{name}"))
    end

    private

    def fetch_data
      # fetch directors from API
    end
  end
end
```

The client returns a lightweight `Director` object based on the JSON response.

```ruby
module MovieFacts
  class Director
    def initialize(json)
      @raw_data = JSON.parse(json)
    end

    def name
      @raw_data.fetch("name")
    end

    def id
      @raw_data.fetch("id")
    end
  end
end
```

## Inheritance

A new set of requirements come in. `movie-facts.com` is rate limiting your
service so you need to watch how many requests you make in a day. The good news
is that you know that `movie-facts.com` only updates its systems once a day so
it should be trivial to cache the data in memory and only fetch from the API
once a day. Not only does this fix your rate-limiting issues but it also speeds
up performance of your own system.

There are now two ways of doing the same task (and it's not too hard to imagine
others coming along down the road). Creating a separate class would result in a
lot of duplicated code.

![Duplicated code](https://images.thoughtbot.com/composition-versus-inheritance/LkiZjmzHQPiQaRbTMjYy_duplicate-code.png)

The intro to "Design Patterns in Ruby" describes an additional principle:

> Separate things that change from those that stay the same

The logic for returning the list of director objects remains the same, but the
way we fetch the data changes based on context. We need a way to separate the
fetching logic (which changes), from the director logic (which doesn't).

The most commonly used solution for this problem is inheritance. Inheritance is
a way of creating a specialized form of an object. Because of this, child
classes have access to all the methods and private state of their parent. They
_are_ their parent, with a few modifications.

![Extracting a base class](https://images.thoughtbot.com/composition-versus-inheritance/YdUg6I6JSjOYHhet3XYS_extract-base-class.png)

You start by extracting the shared logic into a `ClientBase` class

```ruby
module MovieFacts
  class ClientBase
    NotImplementedError = Class.new(StandardError)

    def initialize(client_id, client_secret)
      @client_id = client_id
      @client_secret = client_secret
    end

    def directors
      fetch_data("/directors").map { |director| Director.new(director) }
    end

    def director(name)
      Director.new(fetch_data("/directors/#{name}"))
    end

    private

    def fetch_data
      raise NotImplementedError
    end
  end
end
```

You then derive a class for handling fetches via HTTP:

```ruby
module MovieFacts
  class HttpClient < ClientBase

    private

    def fetch_data
      # make HTTP request
      # cache response
    end
  end
end
```

and one for fetches from cache.

```ruby
module MovieFacts
  class CacheClient < ClientBase

    private

    def fetch_data
      # read data from cache
    end
  end
end
```

Now if you ever want to add a third way to fetch the data in the future, all you
need to do is add a new sub-class. Great use of the [Open/Closed principle]!

[Open/Closed principle]: https://8thlight.com/blog/uncle-bob/2014/05/12/TheOpenClosedPrinciple.html

## Limitations of inheritance

Simple inheritance solved our duplicated logic problem. It also made it easy for
us to extend the system to fetch data from other sources if necessary. However,
it does have a few critical weaknesses when it comes to building reusable OO
components:

1. it is vulnerable to [combinatorial explosion] when there are multiple
   independent parts of the code that vary.
2. there is no encapsulation between parents and descendants.

We dig into how to solve this problems in the next posts on [modules] and
[composition].

[combinatorial explosion]: https://en.wikipedia.org/wiki/Combinatorial_explosion
[modules]: reusable-oo-modules
[composition]: reusable-oo-composition

## Further Reading

This article is part 1 of 4 in a series on building reusable object-oriented
software.

1. [Simple Inheritance (this article)](reusable-oo-inheritance)
1. [Mixins/Multiple Inheritance](reusable-oo-modules)
1. [Composition](reusable-oo-composition)
1. [Composition vs Inheritance](reusable-oo-composition-vs-inheritance)
