Philosophical Design Questions for OOP-Tetris [closed]
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 4 years ago.
Improve this questionYou are writing a Tetris program in Java. How would you set up your class design with regards to the following aspects?
- Piece class: Have one
Piece
class, with an internal array which determines the shape of the piece, versus having sevenPiece
classes, one for each of the pieces. They are all subclasses of one generic Piece class. - Piece class representation: Have an array of 4 instances of
Block
, representing one square of a piece, and eachBlock
contains its location on the Board (in graphical coordinates) vs. having a 4x4 array wherenull
means there is no block there, and location is determined by the shape of the array. - Location: Each
Block
in thePiece
array or on theBoard
array stores its location vs. thePiece
and theBoard
know the locations of theBlocks
that comprise them. - Generating a Piece: Have a static method of the Piece class
getRandomPiece
, or have aPieceFactory
which you make one instance of that has thegenRandomPiece
method on the instance. - Manipulating the current piece: Use the
Proxy
pattern, so that everything that needs access to it just uses the proxy, or have agetCurrentPiece
method on theBoard
class and c开发者_Python百科all that any time you want to do something with the current piece.
This is not homework. I'm just at odds with what the intro CS course teaches at my college and I want to see what people in general believe. What would be thought of as "good" OOP design? Ignore the fact that it's for an intro course - how would you do it?
Firstly, I wouldn't subclass the Piece
class because it's unnecessary. The Piece
class should be capable of describing any shape without using inheritance. IMHO, this isn't what inheritance was made for and it just complicates things.
Secondly, I wouldn't store the x/y coordinates in the Block
objects because it allows two blocks to exist in the same place. The Piece
classes would keep a grid (i.e. 2D array) holding the block objects. The x/y coordinates would be the indexes of the 2D array.
As for the static method vs factory object for getting a random piece, I'd go with the factory object for the simple fact that the factory object can be mocked for testing.
I would treat the board as one large Piece
object. The Board
class would keep the large Piece
object as a member variable, and might keep other Piece
objects such as the current piece being played, and the next piece to be played. This is done using composition to avoid inheritance.
All these classes and stuff... it might be making the problem way too abstract for what it really is. Many different ways to represent tetris pieces (stackoverflow.com/questions/233850/…) and many different ways to manipulate them. If it's for an intro course I wouldn't worry about OOP. Just my opinion, not a real answer to your question.
Having said that, one could suffice with simply a Board and Piece class.
Board class: Encapsulates a simple 2d array of rectangles. Properties like currentpiece, nextpiece. Routines like draw(), fullrows(), drop(), etc.. which manipulate the current piece and the filled in board squares.
Piece class: Encapsulates an array of unsigned 16 bit numbers encoding the pieces in their various rotations. You would track color, current location, and rotation. Perhaps one routine, rotate() would be necessary.
The rest, would be, depending on the environment, handling keyboard events etc...
I've found that placing too much emphasis on design tends to make people forget that what they really need to do is to get something running. I'm not saying don't design, I'm saying that more often than not, there is more value in getting something going, giving you traction and motivation to keep going.
I would say, to the class, you have X hours to make a design for a tetris game. Then they would need to turn in that design. Then I would say, you have X days, to get something running based on the design you turned in or even not based on the design.
One(EDIT: OnePiece
interface, with seven classes that implement that interface for the individual pieces (which would also enable the OOP course to discuss interfaces)Piece
class. See comments)- I would have a
BlockGrid
class that can be used for any map of blocks - both the board, and the individual pieces.BlockGrid
should have methods to detect intersections - for example,boolean intersects(Block block2, Point location)
- as well as to rotate a grid (interesting discussion point for the course: If theBoard
doesn't need to rotate, should arotate()
method be inBlockGrid
?). For a Piece,BlockGrid
would represent be a 4x4 grid. - I would create a
PieceFactory
with a methodgetRandomShape()
to get an instance of one of the seven shapes - For manipulating the piece, I'd get into a Model-View-Controller architecture. The Model is the Piece. The Controller is perhaps a
PieceController
, and would also allow or disallow legal/illegal moves. The thing that would show the Piece on the screen is aPieceView
(hrm, or is it aBlockGridView
that can showPiece.getBlockGrid()
? Another discussion point!)
There are multiple legitimate ways to architect this. It would benefit the course to have discussions on the pro's and con's of different OOP principles applied to the problem. In fact, it might be interesting to compare and contrast this with a non-OOP implementation that just uses arrays to represent the board and pieces.
EDIT: Claudiu helped me realize that the BlockGrid
would sufficiently differentiate pieces, so there is no need for a Piece
interface with multiple subclasses; rather, an instance of a Piece
class could differ from other instances based on its BlockGrid
.
Piece class: I think that a single class for all the pieces is sufficient. The class functions shoudl be general enough to work for any piece, so there is no need to subclass.
Piece Class Representation: I believe that a 4x4 array is probably a better way as you will then find it much easier to rotate the piece.
Location: Location should definitely be stored by the board, not the piece as otherwise you would have to go through the entire set of blocks to ensure that no two blocks are in the same position.
Generating a Piece: Honestly for this one I do not feel that it will make too much of a difference. Having said that, I would prefer a static function as there is really not so much to this function that it warrants its own class.
Manipulating the Current Piece: I would just implement a getCurrent function as I feel that there is no need to overcomplicate the problem by adding in an extra class to serve as a proxy.
This is how I would do it, but there are many different ways, and at the end of the day, the thing to focus on is simply getting the program running.
精彩评论