Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Managing inter-object relationships

Tags:

oop

How do you code special cases for objects?

For example, let's say I'm coding an rpg - there are N = 5 classes. There are N^2 relationships in a matrix that would determine if character A could attack (or use ability M on) character B (ignoring other factors for now).

How would I code this up in OOP without putting special cases all over the place?

Another way to put it is, I have something maybe

ClassA CharacterA;
ClassB CharacterB;

if ( CharacterA can do things to CharacterB )

I'm not sure what goes inside that if statement, rather it be

if ( CharacterA.CanDo( CharacterB ) )

or a metaclass

if ( Board.CanDo( CharacterA, CharacterB ) )

when the CanDo function should depend on ClassA and ClassB, or attributes/modifiers with ClassA/ClassB?

like image 374
Timmy Avatar asked Apr 23 '26 22:04

Timmy


2 Answers

i would start with a canSee(Monster monster) or canBeSeenBy(Monster monster) method and see what happens. you may end up with a Visibilility class or end up using the http://en.wikipedia.org/wiki/Visitor_pattern. an extreme example is uncle bobs triple dispatch:

// visitor with triple dispatch. from a post to comp.object by robert martin http://www.oma.com
/*
In this case, we are actually using a triple dispatch, because we have two
types to resolve.  The first dispatch is the virtual Collides function which
resolves the type of the object upon which Collides is called.  The second
dispatch is the virtual Accept function which resolves the type of the
object passed into Collides.  Now that we know the type of both objects, we
can call the appropriate global function to calculate the collision.  This
is done by the third and final dispatch to the Visit function.
*/
interface AbstractShape
    {
    boolean Collides(final AbstractShape shape);
    void Accept(ShapeVisitor visitor);
    }
interface ShapeVisitor
    {
    abstract public void Visit(Rectangle rectangle);
    abstract public void Visit(Triangle triangle);
    }
class Rectangle implements AbstractShape
    {
    public boolean Collides(final AbstractShape shape)
        {
        RectangleVisitor visitor=new RectangleVisitor(this);
        shape.Accept(visitor);
        return visitor.result();
        }
    public void Accept(ShapeVisitor visitor)
        { visitor.Visit(this); } // visit Rectangle
    }
class Triangle implements AbstractShape
    {
    public boolean Collides(final AbstractShape shape)
        {
        TriangleVisitor visitor=new TriangleVisitor(this);
        shape.Accept(visitor);
        return visitor.result();
        }
    public void Accept(ShapeVisitor visitor)
        { visitor.Visit(this); } // visit Triangle
    }

class collision
    { // first dispatch
    static boolean Collides(final Triangle t,final Triangle t2) { return true; }
    static boolean Collides(final Rectangle r,final Triangle t) { return true; }
    static boolean Collides(final Rectangle r,final Rectangle r2) { return true; }
    }
// visitors.
class TriangleVisitor implements ShapeVisitor
    {
    TriangleVisitor(final Triangle triangle)
        { this.triangle=triangle; }
    public void Visit(Rectangle rectangle)
        { result=collision.Collides(rectangle,triangle); }
    public void Visit(Triangle triangle)
        { result=collision.Collides(triangle,this.triangle); }
    boolean result() {return result; }
    private boolean result=false;
    private final Triangle triangle;
    }
class RectangleVisitor implements ShapeVisitor
    {
    RectangleVisitor(final Rectangle rectangle)
        { this.rectangle=rectangle; }
    public void Visit(Rectangle rectangle)
        { result=collision.Collides(rectangle,this.rectangle); }
    public void Visit(Triangle triangle)
        { result=collision.Collides(rectangle,triangle); }
    boolean result() {return result; }
    private boolean result=false;
    private final Rectangle rectangle;
    }
public class MartinsVisitor
    {
    public static void main (String[] args)
        {
        Rectangle rectangle=new Rectangle();
        ShapeVisitor visitor=new RectangleVisitor(rectangle);
        AbstractShape shape=new Triangle();
        shape.Accept(visitor);
        }
    }
like image 180
Ray Tayek Avatar answered Apr 26 '26 11:04

Ray Tayek


Steve Yegge has an awesome blog post about the Properties pattern that you could use handle this. In fact, he wrote an RPG using it!

http://steve-yegge.blogspot.com/2008/10/universal-design-pattern.html

You might say player1 is a type1 and type1s can attack type2s and player2 is a type2, so unless there is some "override" on the specific player1, player1 can attack player2.

This enables very robust and extensible behavior.

like image 21
dmo Avatar answered Apr 26 '26 12:04

dmo