Part of why we want to separate code into separate classes and modules is that it keeps things flexible. For example, let's say that there is no GothamCrimeFigher class and there is only a Human class. If we wanted to include the UtilityBelt module in the human class we would be giving all humans utility belts. While this would be fantastic, it is unnecessary. Therefore, we abstract out the functionality that is not shared across all of humanity into a subclass that we can change as we see fit
When deciding whether to use a module vs a class it makes sense to consider the similarities of the classes that contain the functionality you're trying to extend
For example, Kryptonian and Human share traits and similarites. So we can abstract those things out into a superclass
class Person
def initialize
@consciousness = true
@reason = true
end
end
class Human < Person
attr_reader :home_planet
def initialize
super
@home_planet = 'Earth'
end
end
class Kryptonian < Person
def initialize
@home_planet = 'Krypton'
@powers = false
end
def yellow_sun
@powers = true
end
end
In the case of modules let's look at Batman's utility belt and the Batmobile.
class Batmobile
attr_reader :armaments
def initialize
@armaments
end
def load(item)
@armaments << item
end
def use(item)
@armaments.reject! { |slot| slot == item }
end
end
And of course the belt:
class UtilityBelt
attr_reader :pockets
def initialize
@pockets = []
end
def load(item)
@pockets << item
end
def use(item)
@pockets.reject! { |slot| slot == item }
end
end
These classes don't really share any similarities out side of their functionality. After all, the Batmobile is a car and the utility belt is a piece of clothing. They do, however, share functionality. We can abstract this functionality into a module.
module Loadable
def load(container, item)
container << item
end
def use(container, item)
container.reject! { |slot| slot == item }
end
end
In order to keep things flexible we have to create arguments for the respective collections that we're manipulating with the argument of container, but outside of that the functionality remains. Now we can include this module in our classes which will dry things up significantly
class UtilityBelt
attr_reader :pockets
include Loadable
def initialize
@pockets = []
end
end
class BatMobile
attr_reader :armaments
include Loadable
def initialize
@armaments = []
end
end
Now we can extend this functionality into anything that can be loaded and used! Huzzah!
Check the attatched ruby file to see the implementation.