Shakin’ Class: Pianos in Different Area Codes

Inheritance and composition in Ruby

Shakin’ Class: Pianos in Different Area Codes

Shakin’ Class: Pianos in Different Area Codes 150 150 Clark

There are a lot of ways to model objects in our world – a globe to represent earth, a popsicle-stick art project to model a real building, a drawing to represent statue, a mathematical equation to model a beehive, a Minkowski spacetime diagram to model space and time, ad infinitum. One of the popular ways that scientists (computer, biological, etc.) model objects in reality is a Class-based system.

Think of the taxonomies in biology (Life > Domain > Kingdom > Phylum … Family > Genus > Species), or the ‘basic’ types in most programming languages (String, Array, Number, etc.) – these taxonomies, and many more, tend to be built around class hierarchies. All species are instantiations (objects) of the Species class, which is a subclass of the Life class. That just means that all species share features common to all other species, as well as common to all other life.

Classes are are used in Object Oriented Programming Languages (OOP) to organize the types of data and behavior of data throughout a program. A class is basically the description of a type – an object is just an entity of a certain type, or an “instantiation” of a class. For example, since I will be using pianos throughout this post:

Class: Piano

Object: a 110 year old Chickering Grand Piano

If I were trying to create a computer program, let’s say a web application for a piano warehouse and reseller, then I might want to show a bunch of different pianos in the webapp with the pianos’ respective information. In this case, there might be a bunch of different pianos, all different brands, sounds, woods, colors, etc. All of these pianos are pianos! Or, less redundantly on the surface, all of these piano objects are of the class Piano.

I could write a Piano class that has a description of what all piano objects will share – some basic data and basic behavior. Then, when I want to show a piano in the webapp, I can instantiate an object of the Piano class – that piano object will then be available for use with all the features and behavior of a piano.

I could choose to play the piano, buy the piano, sell the piano, etc., all within my program if I decide to give the Piano class those abilities.

Now that we have a general idea of what a class is, let’s discuss when to use a class in a computer program.


When to Use a Class

Classes are fairly magical for inheritance, encapsulation, reuse, and other goodies – so, classes get heavily overused, especially by n00bs. Far from being a panacea, classes can be used with care and in the proper situations. There are many times when a module might be better for inheritance, encapsulation, and reuse. Depending on the language (our examples and discussions will center around Ruby, primarily), there might be a better tool for the job than a class.

A class should be used if a program entity is a type of thing that shares other important characteristics with other program entities. But that is not enough – a class should only be used if the shared characteristics are because the entities are all of the same type, or all stand in a is-a relationship with a type – e.g., I am a person, a person is a sentient being, a sentient being is a living organism, etc.

If your shared charactersitics are reusable but not because of a ‘is-a’ relationship, then you should probably using a different programming pattern than class-based inheritance.

If you are familiar with any semantic web technologies, you might recognize this ‘is-a’ relationship vs. other relationships distinction. The semantic web folksonomies like RDF and OWL use class-based (is-a) relationships to structure data, while the formal semantic ontologies like SUMO and MILO use more granular relationships to structure data. Because of this difference, the formal ontologies are much more expressive than pure class (is-a) ontological hierarchies. Likewise, there are many ways to modularize and share features in a computer program that are beyond the capabilities of pure is-a inheritance hierarchies, i.e., pure class-based inheritance.

So, say that you have a clear ‘is-a’ relationship between objects that you want to model in a computer program – you sell pianos; you have a ton of different pianos sitting in a showroom; all of these pianos share certain types of features like price, wood, key material, action material, age, brand, etc.; you want a webapp where customers can browse and compare all of the different pianos. Would it make sense to build a custom software object from scratch for each piano, or would it make more sense to build a Piano software blueprint that could be reused for all of the different pianos by just filling in the individual characteristics for each piano without having to rebuild the outline from scratch? Probably the latter – that is, use a class.

Each piano in the storeroom (and webapp) is a piano, meaning that all of the individual piano objects can be modeled as instantiations of a Piano class. How, you ask? Gently, as follows.


How to Use a Class … in Ruby

Creating a class in the Ruby language is fairly simple – all it requires is a class keyword, a class name, and an end keyword to close the class.

    class Piano
    end

That simple code above would allow me to then create piano objects of the class Piano, like so:

    piano1 = Piano.new

Which would return a piano object similar to #<’Piano:0x0000010129bda8’>, which I could then use in my webapp to do things like display the piano object id, store the piano object in a database, and more.

If I wanted to actually add data and behavior to all of our pianos, I could add code within the class and end keywords. Let’s try that now, with a basic Piano class example.


Piano Class Example

    class Piano
      def initialize(brand, age, price, keys, size, soundboard_wood)
        @brand = brand
        @age = age
        @price = price
        @keys = keys
        @size = size
        @wood = soundboard_wood
      end

      def piano_desc
        puts "The #{@age} year old #{@size} #{@brand} piano currently costs #{@price} U.S. dollars. The piano's soundboard is made of #{@wood} and has #{@keys} keys."
      end
    end

Now, create some pianos!

    my_piano       = Piano.new("Chickering", 110, "N/A", "ivory-ebony",
                               "full-grand", "spruce")
    elt_john_piano = Piano.new("Yamaha", 4, "1,000,000", "ivory-ebony",
                               "full-grand", "spruce")

Now we can call the piano_desc method on each of the pianos to see a description of the pianos.

    my_piano.piano_desc
    elt_john_piano.piano_desc

Those two method calls would print out the following:

The 110 year old full-grand Chickering piano currently costs N/A U.S. dollars. The piano’s soundboard is made of spruce and has ivory-ebony keys.

The 4 year old full-grand Yamaha piano currently costs 1,000,000 U.S. dollars. The piano’s soundboard is made of spruce and has ivory-ebony keys.

Obviously these examples were highly simplified and contrived; however, I hope that the high-level concepts surrounding classes and objects were made more clear than prior to reading this post.

Though fairly simple to use, classes are very powerful. Use with care, and only use when you are trying to model an is-a relationship between a bunch of objects and a shared type. When those prerequisites are met, classes will unlock powerful gains from inheritance, encapsulation, and reusability.

WIP

Another Explanation of Composition and Inheritance … ugh

There are a lot of articles on the web covering the differences between inheritance and composition in software design – when to use one or the other, how inheritance is the devil, blah blah bliggityblah. I am writing this piece to solidify my own understanding on the topic, so I might take some liberties with my presentation. Also, all examples will be using Ruby, though most of the concepts are programming-language-agnostic. Allons-y!


Main Motivation: Laziness

This isn’t a new point – programmers are notoriously lazy (in some sense). They don’t like to do more work than they necessary to solve a given problem. They don’t like to repeat tedious tasks that could be automated with code. They don’t like to have to maintain messy codebases that could be DRYed up. So, software developers tend to reuse code. Reusable code is produced by using inheritance and composition (correctly). et Voil&agrave – inheritance and composition motivated.


Inheritance

Inheritance in software tends to refer to using ‘parent’ and ‘child’ relationships between classes to share functionality between the parent and children classes. In Ruby, a contrived example might look like this:

    class Parent
      def family_history
        puts "PARENT family history"
      end
    end

    class Child < Parent
    end

    gbush = Parent.new
    gwbush = Child.new

    gbush.family_history # => "PARENT family history"
    gwbush.family_history # => "PARENT family history"

First, a parent class is created. Some functionality or behavior is added to that parent class (in this case, printing the family history of the parent). Next, a child class is created with Child < Parent. The ‘<’ is all that is needed to ‘connect’ the parent and child classes in an inheritance relationship.

Now we create a parent object and a child object. Finally, we can call #family_history on both the parent and child object even though the child class never explicitly defined a #family_history method. Both the parent and child return the parent’s family history when #family_history is called.

This contrived example showed how you can use inheritance to share behavior between two classes. This example only made mild sense because a child shares her parents’ family history – there is a very tight relationship in that a child’s family history just is her parents’ family history because of the relationship between parents and children.

However, usually you don’t want to use inheritance unless you require your parent to maintain its own state. Let’s modify the example to show a case where inheritance would make even more sense because the parent is storing a last name in an instance variable.

    class Parent
      attr_accessor :last_name

      def initialize(last_name)
        @last_name = last_name
      end

      def family_history
        puts "PARENT family history"
      end
    end

    class Child < Parent
    end

    roger = Parent.new("Feusier")
    jessica = Child.new("Feusier")

    roger.last_name # => "Feusier"
    jessica.last_name # => "Feusier"

    jessica.last_name = "Lex"

    roger.last_name # => "Feusier"
    jessica.last_name # => "Lex"

As you can see, the parent now initializes with a last name, as does the child. It makes sense to use inheritance here because the child usually shares a last name with a parent for a signficant part of his life. The beauty of this inheritance pattern is the reusability of the parent’s family history and last name for children with the ability for the child to change his last name if needed, e.g., line 22 where jessica’s last name is changed.

Now that we have a grasp of inheritance, let’s be honest. These simplistic examples tend to give beginners (as myself) an overly romanticized picture of inheritance. Modeling the real-world as necessary for production software is never this clean. The real-world is made up of a lot of has-a relationships between objects and only a few is-a relationships. There are a lot more cases in the world like this: I am a human. I have a brain. My dog is a dog. My dog has a brain. Should my dog and I stand in an inheritance relation to some direct parent just because we both share the functionality of having a brain? Probably not. It makes more sense to model this case as a scenario where my dog and I are both composed of brains (as well as main other things), i.e., we both have brains. BOOM – composition re-motivated.


Composition

You can compose an object out of other objects (behavior or state). This can be done using class-based composition or module-based composition. I will focus on the latter because it is far more common to see composition via modules. Let’s say that I am trying to model a car class so that I can create car objects for my dealership database.

I will be using at least one class – the Car class. But, instead of making that Car class inherit functionality from a parent class like a Vehicle class, I can use modules to replace the parent classes. If we want a car object to have 4 wheels, then we can compose the car out of a module that replicates “has 4 wheels” behavior.

    module FourWheels

      def self.four_wheels
        puts "4 WHEELS WOOHOO!"
      end

    end

    class Car

      def four_wheels
        FourWheels.four_wheels
      end

    end

    car = Car.new

    car.four_wheels # => "4 WHEELS WOOHOO!"

The beauty of composition via modules and mixins is that you can abstract your code to handle more cases while still keeping your code organized and decoupled. For example, instead of the above code, we could do the following:

    module Wheels

      def self.wheels(num_wheels)
        puts "#{num_wheels} WHEELS WOOHOO!"
      end

    end

    class Car

      def initialize(wheels)
        @wheels = wheels
      end

      def wheels
        Wheels.wheels(@wheels)
      end

    end

    class Tricycle

      def initialize(wheels)
        @wheels = wheels
      end

      def wheels
        Wheels.wheels(@wheels)
      end

    end

    car = Car.new(4)
    tricycle = Tricycle.new(3)

    car.wheels # => "4 WHEELS WOOHOO!"
    tricycle.wheels # => "3 WHEELS WOOHOO!"

Obviously the example is way overkill for just creating objects with a number of wheels 🙂 But, it does show how you can easily use composition to abstract away from concrete cases, increasing reusability and maintainability (usually).


I will leave you with the following ‘practical’ method for deciding to use inheritance or composition.

“The choice of which is better … depends on if you need to maintain state in each function call in the reusable portion of your code” (source).

If you need the reusable part of your code (the parent class or the module to mixin) to store and persist its own state, then you should probably use inheritance. If you can store state in the ‘child’ part of your code, then you should use composition. For example, the Wheels module doesn’t need to store how many of ‘itself’ is required within itself, that information is provided by each of the classes that mixin the Wheels module. That being said, more information and context will be required for actually deciding which design pattern to use in your software. Good luck!