管理对象间关系

时间:2009-06-04 04:10:50

标签: oop

如何为对象编写特殊情况?

例如,假设我正在编写一个RPG - 有N = 5个类。矩阵中存在N ^ 2个关系,用于确定角色A是否可以攻击(或使用能力M)角色B(暂时忽略其他因素)。

如何在没有特殊情况的情况下在OOP中对此进行编码?

另一种说法是,我有点可能

ClassA CharacterA;
ClassB CharacterB;

if ( CharacterA can do things to CharacterB )

我不确定if语句里面是什么,而不是

if ( CharacterA.CanDo( CharacterB ) )

或元类

if ( Board.CanDo( CharacterA, CharacterB ) )

当CanDo函数应该依赖于ClassA和ClassB,还是依赖于ClassA / ClassB的属性/修饰符?

7 个答案:

答案 0 :(得分:3)

我将从canSee(怪物怪物)或canBeSeenBy(怪物怪物)方法开始,看看会发生什么。您最终可能会使用Visibilility类或最终使用http://en.wikipedia.org/wiki/Visitor_pattern。一个极端的例子是叔叔突然三重派遣:

// 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);
        }
    }

答案 1 :(得分:1)

“看”的定义是什么?如果他们占据同一个广场?如果是这样,将在如何实现冲突检测(或本例中的任何内容)而不是字符之间的OOP关系中回答这个问题。在不知道更多信息的情况下,我会以这种方式处理问题(用C ++ /伪代码表示):

class Character {
private:
    matrixSquare placement;
public:
    Character() {};
    ~Character {};
    matrixSquare getLocation() { return placement;};
};

class GameBoard {
private:
    //your 5 x 5 matrix here
public:
    GameBoard() {};
    ~GameBoard() {};
    boolean isOccupied(matrixSquare)
    {
        if (occupied)
        {
            //do something
            return true;
        }
        else
        {
            return false;
        }
    }
};

这里的诀窍是定义角色片段和比赛场地实施之间的关系。在那之后,你可以澄清你如何确定两个角色是否在同一个正方形,相邻的正方形等等......希望有所帮助。

答案 2 :(得分:1)

我想说使用设计模式,通常我认为Observer,Mediator和Visitor模式非常适合管理复杂的对象间关系。

答案 3 :(得分:1)

我会(假设C ++)给每个类一个std::string标识符,由类的实例上的getter方法返回,并使用std::map< std::pair<std::string, std::string>, ... >来编码类之间关系的特殊情况,一切都很好并在一个地方订购。我还将通过getter函数专门访问该映射,以便更改编码部分或全部特殊情况的策略变得简单。例如:如果25个中只有几对类具有“invisibility”属性,则该情况下的映射可能只包含具有该属性的少数异常(对于像{a} std::set这样的布尔属性可能实际上是一个更好的实现,在C + _)。

有些OO语言有多个调度(Common Lisp和Dylan是目前想到的两个),但是对于缺少它的所有(绝大多数)语言来说,这是一种很好的方法(在某些情况下)你会发现集中式地图/字典对动态访问者设计模式等具有限制性和重构性,但是由于“getter函数”,这些重构对于你的所有其余代码都是非常透明的。)

答案 4 :(得分:1)

在回答您对问题的编辑时,您需要研究多态性。我个人会将cando()函数作为Board的一部分,然后,根据传入的两个类,Board会调用相应的函数并返回结果(战斗,视觉,等等) 。

如果您在java中执行此操作,则使用enum / interface与游戏板一起使用将是解决此问题的一种非常简洁的方法。

答案 5 :(得分:1)

史蒂夫·耶格(Steve Yegge)有一篇关于“属性”模式的 awesome 博客文章,您可以使用它处理此问题。事实上,他用它写了一个RPG!

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

你可能会说play1是type1而type1s可以攻击type2s而player2是type2,所以除非在特定的player1上有一些“覆盖”,否则player1可以攻击player2。

这可以实现非常强大和可扩展的行为。

答案 6 :(得分:1)

我建议你看一下双重调度模式。

http://c2.com/cgi/wiki?DoubleDispatchExample

以上示例说明了一组打印机如何打印一组形状。

http://en.wikipedia.org/wiki/Double_dispatch

维基百科的例子特别提到用这种模式解决自适应碰撞问题。