Sonntag, 17. August 2014

Let it walk like a duck!

Please, do not let the title irritate you. It is just an controversial introduction to duck typing in Ruby, backed by the well-know saying:
"When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck."
Duck types are public interfaces that are not tied to any specific class. They are about the object's methods and properties, rather that its inheritance from a certain class. In a nutshell:
Behaviour over class membership
Following that pattern helps to improve:
  1. readability (less code for all message sender)
  2. add enormous flexibility
  3. reduces class coupling and therefore increases maintainability
The following original code contains 3 classes, each offering one getter method (Ingredient#name, Food#brand and Product#brand) returning a String.
class Drug
  attr_accessor :dope, :brand
end

class Food
  attr_accessor :ingredients
  def labeling
    ingredients.map { |ingredient| ingredient.name }.join(', ')
  end
end

class Clothing
  attr_accessor :name
end
and a Bill class for printing out:
class Bill
  def print_name product
    return puts "#{product.brand} (#{product.dope})" if product.respond_to? :dope
    return puts product.labeling if product.respond_to? :labeling
    puts product.name
  end
end
The Bill#print_name can be refactored by also duck typing the 3 classes:
class Drug
  attr_accessor :dope, :brand
  def name
    "#{product.brand} (#{product.dope})"
  end
end

class Food
  attr_accessor :ingredients
  def labeling
    ingredients.map { |ingredient| ingredient.name }.join(', ')
  end
  alias_method :name, :labeling
end

class Clothing
  attr_accessor :name
end
All 3 classes now respond to name and can called in Bill#print_name like:
class Bill
  def print_name product
    puts product.name
  end
end
Further articles of interest:

Supported by Ruby 2.1.1

Keine Kommentare:

Kommentar veröffentlichen