The Artima Developer Community
Sponsored Link

Ruby Buzz Forum
Mark IV Coffee Maker on Dissident

0 replies on 1 page.

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 0 replies on 1 page
Christian Neukirchen

Posts: 188
Nickname: chris2
Registered: Mar, 2005

Christian Neukirchen is a student from Biberach, Germany playing and hacking with Ruby.
Mark IV Coffee Maker on Dissident Posted: Aug 31, 2005 5:13 AM
Reply to this message Reply

This post originated from an RSS feed registered with Ruby Buzz by Christian Neukirchen.
Original Post: Mark IV Coffee Maker on Dissident
Feed Title: chris blogs: Ruby stuff
Feed URL: http://chneukirchen.org/blog/category/ruby.atom
Feed Description: a weblog by christian neukirchen - Ruby stuff
Latest Ruby Buzz Posts
Latest Ruby Buzz Posts by Christian Neukirchen
Latest Posts From chris blogs: Ruby stuff

Advertisement

Jim Weirich uses an excellent example for introducing Dependency Injection in his OSCON 2005 talk, the Mark IV Coffee Maker. I’m not going to introduce it here except for the API, if you did not read his slides yet (or even attended?), you are better off if you do that now.

Basically, you have three classes that need to know of each other: PotSensor, Heater and Warmer. You want to instantiate a Warmer.

class PotSensor
  def coffee_present?; ...; end
end

class Heater
  def on; ...; end
  def off; ...; end
end

class Warmer
  def trigger
    if pot_sensor.coffee_present?
      heater.on
    else
      heater.off
    end
  end
end

Jim uses his own container, Dependency Injection/Minimal in the talk, and makes use of Constructor Injection. It looks like this when using DIM (example sightly simplified for blogging purposes):

dim = DIM::Container.new

dim.register(:pot_sensor_io_port) { 0x08F0 }
dim.register(:warmer_heater_io_port) { 0x08F1 }

dim.register(:pot_sensor) { |c| PotSensor.new(c.pot_sensor_io_port) }
dim.register(:warmer_heater) { |c| Heater.new(c.warmer_heater_io_port) }

dim.register(:warmer) { |c|
  Warmer.new(c.pot_sensor, c.warmer_heater)
}

dim.warmer

If we wanted to use Constructor Injection too in Dissident, we could go like this:

class MarkIVConfiguration < Dissident::Container
  def pot_sensor_io_port; 0x08F0; end
  def warmer_heater_io_port; 0x08F1; end

  def pot_sensor
    PotSensor.new pot_sensor_io_port
  end

  def warmer_heater
    Heater.new warmer_heater_io_port
  end

  def warmer
    Warmer.new(container.pot_sensor, container.warmer_heater)
  end
end

(Assuming container is self, this is even valid and working Ruby code without using Dissident.) At least you would write it that way some iterations ago. Recently, Dissident learnt of some useful constructs, however, that simplify above greatly. We now can write:

class MarkIVConfiguration < Dissident::Container
  constant :pot_sensor_io_port, 0x08F0
  constant :warmer_heater_io_port, 0x08F1

  provide :pot_sensor, PotSensor, :pot_sensor_io_port
  provide :warmer_heater, Heater, :warmer_heater_io_port
  provide :warmer, Warmer, :pot_sensor, :warmer_heater
end

I don’t think Constructor Injection can be more straight-forward in a language that doesn’t allow for argument name (or “type”) reflection. We instantiate the warmer by setting up the container and calling:

Dissident.with MarkIVConfiguration do |c|
  c.warmer
end

Now, let’s do the same with Setter Injection in Dissident.

class PotSensor
  inject :pot_sensor_io_port
end

class Warmer
  inject :pot_sensor
  inject :warmer_heater
end

class MarkIVConfiguration < Dissident::Container
  constant :pot_sensor_io_port, 0x08F0
  constant :warmer_heater_io_port, 0x08F1

  provide :pot_sensor, PotSensor
  provide :warmer_heater, Heater, :warmer_heater_io_port
end

Note how Setter and Constructor Injection can be mixed. (PotSensor uses Setter Injection, Heater gets its port via Constructor Injection.) Now, watch how we instantiate that:

Dissident.with MarkIVConfiguration do
  Warmer.new
end

As you can see, this looks like “ordinary” Ruby, as if DI was not used at all. It may not make a big difference if you see the whole code at once, but in my experience distributing the injections to the classes that need it greatly simplifies coding because things are where they are related to.

Also, see how easy we can mock, say, for testing:

class MarkIVTestConfiguration < MarkIVConfiguration
  provide :pot_sensor, MockPotSensor
  provide :warmer_heater, MockWarmerHeater
end

Dissident.with MarkIVTestConfiguration do
  Warmer.new
end

Dissident is now in a state where I think I can make the general public have a look at the code, so you are welcome to get it at the darcs repository (or just browse the code there). I hope to make a proper release really soon now.

NP: Alanis Morissette—You Oughta Know

Read: Mark IV Coffee Maker on Dissident

Topic: HtmlClipping 0.1.0 Previous Topic   Next Topic Topic: Java Rehabilitation Clinic

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use