views:

34

answers:

2

Let's say I have a package called 'animal' including Animal parent class, Cat extends from Animal, Dog extends from Animal, also. Animal, however, is designed like this:

class Animal {
  int amount;
  Animal next; // Then a constructor initializes these.

  drinkWater(int n) { ... }
}

Cat & Dog classes follow this structure:

class Cat extends Animal {
  Cat(int amount, Animal next) {
    super(amount, next);
  }

  @Override
  drinkWater(int n) { .. }
}

Each of them has the method, drinkWater() like this:

public void drinkWwater(int n) {
  amount -= n;
  if (amount < 0) amount = 0;
  if (next != null) next.drinkWater(n);
}

What I am trying to do now here is I created a 'linked list' of animals, each of them drinking water in sequence. However, let's say, if a cat drinks n amount of water, it passes n+1 amount of water to its.next

My purpose is finding a solution to overcome the problem that 'don't touch the original animal package, but change behavior of drinking water each one of them'. I have come with that 'famous' naive solution with a class:

class InvokeStaticTypeBDrink {
  static void typeBdrink(Animal animal, int n) {
    animal.amount -= n;
    if (animal.amount < 0) animal.amount = 0;
    if (animal.next != null) {
      if (animal instanceof Cat)
        InvokeStaticTypeDrink.drinkWater(animal.next, n+1);
      else if (animal instanceof Dog)
        InvokeStaticTypeDrink.drinkWater(animal.next, n-1);
      else
        InvokeStaticTypeDrink.drinkWater(animal.next, n);
    }
  }
}

Then, I started to research. Because this really looked quick and dirty solution.

So, I found that design pattern called 'Visitor Pattern'. Well, pretty cool pattern which solves the problem with double dispatch, but there is a problem on my side: Visitable interface (which declares accept() method) should be 'implement'ed by the original Animals. However, my goal is 'do NOT to do any modification on original animal package, but do change the drinking water behavior'. I am pretty sure I am missing something.

So, do you think with a little bit hack, Visitor Pattern would still work or another pattern/solution would be better? Thanks.

+2  A: 

If you do not want to touch the original classes, then the only way to apply visitor pattern would be to wrap the original classes inside new (wrapper) classes.

Anyway, if you just want to change the behavior of some animals, then in your situation I would just extend those specific classes and override the drinking behavior.

Then you would have a cat like this:

class NonThirstyCat extends Cat {
  Cat(int amount, Animal next) {
    super(amount, next);
  }

  @Override
  public void drinkWater(int n) {
    amount += n;
    if (amount < 0) amount = 0;
    if (next != null) next.drinkWater(n);
  }
}
Neeme Praks
A: 

I suppose subclassing will not help in your case.

The visitor pattern would be good but it does not work without modifying Animal. I have two suggestions. Actually, I have three:

  1. Don't do it. Rethink your problem. It looks like bad design to me and propably breaks every principle of OOP.
  2. Use AOP. Google for AspectJ.

Or (3) try something like this:

class FixedAnimal extends Animal {
    public static Animal fix(Animal a) {
        Animal result = a;
        if (a instanceof Cat || a instanceof Dog)
            result = new FixedAnimal(a);
        if (a.next != null) a.next = fix(a.next);
        return result;
    }
    Animal a;
    FixedAnimal(Animal a) { 
        super(0, null); 
        this.a = a;
    }
    public void drink(int n) {
        // do stuff
        if (a.next != null) a.next.drink(n);
    }
}

Of course this makes some assumptions about the usage of Animal, but maybe you get the idea.

My recommendation would be #1. Or be more specific about what you want to achieve.

Arian