Skip to content

Instantly share code, notes, and snippets.

@yhordi
Last active August 29, 2015 14:26
Show Gist options
  • Save yhordi/daad75fb7e3351f2d06e to your computer and use it in GitHub Desktop.
Save yhordi/daad75fb7e3351f2d06e to your computer and use it in GitHub Desktop.
refactored code from my "Applying Object Orientation"

Flexibility

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

Modules vs Classes

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.

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
class MartialArt
attr_reader :name
def initialize(name)
@name = name
end
end
module Loadable
def load(container, item)
container << item
end
def use(container, item)
container.reject! { |slot| slot == item }
end
end
class UtilityBelt
attr_reader :pockets
include Loadable
def initialize
@pockets = []
end
end
class BatMobile
attr_reader :armaments
include Loadable
def initialize
@armaments = []
end
end
class GothamCrimeFighter < Human
attr_reader :martial_arts, :car, :belt
def initialize(martial_arts)
super()
@martial_arts = martial_arts
@car = nil
@belt = UtilityBelt.new
end
def show_martial_arts
@martial_arts.each do |skill|
p skill.name
end
end
def drive
@car = BatMobile.new
end
end
superman = Kryptonian.new
superman.yellow_sun
supergirl = Kryptonian.new
supergirl.yellow_sun
batgirl = GothamCrimeFighter.new([MartialArt.new('Tae Kwon Do'), MartialArt.new('Muay Thai')])
batman = GothamCrimeFighter.new([MartialArt.new('Karate'), MartialArt.new('Kung Fu'), MartialArt.new('Escrima')])
p batman.car == nil
batman.drive
p batman.car.is_a?(BatMobile) == true
batman.car.load(batman.car.armaments, 'Tow Cable')
p batman.car.armaments.include?('Tow Cable') == true
batman.belt.load(batman.belt.pockets, 'Smoke Grenade')
p batman.belt.pockets.include?('Smoke Grenade') == true
batman.belt.use(batman.belt.pockets, 'Smoke Grenade')
p batman.belt.pockets.include?('Smoke Grenade') == false
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment