Управление межобъектными отношениями - PullRequest
2 голосов
/ 04 июня 2009

Как вы кодируете особые случаи для объектов?

Например, допустим, я кодирую RPG - существует N = 5 классов. В матрице есть N ^ 2 отношений, которые определяют, может ли персонаж A атаковать (или использовать способность M на) персонажа B (пока игнорируя другие факторы).

Как бы я закодировал это в ООП, не помещая специальные случаи повсюду?

Еще один способ выразить это, у меня есть кое-что, может быть,

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 ]

3 голосов
/ 04 июня 2009

Я бы начал с метода canSee (Monster monster) или canBeSeenBy (Monster monster) и посмотрел, что произойдет. вы можете получить класс 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 голос
/ 04 июня 2009

Я предлагаю вам взглянуть на шаблон двойной отправки.

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

Приведенный выше пример объясняет, как группа принтеров может печатать группу фигур.

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

В примере из Википедии конкретно упоминается решение проблем адаптивного столкновения с помощью этого шаблона.

1 голос
/ 04 июня 2009

У Стива Йегге есть потрясающее сообщение в блоге о шаблоне свойств, которое вы можете использовать, чтобы справиться с этим. На самом деле он написал RPG, используя его!

http://steve -yegge.blogspot.com / 2008/10 / универсальный дизайн-pattern.html

Вы можете сказать, что player1 - это тип1, а type1s может атаковать type2s, а player2 - это тип2, поэтому, если не будет «переопределения» для конкретного игрока1, игрок1 может атаковать игрока2.

Это обеспечивает очень устойчивое и расширяемое поведение.

1 голос
/ 04 июня 2009

В ответ на ваше изменение вашего вопроса вы захотите взглянуть на полиморфизм. Я лично хотел бы, чтобы функция cando () входила в состав Board, а затем, в зависимости от двух пройденных классов, Board вызывала соответствующую функцию и возвращала результат (битвы, видения и т. Д.) ,

Если вы делаете это в java, enum / interface вместе с игровой доской был бы очень чистым способом решения этой проблемы.

1 голос
/ 04 июня 2009

Я бы (при условии C ++) дал каждому классу идентификатор std::string, возвращаемый методом getter в экземпляре класса, и использовал бы std::map< std::pair<std::string, std::string>, ... > для кодирования особых случаев отношений между классами, все красиво и упорядочено в одном место. Я бы также получил доступ к этой карте исключительно через функцию получения, так что изменение стратегии кодирования некоторых или всех особых случаев стало простым, как круговая диаграмма. Например: если только несколько пар классов из 25 имеют свойство «невидимость», карта в этом случае может содержать только несколько исключений, которые имеют это свойство (для логического свойства, подобного этому, std::set может быть предпочтительная реализация, в C + _).

Некоторые ОО-языки имеют множественную диспетчеризацию (Common Lisp и Dylan - это те, которые приходят на ум в данный момент), но для всех (подавляющего большинства) языков, в которых его нет, это хороший подход (в некоторых случаях вы обнаружите, что централизованная карта / dict является ограничительной и реорганизует шаблон проектирования Dynamic Visitor или тому подобное, но благодаря «функции получения» такие рефакторинги будут довольно прозрачными для всего остального кода).

1 голос
/ 04 июня 2009

Я бы сказал, что используйте шаблоны проектирования, обычно я думаю, что шаблоны Observer, Mediator и Visitor вполне хороши для управления сложными межобъектными отношениями.

1 голос
/ 04 июня 2009

Какое определение "видеть"? Если они занимают одну и ту же площадь? Если это так, то ответ будет дан в том, как вы реализуете обнаружение столкновений (или что-то еще в этом случае), а не отношения ООП между символами. Не зная дополнительной информации, я бы подошел к проблеме следующим образом (в 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;
        }
    }
};

Хитрость здесь в том, чтобы определить отношения между частями вашего персонажа и вашей реализацией игрового поля. После того, как это установлено, вы можете уточнить, как вы определяете, находятся ли два символа в одном квадрате, смежных квадратах и ​​т. Д. Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...