⛵ + Object Composition =
Object-oriented programming can be a tough concept to grasp for a beginning programmer. Its usually the first major obstacle in your learn-to-code journey. Launchers are introduced to objects early in Ignition (our pre-work program), where we break the concept down into easily digestible parts.
What defines an object? How do we know when an object is too complex? Detemining the correct composition of an object and the circumstances that warrant splitting a concept into multiple objects is one of the fundamentals of object-oriented programming. At the core of object composition is the Single Responsibility Principle.
Single Responsibility Principle
How do we decide what belongs in a single class?
This question is answered by the Single Responsiblity Principle. Formally, the Single Responsibility Principle states that "a class should have one, and only one, reason to change".
In practice, this means that each class should only do one thing, and you should be able to explain the purpose of a class in a single sentence. So if you need to use the words "and" or "or" to describe the purpose of a class, that class probably has more than one purpose, and should be split into multiple classes.
Let's say we're trying to represent a canoe and an oil tanker in our application. Should this be one class, or two? If we represented these two objects as instances of a single class,Boat
, what would the purpose of this class be? "An object that transports people and oil across small and large bodies of water." Even though we're oversimplifying canoes and oil tankers quite a bit, the purpose that we present above still violates the Single Responsibility Principle. There are two ands, which is two too many.
What if we made these two separate classes? Then we might have:
Canoe
: An object that transports people across small bodies of water OilTanker
: An object that transports oil across large bodies of water
As two separate classes, Canoe
and OilTanker
each have a single responsibility.
Sailboats
Continuing with our boat theme, let's consider an object: Sailboats
. As you may know, sailboats are incredibly complex objects. Some attributes of a sailboat we might want to characterize are:
- Mainsail Type
- Mainsail Size
- Jib Type
- Jib Size
- Boat Length
class Sailboat
attr_reader :mainsail_type, :mainsail_size, :jib_type, :jib_size, :length
def initialize(mainsail_type, mainsail_size, jib_type, jib_size, length)
@mainsail_type = mainsail_type
@mainsail_size = mainsail_size
@jib_type = jib_type
@jib_size = jib_size
@length = length
end
end
In this case, however, the Sailboat
class is taking responsibility for not just properties of the sailboat (i.e., length
), but is also in charge of attributes of the mainsail and jib as well (i.e., type, size). From an organizational standpoint, this may seem a little messy, but still manageable. What if we consider adding another attribute, like color, to the mainsail and jib? Now our Sailboat
class would look like so:
class Sailboat
attr_reader :mainsail_type, :mainsail_size, :mainsail_color, :jib_type, :jib_size, :jib_color, :length
def initialize(mainsail_type, mainsail_size, mainsail_color, jib_type, jib_size, jib_color, length)
@mainsail_type = mainsail_type
@mainsail_size = mainsail_size
@mainsail_color = mainsail_color
@jib_type = jib_type
@jib_size = jib_size
@jib_color = jib_color
@length = length
end
end
The Sailboat
class is now burdened with taking in attributes about two sails: the mainsail and the jib, that technically have nothing to do with the Sailboat
itself. The Sailboat
class simply cares that there is a mainsail and a jib - but should it be concerned with the color and type of these sails?
There must be a better way!