Home > Articles > Programming > Ruby

  • Print
  • + Share This
This chapter is from the book

This chapter is from the book

Bundles of Object Creation

Bundles of Object Creation

One problem with our new Habitat class is that it is possible to create incoherent (not to mention ecologically unsound) combinations of fauna and flora. For instance, nothing in our current habitat implementation tells us that tigers and lily pads do not go together:

unstable = Habitat.new( 2, Tiger, 4, WaterLily)

This may not seem like much of a problem when you are dealing with just two kinds of things (plants and animals, in this case), but what if our simulation was much more detailed, extending to insects and birds and mollusks and fungi? We certainly don’t want any mushrooms growing on our lily pads or fish floundering away in the boughs of some jungle tree.

We can deal with this problem by changing the way we specify which creatures live in the habitat. Instead of passing the individual plant and animal classes to Habitat, we can pass a single object that knows how to create a consistent set of products. We will have one version of this object for ponds, a version that will create frogs and lily pads. We will have a second version of this object that will create the tigers and trees that are appropriate to a jungle. An object dedicated to creating a compatible set of objects is called an abstract factory. In fact, the Abstract Factory pattern is yet another of those patterns made famous by the GoF. The code below shows two abstract factories for our habitat simulation, one for the jungle and one for the pond:

class PondOrganismFactory
  def new_animal(name)
    Frog.new(name)
  end

  def new_plant(name)
    Algae.new(name)
  end
end

class JungleOrganismFactory
  def new_animal(name)
    Tiger.new(name)
  end

  def new_plant(name)
    Tree.new(name)
  end
end

After a few simple modifications, our Habitat initialize method is ready to begin using the abstract factory:

class Habitat
  def initialize(number_animals, number_plants, organism_factory)
    @organism_factory = organism_factory

    @animals = []
    number_animals.times do |i|
      animal = @organism_factory.new_animal("Animal#{i}")
      @animals << animal
    end

    @plants = []
    number_plants.times do |i|
      plant  = @organism_factory.new_plant("Plant#{i}")
      @plants << plant
    end
  end

  # Rest of the class...

We can now feed different abstract factories to our habitat, serene in the knowledge that there will be no unholy mixing of pond creatures with jungle denizens:

jungle = Habitat.new(1, 4, JungleOrganismFactory.new)
jungle.simulate_one_day

pond = Habitat.new( 2, 4, PondOrganismFactory.new)
pond.simulate_one_day

Figure 13-2 shows the UML diagram for the Abstract Factory pattern. Here we have two concrete factories, each of which produces its own set of compatible products.

Figure 13-2 The Abstract Factory pattern

The Abstract Factory pattern really boils down to a problem and a solution. The problem is that you need to create sets of compatible objects. The solution is that you write a separate class to handle that creation. In the same way that the Factory Method pattern is really the Template Method pattern applied to object creation, so the Abstract Factory pattern is simply the Strategy pattern applied to the same problem.

  • + Share This
  • 🔖 Save To Your Account