I am writing another Tetris, just for myself. After Martin and McConel, you want a simple and beautiful architecture. Until a certain moment everything was possible, but crutches began to creep out.

There is the following simplified piece of architecture:

  1. The figure on the field:

    class Figure{ List<Shape> shapes; Shape currentShape; } 
  2. Shape shape:

     class Shape{ private int[][] model; } 

We need a class that creates different shapes ("T", "J", "L" and others). So far, the fantasy only lasted for this:

 public class FigureFactory { private static List< Figure > figures = init(); private static List< Figure > init() { ArrayList< Figure > figures = new ArrayList<>(); Figure J = null, L = null, O = null, S = null, T = null, Z = null; J = new Figure( new Position( 0, 0 ), new ArrayList< Figure.Shape >() {{ add( new Figure.Shape( new int[][]{ { 0, 1 }, { 0, 1 }, { 1, 1 } } ) ); add( new Figure.Shape( new int[][]{ { 1, 0, 0 }, { 1, 1, 1 }, } ) ); add( new Figure.Shape( new int[][]{ { 1, 1 }, { 1, 0 }, { 1, 0 } } ) ); add( new Figure.Shape( new int[][]{ { 1, 1, 1 }, { 0, 0, 1 }, } ) ); }} ); ... return Arrays.asList( J, L, O, S, T, Z ); } public static Figure getJ() { return figures.get( 0 ); } public static Figure getL() { return figures.get( 1 ); } ... public static Figure getRandomFigure() { return figures.get( ( (int) ( Math.random() * figures.size() ) ) ); } 

Problems and questions are as follows:

  • Class name The Factory suffix, as Google suggests, is used when using the “Abstract Factory” pattern, but here it is clearly not there. What is the best fit? Builder does not fit for the same reason. Is there any other pattern for this?

  • Immunity. The main problem - Figure not immutable (immutable), because you need to switch the current state ( Shape ), turning the figure. Now each subsequent call to getL() returns a reference to the same object, changing which will spoil it for subsequent calls. You can move the creation of each specific shape from init() to getL() / getlJ() and return new objects, but when to be with getRandomFigure() ? We need a method that returns any random shape of all possible, but without a list of possible, it is not clear how to do this. I would like without reflection. The question is not how to make it work, but how to make it nice and neat.

  • Is it reasonable to apply inheritance here by implementing a group of classes like JFigure , TFigure , LFigure and hiding the whole creation there?

I would be grateful for any links \ books and especially for criticism.

    1 answer 1

    For a start, names are not so important for proper architecture. Whether or not the Factory suffix is ​​in your factory is not so important.

    Now about what you already have.

     class Figure{ List<Shape> shapes; Shape currentShape; } 

    This is bad. You describe here not one figure, but all that are on the field. So this is class Board or class Arena .

    Then, the figures that have already fallen. You do not need to know the original pieces, because they left the game. In addition, cells can be removed from them when the line is filled and deleted. So, you need only one - the current - figure (and perhaps the next figure for the clue). For the rest, you need a list of fields, a sign of whether or not each field is occupied, and perhaps the color of the field if you want to assign different colors to different shapes.

    Next, the figure. The figure is not immutable! It moves across the field and turns! But its form is immutable. This means that we have classes Shape (immutable form) and Figure (since we already renamed the old class Figure into Board ), which contains Shape , its orientation and position. And he can, of course, issue a list of his cells.

    Then, you need a game logic. Which determines that on each tick the figure travels down, rotates on command (with the possibility check), and upon reaching the occupied cells, it breaks up into mold and lime honey, occupied cells of the same color as the original figure. And yet, the logic is trying to place a new figure, and if it cannot, the end of the game.

    Then, you need a shape generator, which produces a new shape at the request.

    Well, the input from the user and UI, without this, even in Tetris in any way.


    OK, the clarification in the commentary says that Figure does describe the shape. Then, it seems to me, to have a list of Shape 's inside is completely unnecessary. Let the figure know the list of its cells. Then we do not need types of shapes. We get a simple factory of figures:

     class Rotation { public rotateAbout(Point origin, Point what) { int dx = k11 * (what.X - origin.X) + k12 * (what.Y - origin.Y); int dy = k21 * (what.X - origin.X) + k22 * (what.Y - origin.Y); return new Point(origin.X + dx, origin.Y + dy); } static public Rotation Identity() { return new Orientation(1, 0, 0, 1); } static public Rotation Next() { return new Orientation(k12, -k11, k22, -k21); } static public Rotation Prev() { return new Orientation(-k12, k11, -k22, k21); } } class Shape { public Collection<Point> getPoints() { return points; } } class Figure { Rotation rotation; Vector locationOffset; final Shape shape; public Collection<Point> getPoints() { ArrayList<Point> result = new ArrayList<Point>(); for (Point p : shape.getPoints()) { Point rotated = rotation.rotateAbout(shape.Origin, p); Point shifted = rotated.add(locationOffset); result.add(shifted); } } } 

    Well, now

     static class ShapeFactory { final Collection<Point> lPoints = new ArrayList<Point>() { new Point(0, 0), new Point(0, 1), new Point(0, 2), new Point(1, 2) }; final Shape L = new Shape(lPoints); public Shape getL() { return L; } final ArrayList<Shape> allShapes = new ArrayList<Shape>() { L, S, // ну и все остальные } public Shape getRandom() { int index = random.nextInt(allShapes.length); return allShapes[index]; } } 

    So you have an immutable Shape and a mutable Figure .


    Inheritance of Shape 's (and especially figures) seems to me to be overdizaired, you just need to virtualize data.

    • You didn’t understand a bit, the current Figure contains exactly the list of the imbutable forms (Shape), and not all the figures on the field. The field is generally a separate class and everything is in order :) Each such form is a different orientation of one figure. In figure O, for example, there is only 1 form, and in I - two (horizontal and vertical). The logic of the game itself is not complicated and is also almost ready. And I asked about the generator :) - Slavuti4
    • @ Slavuti4: Ok, added the answer (more). - VladD