views:

547

answers:

13

I have two classes; let's call them Ogre and Wizard. (All fields are public to make the example easier to type in.)

public class Ogre
{
  int weight;
  int height;
  int axeLength;
}

public class Wizard
{
  int age;
  int IQ;
  int height;
}

In each class I can create a method called, say, battle() that will determine who will win if an Ogre meets and Ogre or a Wizard meets a Wizard. Here's an example. If an Ogre meets an Ogre, the heavier one wins. But if the weight is the same, the one with the longer axe wins.

public Ogre battle(Ogre o)
{
  if (this.height > o.height) return this;
  else if (this.height < o.height) return o;
  else if (this.axeLength > o.axeLength) return this;
  else if (this.axeLength < o.axeLength) return o;
  else return this;    // default case
}

We can make a similar method for Wizards.

But what if a Wizard meets an Ogre? We could of course make a method for that, comparing, say, just the heights.

public Wizard battle(Ogre o)
{
  if (this.height > o.height) return this;
  else if (this.height < o.height) return o;
  else return this;
}

And we'd make a similar one for Ogres that meet Wizard. But things get out of hand if we have to add more character types to the program.

This is where I get stuck. One obvious solution is to create a Character class with the common traits. Ogre and Wizard inherit from the Character and extend it to include the other traits that define each one.

public class Character
{
  int height;

  public Character battle(Character c)
  {
    if (this.height > c.height) return this;
    else if (this.height < c.height) return c;
    else return this;
  }
}

Is there a better way to organize the classes? I've looked at the strategy pattern and the mediator pattern, but I'm not sure how either of them (if any) could help here. My goal is to reach some kind of common battle method, so that if an Ogre meets an Ogre it uses the Ogre-vs-Ogre battle, but if an Ogre meets a Wizard, it uses a more generic one. Further, what if the characters that meet share no common traits? How can we decide who wins a battle?

Edit: Lots of great responses! I need to digest them and figure out which one works best for my situation.

+4  A: 

What about separating the battle logic into its own class, with methods like

Battle(Ogre ogre, Wizard wizard)

Which would return an object containing the winner (or the winner itself, whatever). This would separate the battle logic from the battlers, and also allow you to generercize, i.e.:

Battle(Creature creat1, Creature creat2)

Would be a fallback method for any creature pairing (assuming Wizard/Ogre/etc all have 'Creature' as a base class) that doesn't have specific logic. This would allow you to add/edit/remove battle logic without modifying any of the creatures themselves.

mgroves
The problem with this approach is that Java will look at the compile-time type to determine which version of the `Battle` method to call. For example: `Creature c1 = new Ogre(); Creature c2 = new Wizard(); Battle(c1,c2);` would call `Battle(Creature,Creature)` and **not** `Battle(Ogre,Wizard)`.
Skrud
-1 Creates high coupling.
OscarRyz
+2  A: 

One way to do it would be to make a new interface to all of the character types like

public interface Fightable
{
    public Fightable doBattle(Fightable b);
}

And then from there you would implement doBattle in each class. For example, in the Ogre class, you could check whether b was an instance of Ogre (in which case do one thing), a Wizard (in which case another), etc...

The problem there is that every time you add a new type, you need to add code to every single class of character to every other character, which isn't particularly maintainable. Further you'd have to stress about making sure that the operations were properly maintained, i.e. if you changed the doBattle method in the Ogre class with respect to Wizards but not in the Wizard class with respect to Ogres, you could have a situation where the result differ whether you call anOgre.doBattle(aWizard) or aWizard.doBattle(anOgre).

What may be better would be to create a Battle class that accepts two Characters and contains the logic for fighting by seeing which two class types have been passed to it: that way, you only need to change your Battle class every time a new Character type is added! You want to encapsulate the behavior that is most likely to change most often.

JF
Reformatted code; please revert if incorrect.
trashgod
This approach is useful if some characters don't implement `Fightable` but do implement `Communicative`, etc. You can also, abstract the input parameters of your outcome function and arrange for each implementing character to return a value based on its own characteristics.
trashgod
Looks better, thanks :)
JF
-1 The problem with this is that you create a dependency between each character.
OscarRyz
Read the last paragraph...
JF
+5  A: 

Sounds like you want double dispatch.

Basically your Ogre and Wizard would (probably) have a common base. When you call the battle method from Ogre's base it takes in the base of Wizard and invokes another function on that base that takes the Ogre as an argument. The polymorphic behaviour on both function calls effectively gives you polymorphism on the two types simultaneously.

Troubadour
-1 You'll will create a dependency on each class from each other. High coupling is not desirable.
OscarRyz
@Oscar: It doesn't create dependencies between the classes. Each class simply overloads a method for each type that it wants to specify the behaviour for. The actual logic could be deferred to a function somewhere else that takes in both types. As I understand it the OP was looking for a way to take two general `Character` objects and simply call a method like `battle` on one of them and have it Do The Right Thing which this does.
Troubadour
+15  A: 

The visitor pattern "is a way of separating an algorithm from an object structure it operates on".

For your example, you can have

class Character {
    boolean battle(BattleVisitor visitor) {
       return visitor.visit(this);
    }
}

class Ogre extends Character {..}
class Wizard extends Character {..}
class Dwarf extends Character {..}

interface BattleVisitor {
    boolean visit(Ogre character);
    boolean visit(Wizard character);
    boolean visit(Dwarf character);
}

class OgreBattleVisitor implements BattleVisitor {
    private Ogre ogre;
    OgreBattleVisitor(Ogre ogre) { this.ogre = ogre; }
    boolean visit(Ogre ogre) {
      // define the battle 
    }

    boolean visit(Wizard wizard) {
      // define the battle 
    }
    ...
}

And whenever a fight occurs:

targetChar.battle(new OgreBattleVisitor(ogre));

Define a Visitor implementation for a Wizard and a Dwarf and whatever appears. Also note that I define the result of the visit method to be boolean (won or lost) rather than to return the winner.

Thus, when adding new types, you will have to add:

  • a method to the visitor to handle fighting the new type.
  • an implementation for handling the fights for the new type

Now, here it turns out that you will have some duplication of code in case "Ogre vs Wizard" == "Wizard vs Ogre". I don't know if this is the case - for example there might be a difference depending on who strikes first. Also, you may want to provide totally different algorithm for, let's say "Swamp battle with ogre" compared to "village battle with ogre". Thus you can create a new visitor (or a hierarchy of visitors) and apply the appropriate one whenever needed.

Bozho
Could the visitor just be passed in with the constructor to avoid having to pass it as a parameter for each battle?
mgroves
+1 for visitor pattern reference - definitely seems to be more appropriate for this type of a problem.
aperkins
@mgroves: yes, but this allows different battle scenarios: in different situations, different results might be wanted.
Eric
I see. I love it, I think it's very elegant :)
mgroves
So... whenever you want to add a new character you must add one extra method per existing character in each visitor? How is that any better than changing all other characters?
wilhelmtell
@WilhelmTell of Purple-Magenta you will have to add these methods anyway, because they are required logic. It is better because you don't change the character - i.e. the structure. You are only adding operations in another place
Bozho
@Oscar Reyes this is by requirement - each character should know how to fight each other character because of character specifics. But thus you are not tying a character with the way he fights with others. That is, the Character (Wizard, Ogre) itself isn't coupled with others at all. And, the visitor pattern is GoF, no my invention :)
Bozho
@Bozho I misread your answer, I take back my comment, this doesn't add high coupling between the objects because that's handled by the visitor.
OscarRyz
A: 

Hmm, first of all, your first design is not good, because you let the fighters decide who wins. If you use Mediator, e.g. something like proposed Battle class, you´will be able to centralize the fighting logic and easily change any fighting rule at one place. Imagine having many creatures... once you sould want to change how two fight together, where will you go to search for the battle method? In first or in second class? Some superclass? So Mediator is a good idea. Yet another problem is deciding which rule to use. You might easily get to the multiple dispatch problem.

Gabriel Ščerbák
What's "the multiple dispatch problem"? Isn't multiple dispatch a solution? :-)
Ken
@Ken yeah, it is a solution, which is in many languages problematic to implement:)
Gabriel Ščerbák
A: 

Something like this?

class Trait
{
    enum Type
    {
        HEIGHT,
        WEIGHT,
        IQ
    }
    protected Type type;
    protected int value;

    public Trait(int value, Type type)
    {
        this.type = type;
        this.value = value;
    }

    public boolean compareTo(Trait trait)
    {
        if(trait.type != this.type)
            throw new IllegalArgumentException(trait.type+" and "+this.type+" are not comparable traits");
        else
            return this.value - trait.value;
    }
}

class Character
{
    protected Trait[] traits;

    protected Character(Trait[] traits)
    {
        this.traits = traits;
    }

    public Trait getTrait(Trait.Type type)
    {
        for(Trait t : traits)
            if(t.type == type) return t;
        return null;
    }

    public Character doBattleWith(Character that)
    {
        for(Trait thisTrait : traits)
        {
            otherTrait = that.getTrait(thisTrait.type);
            if(otherTrait != null)
            {
                int comp = thisTrait.compareTo(otherTrait);

                if(comp > 0)
                    return this;
                else if (comp < 0)
                    return that;
            }
        }
        return null;
    }
}

class Ogre extends Character
{
    public Ogre(int height, int weight)
    {
        super(new Trait[]{
            new Trait(Type.HEIGHT,height),
            new Trait(Type.WEIGHT,height)});
    }
}
Eric
+5  A: 

What should happen in the case where two Ogres have the same height/weight and the same axe-length? According to your example, the one that was lucky enough to get called first would win.

I don't know if this is a suitable alternative, but what if you went for a completely different scheme and attributed a "battle-score" to each character instead of relying on comparing individual traits. You could use a character's attributes in a formula to give some integer that can be compared to another character's. Then you could use a generic battle method to compare the two scores and return the character with the higher one.

For example, what if an Ogre's "battle-score" was calculated by his height plus his weight times his axe-length and a Wizard's score was calculated by his age multiplied by his IQ?

abstract class Character {
    public abstract int battleScore();

    public Character battle(Character c1, Character c2) {
        (c1.battleScore() > c2.battleScore()) ? return c1 : c2;
    }
}

class Ogre extends Character {
    public int battleScore() {
        return (height + weight) * axeLength;
    }
 }

 class Wizard extends Character {
    public int battleScore() {
        return height + (age * IQ);
    }
 }
Skrud
Is a wizard of age 100 and IQ 200 as powerful as a wizard of age 200 and IQ 100? I see these are just examples, but there's an issue with flatenning ideas (age, IQ, ...) into integers. Still, I personally like this direction much more than the visitor pattern.
wilhelmtell
That was just a simple example, so you could say that age is worth `x` percent and iq worth `y`.
DMan
+1  A: 

You are going to have to define the unique logic (assuming that the logic IS unique) for every single combination of battle anyways - it doesn't matter what design pattern you choose to use. The only requirement is to separate this logic from the Ogre and Wizard class and create the battle methods in a different class. I think what you are doing currently is completely fine (once you move the battle logic somewhere else) without requiring a visitor pattern, which is what I would use if this was some enterprise game :)

Don't listen to all of this fluff about design patterns...

GreenieMeanie
-1, I think the visitor pattern is a very elegant solution
mgroves
+2  A: 

I think you should rethink this whole thing.

Let's just take World of Warcraft as an example of how battle can be done, simply because it's a well-known game.

You have a number of different classes, which are capable of doing different things, and have their own strengths and weaknesses. However, all of them share some common types of statistics. For example, a Mage has more Intellect than a Warrior, but the Warrior is going to have a lot more Strength than the Mage.

So how do they actually battle? Well, regardless of the class, each character has a number of abilities at their disposal. Each ability does some amount of damage, and once the HP of one of the characters drop to 0, that character dies - they lose the fight.

You should use a similar approach: define a common base class with common attributes which apply to everyone - stuff like strength, spellpower, defense, and stamina. Then, when each type of character does battle, they can use any of a series of attacks or spells - the damage caused by each attack or spell will depend on the stats of both the attacker and defender, using some suitable formula (with some randomness to keep it interesting, it'll probably be no fun if it's impossible for a Wizard to ever beat an Ogre, or vice versa).

But here's something else to consider: Maybe you shouldn't use a class per type. It would be more preferable if you could use the same formula for everyone - even if they don't have the same set of abilities. Instead of having to code each ability in there, you would just have a list of abilities and their parameters in a file, and the Character class would use it to do all of these calculations. This makes it easier to adjust the formula (only that one place to look), and easier to adjust the abilities (just change the file). It's a bit harder to write that formula, because you might want to give the Ogre a bonus for having a high strength, while the Wizard would get a bonus for a high intellect, but it's better than having X almost identical formulae, one for each stat which can affect the output.

Michael Madsen
I understand what you are saying here, but I also think you are basically creating a different game from what he is trying to create.
mgroves
@mgroves: I'm not saying cover every single aspect that WoW covers is the right way to go, but if you don't have any common attributes, you have to specify a different way of battling for each and every combination of types - all of which must be specified individually. It's very likely to become unmaintainable purely because of all of these combinations, and that's why you would probably be better off trying to centralize some of these things.
Michael Madsen
If the individual battle methods ended up being very similar/repetitive, I think an approach like yours would be the best way to go. However, it seems from the question that each battle pairing could be very, very unique, and I think the visitor pattern is best for that.
mgroves
+2  A: 

This is exactly the kind of problems Strategy is aim to solve.

Let's review the parts of Strategy

  • Context : The fight
  • Strategy: How would you define which will win
  • Concrete Strategy : Where the fight takes place and the decision is made.

So, instead of leaving that responsibility to the character them selves ( because they will always say, "I won!!, no I won, no I.." ) you can create a RefereeStrategy.

The concrete implementation will decide who wins.

strategy in action

diagram generated with http://yuml.me

You may either define common methods that all the characters agree to respond ( which doesn't seems like what you want, this is useful when all the characters have the same "attributes" or methods like defense():int, attack():int, heal():int ) or do "blind" strategy.

I'm doing the second ( "blind" strategy )

// All the contenders will implement this.
interface Character {
    public String getName();    
}
// The context
class FightArena {
    Character home;
    Character visitor;

    // The strategy 
    Referee referee;

    Character fight(){
        this.referee = RefereeFactory.getReferee( home.getName(), visitor.getName() );
        Character winner = referee.decideFightBetween( home, visitor );
        out.println(" And the winner iiiiss...... " + winner.getName() );
    }
}

interface Referee {
    Character decideFightBetween( Character one, Character two );
}

class RefereeFactory {

        static Referee getReferee( Character one, Character two ) {
             .... return the appropiate Refereee... 
        }    
}

// Concrete Referee implementation 
// Wizard biased referee, dont' trust him
class OgreWizardReferee implements Referee {
    Character decideFightBetween( Character one, Character two ) {
        if( one instanceof Wizard ){
            return one;
        }else{
            return two;
        }
    }
}
class OgreReferee implements Referee {
    Character decideFightBetween( Character one, Character two ) {
        Ogre a = ( Ogre ) one;
        Ogre b = ( Ogre ) two;

        if( a.height > b.height || a.axeLength > a.axeLength ) {
            return a;
        }
        return b;
    }

}

This allow you to plug new algorithms ( strategies - referees - what the pattern is good for ) as you need them.

It keeps the context ( your fight arena ) free of "if/elseif/else/if/else" constructs by forwarding to the referee the decision of the winner, and isolate your different characters from each other.

OscarRyz
_"I won!!, no I won, no I.."_ WTF?
wilhelmtell
I wouldn't say I like this option, because it relies on string constants as map keys. So the something similar to if/elseif/else/if is actually hidden within the map, but it still exists.
Bozho
I can see why this would be a good solution for a language that _doesn't_ support polymorphism.
Troubadour
@Troubadour. Polymorphism is needed for the concrete strategy to work.
OscarRyz
@Bozho I was actually thinking in the RefereeFactory as a plugin architecture where new referees may be changed adding new jars' files, but I'm not going to code all that. This was a simple understandable way to show it.
OscarRyz
@Oscar: No, the concrete strategy doesn't require polymorphism here at all. The point is that you've used a map to completely bypass polymorphism. All you do in your example is get a `Referee` from a factory and call `decideFightBetween` on it. You may as well make `decideFightBetween` a method on the factory. Returning the referee is just a token attempt to use polymorphism after completely bypassing it.
Troubadour
A: 

try uncle bob's triple dispatch. see my answer to: http://stackoverflow.com/questions/948564/managing-inter-object-relationships

Ray Tayek
+1  A: 

Instead of trying to resolve each battle for type of monster vs other type of monster, why not create some value for a monster based on it's attributes.

If it has a higher value than what it fights, it wins.

To compensate for certain enemies being better against other enemies, implement some kind of defence for each attack type

e.g Archer attacks from range, ogre is melee, and wizard is magic. Ogre has melee defence, ranged defence and magic defence.

The value of an monster can then be calculated based it's attack & and the enemies respective armour, as well as HP etc etc etc.

So you don't bother with a case by case basis.

MrBones
A: 

I know this is a little late, but Steve Yegge wrote an article a few years back on almost this exact problem (he even used a game example!).

derivation