Skip to content

Instantly share code, notes, and snippets.

@sgharms
Created October 4, 2012 16:11
Show Gist options
  • Save sgharms/3834658 to your computer and use it in GitHub Desktop.
Save sgharms/3834658 to your computer and use it in GitHub Desktop.
Delegators and composition in Ruby
class Kitchen
def prepare_hamburger
return ( %w/ artisinal_bun niman_ranch_beef cowgirl_creamery_bleu_cheese lettuce central_valley_avocado/.
join ', ' ) + " hamburger."
end
def generate_mixin
Module.new do
def prepare_hamburger
puts "Enjoy a: " + @kitchen.prepare_hamburger
end
end
end
end
class Restaurant
def initialize(name)
@name = name
@kitchen = Kitchen.new
self.extend @kitchen.generate_mixin
end
end
my_resto = Restaurant.new "The Stinky Cow"
my_resto.prepare_hamburger
#!/usr/bin/env ruby
#
# The Goal: Use composition effectively. A Restaurant purports to "prepare a hamburger," but we
# know that it is actually the responsibility of the kitchen to do the work.
#
# Key feature: the token used for the action is the SAME between the two entitites `prepare_hamburger`.
#
# Desired: A way to have Restaurant delegate :prepare_hamburger to Kitchen **AND**
# **do not** define the wrapper :prepare_hamburger on Restaurant.
class Kitchen
def prepare_hamburger
return ( %w/ artisinal_bun niman_ranch_beef cowgirl_creamery_bleu_cheese lettuce central_valley_avocado/.
join ', ' ) + " hamburger."
end
end
class Restaurant
def initialize(name)
@name = name
@kitchen = Kitchen.new
end
def prepare_hamburger
puts "Enjoy a: " + @kitchen.prepare_hamburger
end
end
my_resto = Restaurant.new "The Stinky Cow"
my_resto.prepare_hamburger
@loveybot
Copy link

loveybot commented Oct 4, 2012

Yay Ruby!!

@sgharms
Copy link
Author

sgharms commented Oct 4, 2012

class Kitchen
def prepare_hamburger
return ( %w/ artisinal_bun niman_ranch_beef cowgirl_creamery_bleu_cheese lettuce central_valley_avocado/.
join ', ' ) + " hamburger."
end

def generate_mixin
Module.new do
def prepare_hamburger
puts "Enjoy a: " + @kitchen.prepare_hamburger
end
end
end
end

class Restaurant
def initialize(name)
@name = name
@kitchen = Kitchen.new
self.extend @kitchen.generate_mixin
end
end

my_resto = Restaurant.new "The Stinky Cow"
my_resto.prepare_hamburger

@sgharms
Copy link
Author

sgharms commented Oct 4, 2012

The first is simple and plain. It feels like needless work to write those generator methods.

The second feels a bit more like Ruby Metaprogramming style, but the down side is that the Kitchen class has to know that it was instantiated on the @kitchen iVar. That doesn't seem right.

I'm looking for a better implementation.

@sgharms
Copy link
Author

sgharms commented Oct 4, 2012

How the hell do you delete a gist comment (see 2 up).

@JEG2
Copy link

JEG2 commented Oct 4, 2012

I think it's hard for me to make suggestions about this code, because I'm not totally sure I understand the point. I think I need something a little less abstract. For example, Kitchen is an object with no state, so why not just make it a module?

I don't like the mix-in trick. It seems like a lot of magic for a gain I don't really understand.

I see about one change I am certain sure I would make for this code:

class Kitchen
  INGREDIENTS = %w[ artisinal_bun
                    niman_ranch_beef
                    cowgirl_creamery_bleu_cheese
                    lettuce
                    central_valley_avocado ]

  def prepare_hamburger
    "#{INGREDIENTS.join(', ')} hamburger."
  end
end

class Restaurant
  def initialize(name, kitchen = Kitchen)  # the important change!
    @name    = name
    @kitchen = kitchen.new
  end

  def prepare_hamburger
    puts "Enjoy a: " + @kitchen.prepare_hamburger
  end
end

my_resto = Restaurant.new("The Stinky Cow")
my_resto.prepare_hamburger

To me, this is key because it lets me test Restaurant's delegation, wrap Kitchen before I pass it in, or out and out replace it with a similar interface. That feels like all I need here.

Perhaps I didn't understand the question well enough though.

@steveklabnik
Copy link

If your goal is to use composition effectively, and then you call #extend, you've failed, because mixins are inheritance, not composition. ;)

That said, I sorta agree with James; I'm also not sure what the point is... :/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment