Как думать Скажи, не спрашивай в этом простом примере?

Как бы вы придерживались принципа «Говори, не спрашивай» (далее «принцип») в следующем простом сценарии? В игре Tetris у меня есть классы Board, BlockGrid и Piece, относящиеся к следующему примеру:

public class Board
{
    private var fallingPiece:Piece;
    private var blockGrid:BlockGrid;
    ...
    public function moveFallingPiece(xDirection:int, yDirection:int):void
    {
        blockGrid.movePiece(fallingPiece, xDirection, yDirection);
    }
} 

Как только падающая часть помещается в нижнюю строку BlockGrid, она больше не должна быть «падающей частью». Прав ли я в том, что не нарушаю принцип следующим?

if(blockGrid.getPiecePosition(piece).y == 0)
{
    fallingPiece = null;
}

Но действительно ли это отличается от того, что я считаю явно нарушающим принцип?

public function moveFallingPiece(xDirection:int, yDirection:int):void
{
    if(blockGrid.getPiecePosition(piece).y > 0)
    {
        blockGrid.movePiece(fallingPiece, xDirection, yDirection);
    }
    else
    {
        fallingPiece = null;
    }
}

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


РЕДАКТИРОВАТЬ, Предлагаемое решение:

Я пошел с ответами, предлагающими «командную обратную связь» через события. Доска говорит BlockGrid переместить часть. Метод movePiece в BlockGrid отправляет события MOVED_TO или MOVE_FAILED в зависимости от результата, который Board может прослушивать и использовать, чтобы определить, перестал ли падать элемент. Пожалуйста, не стесняйтесь оставлять отзывы об этом решении.

public class Board
{
    ...
    public function Board()
    {
        ...
        blockGrid.addEventListener(PieceMoveEvent.MOVE_FAILED, onPieceMoveFailed);
        ...
    }

    public function moveFallingPiece(xDirection:int, yDirection:int):void
    {
            blockGrid.movePiece(fallingPiece, xDirection, yDirection);
    }

    public function onPieceMoveFailed(event:MovePieceEvent):void
    {
        if(event.instance == currentlyFallingPiece && event.fromPosition.y != event.toPosition.y)
        {
             currentlyFallingPiece = null;
        }
    }

person Stiggler    schedule 07.02.2010    source источник
comment
Как работает не спрашивай не говори?   -  person Hamish Grubijan    schedule 07.02.2010
comment
Вот хорошая ссылка на сообщение, не спрашивайте: pragprog.com/articles/tell- не спрашивай   -  person Dolph    schedule 07.02.2010


Ответы (4)


Я думаю, чтобы лучше следовать принципу «Говори, не спрашивай», у вас должен быть блокGrid, уведомляющий ваш класс Board, когда падающая часть достигает точки покоя. В обоих приведенных выше сценариях вы спрашиваете blockGrid, если position.y == 0, чтобы определить, должен ли падающий элемент иметь значение null. Вместо этого вы хотите, чтобы blockGrid сообщал классу Board, что fallPiece.y достиг 0.

person sberry    schedule 07.02.2010
comment
на самом деле в фигурах не должно быть никакой логики, доска должна говорить им, что делать. - person ; 07.02.2010

То, что вы ищете, это программирование, управляемое событиями. Вам нужен интерфейс Listener с методом .event() и интерфейс Event для представления событий. Объекты будут регистрироваться с другими объектами (обратными вызовами) в интерфейсе Listener.

когда вы создаете Piece and Board, они должны реализовывать интерфейс Listener. Затем вы можете установить доску с помощью registerListener(board); Затем, когда что-то происходит внутри Piece, он будет проходить через всех зарегистрированных слушателей и вызывать .event(event) для каждого. То же самое с Board, вызовите board.registerListener(piece) каждый раз, когда вы создаете новый кусок, поскольку он решает, что что-то происходит, он может сообщить всем зарегистрированным слушателям, что произошло. Затем вы можете сообщить фигуре, что она больше не падает, с помощью объекта Board, решившего это. Вот обязательная запись в Википедии.

person Community    schedule 07.02.2010

Я бы ожидал класс, представляющий каждую форму (без информации о положении), контроллер, содержащий форму, положение и ориентацию, и еще один класс, представляющий текущую результирующую сетку «приземлившихся» фигур. Приземленная сеть будет иметь

testLanded(shape, shapePosition, orientation)

метод, который будет вызываться до/после каждой операции перемещения, чтобы решить, должна ли фигура присоединиться к приземлившейся сетке или должна двигаться и оставаться падающей частью.

Я придерживаюсь идеи не передавать данные объектам, которые на самом деле не должны владеть этими данными, но я никогда не реализовывал тетрис...

person DaveC    schedule 07.02.2010
comment
Интересно, а как бы вы реализовали testLanded, не запрашивая у других объектов их состояние? - person Stiggler; 08.02.2010
comment
у landedGrid, вероятно, будет двумерный массив того, где приземлились предыдущие блоки - блок перестанет быть правильным блоком в этот момент и просто станет некоторыми цветами в этой сетке. Таким образом, Landed-Grid использует блоки в своей собственной структуре данных, поэтому у него есть вся информация, чтобы определить, приземлился ли текущий блок на ландшафт, оставленный предыдущими блоками. - person DaveC; 14.02.2010

Возможно, вам придется переосмыслить свой дизайн. Действительно ли Board должен отслеживать падающий элемент или он должен принадлежать BlockGrid? Выясните, кому принадлежит какое поведение.

Сохраняйте информацию о положении в своем классе Piece и, возможно, ваш класс Piece содержит экземпляр BlockGrid.

Затем вы можете попробовать что-то подобное в своем классе Board...

public function moveFallingPiece(xDirection:int, yDirection:int):void
{
    blockGrid.moveFallingPiece(xDirection, yDirection);
}

Затем в методе BlockGrid moveFallingPiece...

public function moveFallingPiece(xDirection:int, yDirection:int):void
{
    fallingPiece.move(xDirection, yDirection);
}

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

public function move(xDirection:int, yDirection:int):void
{
    setPosition(xDirection, yDirection);

    if (getPosition().y <= 0)
    {
        blockGrid.setFallingPiece(null); 
        // this can bubble up to Board if need be
    }
}

Не уверен во всей мощи AS3, но здесь имело бы смысл использовать абстракции. (т. е. пусть ваш класс Piece зависит от ITrackFallingPieces вместо BlockGrid, а BlockGrid реализует ITrackFallingPieces).

Удачи!

person Kevin Swiber    schedule 07.02.2010
comment
Спасибо, я ценю отзывы о дизайне! Однако я не знаю, отвечает ли это на мой вопрос; вызов blockGrid.setFallingPiece из Piece кажется плохой инкапсуляцией. Что касается дизайна, я решил не хранить информацию о позиции в Piece, потому что это допускает возможность того, что две части имеют одну и ту же позицию. BlockGrid предназначался для отслеживания позиций фигур в сетке (возможно, лучше называть ее PieceGrid); концепция падения казалась слишком большой ответственностью. - person Stiggler; 08.02.2010
comment
Чувак, мы говорим об игре Тетрис, а не о новейшем балансировщике топлива НАСА в реальном времени. Остыть. - person e-satis; 08.02.2010
comment
Кому адресован этот комментарий? Моя главная цель в этом вопросе - не заставить игру Тетрис работать, а лучше понять принцип дизайна... - person Stiggler; 08.02.2010