Skip to content

Instantly share code, notes, and snippets.

@arthurstomp
Last active December 2, 2016 23:09
Show Gist options
  • Save arthurstomp/e5a3b7def5567d8b58f5a2690e839b6a to your computer and use it in GitHub Desktop.
Save arthurstomp/e5a3b7def5567d8b58f5a2690e839b6a to your computer and use it in GitHub Desktop.
Ruby Decorator Design Pattern

Ruby Decorator Design Pattern

While styding the Decorator Design Pattern i read there was a drawback on implementing the Decorator pattern using the module + extend + super implementation.

The drawbacks of this implementation are

  1. can not use the same decorator more than once on the same object

  2. difficult to tell which decorator added the functionality

Thinking about that i made the maesters example.

Maester Aemon

A maester is kind of scholars in the universe of Game of Thrones. They get rings of different metals on a chain the hang around their necks symbolizing their mastery. The maester.rb examples is a work around those drawback.

It works like this. The maester starts without any knowledge, any ring in his chain. Every time the maester study(extend) a Tome he increases his knowledge about the subject and get the correspondent ring on his chain. If he study more that subject he will increase his knowledge but he already got the ring so his rings will remain unchanged.

To implement that behavior using the chosen design pattern the Tome module defines the hook self.included to add another hook on the classes that include it. This method is the self.extended method. This second hook sets increases the knowledge of the maester (base class of the extended method) about the tome that is being extended.

class Maester
def initialize
@tomes = {}
@rings = []
end
def show_rings
@rings
end
def tomes
@tomes
end
end
module Tome
def self.included(base)
base.instance_eval do
def self.extended(base)
base.tomes[self.name.to_s] ||= 0
base.tomes[self.name.to_s] += 1
end
end
end
end
module MoneyTome
include Tome
def show_rings
super + ['gold']
end
end
module WarcraftTome
include Tome
def show_rings
super + ['iron']
end
end
maester = Maester.new
maester.extend MoneyTome
maester.extend MoneyTome
maester.extend WarcraftTome
p maester.show_rings # => ["gold", "iron"]
p maester.tomes # => {"MoneyTome"=>2, "WarcraftTome"=>1}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment