Daniel Pitts’ Tech Blog

Posts Tagged ‘polymorphism’

Using enums as a flyweight pattern.

Monday, October 22nd, 2007

When the Java people introduced enums, they went out of their way to support the switch statement with enums. Good OO design tends to avoid switches and instead use polymorphism to “decide” on behavior. While you can switch on enums, you also can add behavior to enums. As a matter of fact, this is useful for the flyweight pattern.

First, for those who don’t know, a Flyweight is a stateless object that represents some behavior for a set of other objects. For example, if you had to have an ‘e’ Glyph”object for every letter ‘e’ in this article, you’d have a lot of instances of the e glyph object. Using a flyweight, you have one instance of the ‘e’ Glyph, and whenever you need to evoke the behavior, you pass in the “state” (eg. the position on the screen) to the appropriate method.

For small sets of Flyweights, where the difference between any two instances is mostly behaviorally, enums are very useful. They wouldn’t be so useful for the ‘e’ Glyph example, since we’d have to write an enum with every character we wished to support. Glyphs also mostly differs in the shape, which can be broken down into data rather than behavior.

So, for our example, we’ll be using a Flyweight to mimic animals. Lets start out with our different types of animals.

public enum AnimalType {
    Cow,
    Chicken,
    Pig,
    Lion
}

Now, if we wanted to have an Animal class. Lets assume that we have an “World” class that contains information about our animal’s world. Including food sources and animal position, etc…

public final class Animal {
   private final AnimalType type;
   private final World world = World.getWorld();
   private int hungerLevel;
   public Animal(AnimalType type) {
      this.type = type;
   }
}

Now, objects aren’t generally useful unless they have behavior. We’re going to add simple delegation from the Animal class to its AnimalType instance.

public final class Animal {
   private final AnimalType type;
   private final World world = World.getWorld();
   private int hungerLevel;
   private int restLevel;
   public Animal(AnimalType type) {
      this.type = type;
   }

   public void eatFood() { type.eatFood(this);}

   public void sleep() {
     type.sleep(this);
   }

   public World world() { return world; }
}

public enum AnimalType {
    Cow {
        public void eatFood(Animal animal) {
            animal.world().findGrass().consumeBy(animal);
        }
        public void sleep(Animal animal) {
            animal.world().findBarn().sleptInBy(animal);
        }
    },
    Chicken{
        public void eatFood(Animal animal) {
            animal.world().findCorn().consumeBy(animal);
        }
        public void sleep(Animal animal) {
            animal.world().findCoop().sleptInBy(animal);
        }
    },
    Pig{
        public void eatFood(Animal animal) {
            animal.world().findTrough().getContents().consumeBy(animal);
        }
        public void sleep(Animal animal) {
            animal.world().findBarn().sleptInBy(animal);
        }
    },
    Lion {
        public void eatFood(Animal animal) {
            animal.world().findPrey().consumeBy(animal);
        }
        public void sleep(Animal animal) {
            animal.world().findSavana().sleptInBy(animal);
        }
    },
;
    public abstract void eatFood(Animal animal);
    public abstract void sleep(Animal animal);
}

So, as you can see, each animal has its own behavior. This particular flyweight implements the Strategy pattern for both eatFood() and sleep().