Вопросы философического дизайна для OOP-Tetris
Вы пишете программу Tetris на Java. Как бы вы создали свой дизайн класса в отношении следующих аспектов?
- Класс Piece:. Имеет один класс
Piece
, с внутренним массивом, который определяет форму части, и имеет семь классов Piece
, по одному для каждой части. Все они являются подклассами одного родового класса Piece.
- Представление класса штук: Имеет массив из 4 экземпляров
Block
, представляющий один квадрат части, и каждый Block
содержит свое местоположение на доске (в графических координатах) против. имеющий массив 4x4, где null
означает, что там нет блока, а местоположение определяется формой массива.
- Местоположение: Каждый
Block
в массиве Piece
или в массиве Board
сохраняет свое местоположение по сравнению с Piece
и Board
знает местоположения Blocks
, которые содержат их.
- Создание фрагмента: Имеет статический метод класса Piece
getRandomPiece
или имеет PieceFactory
, который вы делаете одним экземпляром, который имеет метод genRandomPiece
в экземпляре.
- Манипуляция текущей частью: Используйте шаблон
Proxy
, так что все, что требует доступа к нему, просто использует прокси-сервер или имеет метод getCurrentPiece
в классе Board
и вызов что в любое время вы хотите что-то сделать с текущей частью.
Это не домашнее задание. Я просто расстроен тем, чему учит интро-курс CS в моем колледже, и я хочу посмотреть, что считают люди в целом. Что можно было бы назвать "хорошим" дизайном ООП? Игнорируйте тот факт, что это для вступительного курса - как бы вы это сделали?
Ответы
Ответ 1
Во-первых, я бы не подклассифицировал класс Piece
, потому что он не нужен. Класс Piece
должен быть способен описывать любую форму без использования наследования. ИМХО, это не то, что наследование было сделано, и это просто усложняет ситуацию.
Во-вторых, я не буду хранить координаты x/y в объектах Block
, потому что он позволяет двум блокам существовать в одном и том же месте. Классы Piece
сохраняли бы сетку (т.е. 2D-массив), удерживающую объекты блока. Координаты x/y были бы индексами 2D-массива.
Что касается статического метода vs factory для получения случайного фрагмента, я бы пошел с объектом factory для простого факта, что объект factory можно высмеять для тестирования.
Я рассматривал бы плату как один большой объект Piece
. Класс Board
будет содержать большой объект Piece
в качестве переменной-члена и может содержать другие объекты Piece
, такие как текущий фрагмент, и следующий фрагмент для воспроизведения. Это делается с использованием композиции, чтобы избежать наследования.
Ответ 2
-
Один Piece
интерфейс с семью классами, которые реализуют этот интерфейс для отдельных частей (что также позволит диску ООП обсуждать интерфейсы) ( EDIT: Один Piece
. См. комментарии)
- У меня был бы класс
BlockGrid
, который можно использовать для любой карты блоков - как на доске, так и на отдельных частях. BlockGrid
должны иметь методы для обнаружения пересечений - например, boolean intersects(Block block2, Point location)
- а также для поворота сетки (интересная точка обсуждения для курса: если Board
не нужно вращать, следует использовать метод rotate()
быть в BlockGrid
?). Для Piece BlockGrid
будет представлять собой сетку 4x4.
- Я бы создал
PieceFactory
с помощью метода getRandomShape()
, чтобы получить экземпляр одной из семи форм
- Для управления куском я попал бы в архитектуру Model-View-Controller. Модель - это кусок. Контроллер, возможно, является
PieceController
, а также разрешает или запрещает правовые/незаконные действия. То, что показало бы Piece на экране, это PieceView
(hrm, или это BlockGridView
, который может показать Piece.getBlockGrid()
? Еще одна точка обсуждения!)
Существует несколько законных способов архивирования этого. Было бы полезно, чтобы в ходе обсуждения были обсуждены вопросы, связанные с про-соглашением и различными принципами ООП, применимыми к этой проблеме. На самом деле, может быть интересно сравнить и сопоставить это с реализацией не-ООП, которая просто использует массивы для представления платы и частей.
EDIT: Claudiu помог мне понять, что BlockGrid
будет достаточно различать фрагменты, поэтому нет необходимости в Piece
с несколькими подклассами; скорее, экземпляр класса Piece
может отличаться от других экземпляров на основе его BlockGrid
.
Ответ 3
Все эти классы и прочее... это может сделать проблему слишком абстрактной для того, что она есть на самом деле. Множество разных способов представления фрагментов tetris (stackoverflow.com/questions/233850/...) и множество способов управления ими. Если бы это было для интро-курса, я бы не стал беспокоиться о ООП. Просто мое мнение, а не настоящий ответ на ваш вопрос.
Сказав это, достаточно было бы просто класс Board и Piece.
Класс платы. Инкапсулирует простой 2d-массив прямоугольников. Свойства, такие как currentpiece, nextpiece. Такие процедуры, как draw(), fullrows(), drop() и т.д., Которые манипулируют текущей частью и заполненными квадратами платы.
Класс Piece. Инкапсулирует массив беззнаковых 16-битных чисел, кодирующих фрагменты в разных поворотах. Вы будете отслеживать цвет, текущее местоположение и поворот. Возможно, потребуется одна рутина, rotate().
Остальное будет, в зависимости от среды, обрабатывать события клавиатуры и т.д.
Я обнаружил, что слишком много внимания уделяется дизайну, который заставляет людей забыть, что им действительно нужно сделать, это заставить что-то работать. Я не говорю, не дизайн, я говорю, что чаще, чем нет, есть больше смысла в получении чего-то, что дает вам тягу и мотивацию продолжать идти.
Я бы сказал, для класса у вас есть X часов, чтобы создать дизайн для игры в тетрис. Тогда им нужно будет включить этот дизайн. Тогда я бы сказал, у вас есть X дней, чтобы получить что-то работает на основе дизайна, который вы включили или даже не на основе дизайна.
Ответ 4
Класс Piece: Я думаю, что одного класса для всех частей достаточно. Функции класса shoudl достаточно общие для работы для любой части, поэтому нет необходимости в подклассе.
Представление класса штук: Я считаю, что массив 4x4, вероятно, лучший способ, так как после этого вам будет намного легче повернуть кусок.
Местоположение: Местоположение должно обязательно храниться на плате, а не на куске, так как иначе вам придется пройти весь набор блоков, чтобы гарантировать, что два блока не находятся в одном положении.
Создание части: Честно говоря, для этого я не чувствую, что это сильно изменит ситуацию. Сказав это, я бы предпочел статическую функцию, поскольку для этой функции действительно не так много, что она гарантирует свой класс.
Манипуляция текущей частью: Я бы просто реализовал функцию getCurrent, поскольку я чувствую, что нет необходимости слишком усложнять проблему, добавляя дополнительный класс для использования в качестве прокси.
Вот как я это сделаю, но есть много разных способов, и в конце дня основное внимание уделяется простому запуску программы.