Guilherme Almeida

Software Engineer

Blog and portfolio with some projects about software development.

"8 Puzzle" a game in Java

This is a sliding tile game created with Java to practice Object-Oriented Programming concepts and Design Patterns.

#java#OOP#design-patterns#TDD#MVC#game

Index


1 Intro

This is a project made for a college assignment in the subject of Object-Oriented Programming. The proposed challenge was to create the 8Puzzle (sliding tile game with eight pieces) in the Java language, applying the object-oriented methodologies to architect the software in an organized and reusable way. Also using technologies and libraries like Junit, JDBC and Swing. To create tests, serialization of the game state in a database and the graphical interface.

Source code: https://github.com/GuiSAlmeida/8puzzle-java

1.1 Goals

  • Apply Object-Oriented concepts taught in class to build software, such as:
    • Abstraction
    • Encapsulation
    • Composition
    • Inheritance
    • Polymorphism
  • Create test-oriented project TDD.
  • Keep the code clean without bad smells, with semantic naming of classes, methods and attributes.
  • Implement independent MVC layers.
  • Use Design Patterns.
  • Serialize game state in Postgres database.

1.2 How the game works

8Puzzle is a simple game consisting of a 3 x 3 board (containing 9 squares). One of the squares is empty (in my case use 0). The objective is to move to squares in different positions and have the numbers displayed in the correct sequence.
Move-in-a-game-of-8-puzzle


2 Tests (TDD)

Starting with the tests, using the framework Junit 5. They helped not only to maintain the quality and functionality of the code, but also to formulate how the board moves should happen.

Show me the code:
Test in the TesteControleTabuleiro class to simulate movement of the board.

package br.ies.aps.jogooito.controle;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.Before;
import org.junit.Test;
import br.ies.aps.jogooito.modelo.Tabuleiro;

public class TesteControleTabuleiro {
	private TabuleiroControle tabuleiroControle;
	
	@Before
	public void configuracao() {
		tabuleiroControle = new TabuleiroControle(new Tabuleiro());
	}

	/**
	* The test takes the position of the top field from the position of the pointer (empty field or 0)
	* and stores it in a variable to compare if when moving the pointer up
	* will be in the same position that was stored in the variable.
	* @link https://github.com/GuiSAlmeida/8puzzle-java/blob/master/test/br/ies/aps/jogooito/controle/TesteControleTabuleiro.java
	*/
	@Test
	public void moverPonteiroTabuleiroParaCima() {
		Integer posicaoAcima = tabuleiroControle.getTabuleiro().getPonteiro().getCampoDeCima().getNumero();
		tabuleiroControle.moverPraCima();
		assertEquals(posicaoAcima, tabuleiroControle.getTabuleiro().getPonteiro().getCampoDeBaixo().getNumero());
	}

	...
}

3 Independent Layers (MVC)

Defined the architecture of how the software elements will interact with each other.

3.1 Model

Here are the classes that represent the model of the system. The Model layer is isolated, containing the business rules and the classes that compose it cannot know ANYTHING about the external environment, that is, there must be no references to classes from other layers.

Show me the code:
Jogador class owns its private data and does not have access to data from other layers:

package br.ies.aps.jogooito.modelo;

public class Jogador {
	private String jogadorNome;
	private Boolean ganhador = false;
	private Integer idJogador;
	private Integer jogadas = 0;
	
	public Jogador(String nome) {
		setJogadorNome(nome);
	}
	
	public String getJogadorNome() {
		return jogadorNome;
	}

	public void setJogadorNome(String jogador) {
		this.jogadorNome = jogador;
	}
	
	public Integer getJogadas() {
		return jogadas;
	}	
	
	public void setJogadas(Integer jogadas) {
		this.jogadas = jogadas;
	}	
	
	public Boolean getGanhador() {
		return ganhador;
	}	
	
	public void setGanhador(Boolean ganhador) {
		this.ganhador = ganhador;
	}	
	
	public Integer getIdJogador() {
		return idJogador;
	}	
	
	public void setIdJogador(Integer idJogador) {
		this.idJogador = idJogador;
	}
}

3.2 Controller

In the controller is the class responsible for transforming events generated by the interface, changing the model.

Show me the code:
TabuleiroControle class connects interface to board state:

package br.ies.aps.jogooito.controle;

import br.ies.aps.jogooito.modelo.Tabuleiro;

public class TabuleiroControle {
	private Tabuleiro tabuleiro;

	public TabuleiroControle(Tabuleiro tabuleiro) {
		this.setTabuleiro(tabuleiro);
	}

	public Tabuleiro getTabuleiro() {
		return tabuleiro;
	}

	public void setTabuleiro(Tabuleiro tabuleiro) {
		this.tabuleiro = tabuleiro;
	}

	public void moverPraCima() {
		tabuleiro.getPonteiro().moverParaCima();
		tabuleiro.setPonteiro(tabuleiro.getPonteiro().getCampoDeCima());
	}

	public void moverPraBaixo() {
		tabuleiro.getPonteiro().moverParaBaixo();
		tabuleiro.setPonteiro(tabuleiro.getPonteiro().getCampoDeBaixo());
	}

	public void moverPraEsquerda() {
		tabuleiro.getPonteiro().moverParaEsquerda();
		tabuleiro.setPonteiro(tabuleiro.getPonteiro().getCampoDaEsquerda());
	}

	public void moverPraDireita() {
		tabuleiro.getPonteiro().moverParaDireita();
		tabuleiro.setPonteiro(tabuleiro.getPonteiro().getCampoDaDireita());
	}
}

3.3 View

User interface layer, where the user sees the state of the model and can manipulate the interface to activate business logic.

Show me the code:
TelaControle class responsible for the interface of the controls part and receives user inputs:

package br.ies.aps.jogooito.view.swing.tela;

...

public class TelaControle extends JPanel implements KeyListener, TabuleiroObservador {
	private Tabuleiro tabuleiro;
	private Jogador jogador;
	private TelaTabuleiro telaTabuleiro;
	private JLabel jogadasLabel;
	private BotaoMovimentoCima botaoCima;
	private BotaoMovimentoBaixo botaoBaixo;
	private BotaoMovimentoDireita botaoDireita;
	private BotaoMovimentoEsquerda botaoEsquerda;

	public TelaControle(Tabuleiro tabuleiro, TelaTabuleiro telaTabuleiro, Jogador jogador) {
		this.tabuleiro = tabuleiro;
		this.telaTabuleiro = telaTabuleiro;
		this.jogador = jogador;
		geraControleTabuleiro();
		this.tabuleiro.registrarObservador(this);
	}

	private void geraControleTabuleiro() {
		GridBagLayout layout = new GridBagLayout();
		GridBagConstraints posicao = new GridBagConstraints();
		posicao.fill = GridBagConstraints.HORIZONTAL;

		setLayout(layout);

		posicao.gridy = 0;
		posicao.gridx = 0;
		JLabel jogadorLabel = new JLabel(String.format("Jogador: %s", jogador.getJogadorNome()));
		add(jogadorLabel, posicao);

		posicao.gridy = 1;
		posicao.gridx = 0;
		posicao.gridwidth = 3;
		jogadasLabel = new JLabel(String.format("Jogadas: %d", jogador.getJogadas()));
		add(jogadasLabel, posicao);

		posicao.gridwidth = 1;
		posicao.gridy = 0;
		posicao.gridx = 5;
		botaoCima = new BotaoMovimentoCima("↑", tabuleiro, telaTabuleiro, this, jogador);
		add(botaoCima, posicao);

		posicao.gridy = 2;
		posicao.gridx = 5;
		botaoBaixo = new BotaoMovimentoBaixo("↓", tabuleiro, telaTabuleiro, this, jogador);
		add(botaoBaixo, posicao);

		posicao.gridy = 1;
		posicao.gridx = 6;
		botaoDireita = new BotaoMovimentoDireita("→", tabuleiro, telaTabuleiro, this, jogador);
		add(botaoDireita, posicao);

		posicao.gridy = 1;
		posicao.gridx = 4;
		botaoEsquerda = new BotaoMovimentoEsquerda("←", tabuleiro, telaTabuleiro, this, jogador);
		add(botaoEsquerda, posicao);
	}

	public void atualizaJogadas(Integer numero) {
		jogadasLabel.setText(String.format("Jogadas: %d", numero));
	}

	public void finalizaJogadas(Integer numero) {
		jogadasLabel.setText(String.format("Venceu o jogo com %d jogadas!!", numero));
	}

	public void verificaFimJogo(Tabuleiro tabuleiro) {
		if (tabuleiro.verificaFimJogo()) {
			finalizaJogadas(jogador.getJogadas() + 1);
			jogador.setGanhador(true);
			JogadorDAO jogadorDAO = new JogadorDAO(jogador);
			try {
				jogadorDAO.atualizaBanco(jogador.getIdJogador());
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		} else {
			atualizaJogadas(jogador.getJogadas() + 1);
		}
	}

	@Override
	public void alterouEstadoTabuleiro(Tabuleiro tabuleiro) {
		verificaFimJogo(tabuleiro);
	};

	@Override
	public void keyPressed(KeyEvent event) {
		Map<Integer, Runnable> mapa = new HashMap<Integer, Runnable>();

		mapa.put(KeyEvent.VK_DOWN, new Runnable() {
			@Override
			public void run() {
				botaoBaixo.alteraEstadoTabuleiro();
			}
		});
		mapa.put(KeyEvent.VK_UP, new Runnable() {
			@Override
			public void run() {
				botaoCima.alteraEstadoTabuleiro();
			}
		});
		mapa.put(KeyEvent.VK_RIGHT, new Runnable() {
			@Override
			public void run() {
				botaoDireita.alteraEstadoTabuleiro();
			}
		});
		mapa.put(KeyEvent.VK_LEFT, new Runnable() {
			@Override
			public void run() {
				botaoEsquerda.alteraEstadoTabuleiro();
			}
		});
		
		mapa.get(event.getKeyCode()).run();
	}
}

4 Saving state in the database (Postgres)

For the serialization of the players' data and the state of the board, a Postres database was created, which, through classes following DAO and Factory design patterns, connect the application to the database.

4.1 Conceptual Modeling

image

4.2 Physical modeling

CREATE DATABASE IF NOT EXISTS jogo_oito;

CREATE TABLE jogador (
	id SERIAL PRIMARY KEY,
	nome varchar(50),
	jogadas integer,
	ganhador boolean,
	id_tabuleiro integer
);

CREATE TABLE TABULEIRO (
	id SERIAL PRIMARY KEY,
	campo_cima_esquerda integer,
	campo_cima_meio integer,
	campo_cima_direita integer,
	campo_meio_esquerda integer,
	campo_meio_meio integer,
	campo_meio_direita integer,
	campo_baixo_esquerda integer,
	campo_baixo_meio integer,
	campo_baixo_direita integer
);

ALTER TABLE jogador
	ADD CONSTRAINT id_tabuleiro_fk 
	FOREIGN KEY (id_tabuleiro)
	REFERENCES tabuleiro (id)
	ON UPDATE CASCADE
	ON DELETE NO ACTION;

5 Design Patterns

Design Patterns are typical solutions to common problems in software design. They are like prefabricated blueprints that you can customize to solve a recurring design problem in your code.

5.1 Factory

The FabricaConexao class implements the Factory design pattern, which preaches the encapsulation of the construction (manufacturing) of complicated objects.

Show me the code:
The FabricaConexao class has the necessary data to create a database connection when instantiated:

package br.ies.aps.jogooito.modelo.DAO;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class FabricaConexao {	
	public static Connection getConexao() {
		try {
			final String url = "jdbc:postgresql://host:5432/nome_banco";
			final String user = "username";
			final String password  = "senha_forte";
			return DriverManager.getConnection(url, user, password);
			
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}
	}
}

5.2 DAO (Data Access Object)

The DAO pattern is a design pattern that abstracts and encapsulates the data access mechanisms by hiding the execution details of the data source.

Show me the code:
JogadorDAO class responsible for inserting player data into the database:

package br.ies.aps.jogooito.modelo.DAO;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import br.ies.aps.jogooito.modelo.Jogador;

public class JogadorDAO {
	private Integer idJogador;
	private String nome;
	private Integer jogadas;
	private Boolean ganhador;

	public JogadorDAO(Jogador jogador) {
		this.nome = jogador.getJogadorNome();
		this.jogadas = jogador.getJogadas();
		this.ganhador = jogador.getGanhador();
	}

	public void incluirBanco(Integer idTabuleiro) throws SQLException {
		Connection conexao;
		String sql;
		ResultSet resultado;

		try {
			conexao = FabricaConexao.getConexao();
			sql = "INSERT INTO jogador (nome, jogadas, ganhador, id_tabuleiro) VALUES (?,?,?,?) RETURNING id;";
			PreparedStatement statement = conexao.prepareStatement(sql);
			statement.setString(1, nome);
			statement.setInt(2, jogadas);
			statement.setBoolean(3, ganhador);
			statement.setInt(4, idTabuleiro);
			statement.execute();

			resultado = statement.getResultSet();
			resultado.next();

			idJogador = resultado.getInt("id");
			resultado.close();
			statement.close();
			conexao.close();

		} catch (SQLException e) {
			throw new RuntimeException(e);
		}
	}
	
	public void atualizaBanco(Integer idJogador) throws SQLException {
		Connection conexao;
		String sql;
		PreparedStatement statement;
		
		try {
			conexao = FabricaConexao.getConexao();
			sql = "UPDATE jogador SET jogadas = ?, ganhador = ? WHERE id = ?;";

			statement = conexao.prepareStatement(sql);
			statement.setInt(1, jogadas);
			statement.setBoolean(2, ganhador);
			statement.setInt(3, idJogador);
			statement.execute();			
			statement.close();
			conexao.close();			
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}
	}

	public Integer getIdJogador() {
		return idJogador;
	}
}

5.3 Observer

Observer is a behavioral design pattern that allows you to define a subscription mechanism to notify multiple objects of any events that happen to the object they are observing.

Show me the code:
The Tabuleiro class which owns the methods and core values of the game, is the publisher. A list is created to register the observers and methods to add and notify them as soon as the state of the publisher changes.

package br.ies.aps.jogooito.modelo;

import java.util.ArrayList;
import java.util.List;

public class Tabuleiro {
	...

	private List<TabuleiroObservador> observadores = new ArrayList<>();

	public void registrarObservador(TabuleiroObservador observador) {
		observadores.add(observador);
	}

	public void notificarObservadores(Tabuleiro tabuleiro) {
		for (TabuleiroObservador observador : observadores) {
			observador.alterouEstadoTabuleiro(this);
		}
	}

  ...
}

The TabuleiroObservador interface declares the notification interface. It consists of a single update method (alterouEstadoTabuleiro()) where the publisher (Tabuleiro) passes its state at each update.

package br.ies.aps.jogooito.modelo;

public interface TabuleiroObservador {
	public void alterouEstadoTabuleiro(Tabuleiro tabuleiro);
}

Subscribers register as watchers and perform certain actions in response to notifications sent by the publisher. All these classes must implement the same interface (TabuleiroObservador) so that the publisher is not attached to concrete classes. The class TelaControle implements and overrides the method of TabuleiroObservador and registers itself as an observer.

package br.ies.aps.jogooito.view.swing.tela;

...

public class TelaControle extends JPanel implements KeyListener, TabuleiroObservador {
	private Tabuleiro tabuleiro;
	private Jogador jogador;
	private TelaTabuleiro telaTabuleiro;
	private JLabel jogadasLabel;
	private BotaoMovimentoCima botaoCima;
	private BotaoMovimentoBaixo botaoBaixo;
	private BotaoMovimentoDireita botaoDireita;
	private BotaoMovimentoEsquerda botaoEsquerda;

	public TelaControle(Tabuleiro tabuleiro, TelaTabuleiro telaTabuleiro, Jogador jogador) {
		...

		this.tabuleiro.registrarObservador(this);
	}

	public void verificaFimJogo(Tabuleiro tabuleiro) {
		if (tabuleiro.verificaFimJogo()) {
			finalizaJogadas(jogador.getJogadas() + 1);
			jogador.setGanhador(true);
			JogadorDAO jogadorDAO = new JogadorDAO(jogador);
			try {
				jogadorDAO.atualizaBanco(jogador.getIdJogador());
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		} else {
			atualizaJogadas(jogador.getJogadas() + 1);
		}
	}

	@Override
	public void alterouEstadoTabuleiro(Tabuleiro tabuleiro) {
		verificaFimJogo(tabuleiro);
	};

	...
}

6 Object Orientation

The paradigm of OOP (Object-Oriented Programming) is an analysis, design and programming model based on the approximation between the real world and the virtual world, through the creation and interaction between objects, attributes, codes, methods, among others. others.

6.1 Abstraction

In object orientation, a class is an abstraction of existing entities in the domain of the software system. An abstract class is designed to represent abstract entities and concepts. This class is always a superclass that has no instances. It defines a template for functionality and provides an incomplete implementation (the generic part of that functionality) that is shared by a group of derived classes (subclasses). Each of the derived classes completes the functionality of the abstract class by adding specific behavior.

Show me the code:
The BotaoMovimento class which is derived to the subclasses BotaoMovimentoCima, BotaoMovimentoBaixo, BotaoMovimentoEsquerda and BotaoMovimentoDireita.

package br.ies.aps.jogooito.view.swing.botao;

import java.awt.event.ActionListener;
import java.sql.SQLException;
import javax.swing.JButton;

import br.ies.aps.jogooito.controle.TabuleiroControle;
import br.ies.aps.jogooito.modelo.Jogador;
import br.ies.aps.jogooito.modelo.Tabuleiro;
import br.ies.aps.jogooito.view.swing.tela.TelaControle;
import br.ies.aps.jogooito.view.swing.tela.TelaTabuleiro;

@SuppressWarnings("serial")
public abstract class BotaoMovimento extends JButton implements ActionListener {
	private Tabuleiro tabuleiro;
	private Jogador jogador;
	private TabuleiroControle tabuleiroControle;
	private TelaTabuleiro telaTabuleiro;
	private TelaControle controleTabuleiro;

	public BotaoMovimento(String posicao, Tabuleiro tabuleiro, TelaTabuleiro telaTabuleiro,
		TelaControle controleTabuleiro, Jogador jogador) {
		setText(posicao);
		addActionListener(this);
		setTabuleiro(tabuleiro);
		setJogador(jogador);
		setTabuleiroControle(new TabuleiroControle(tabuleiro));
		setTelaTabuleiro(telaTabuleiro);
		setControleTabuleiro(controleTabuleiro);
	}

	public abstract void alteraEstadoTabuleiro();

	public TabuleiroControle getTabuleiroControle() {
		return tabuleiroControle;
	}

	public void setTabuleiroControle(TabuleiroControle controle) {
		this.tabuleiroControle = controle;
	}

	public Tabuleiro getTabuleiro() {
		return tabuleiro;
	}
	
	public Jogador getJogador() {
		return jogador;
	}

	public void setJogador(Jogador jogador) {
		this.jogador = jogador;
	}

	public void setTabuleiro(Tabuleiro tabuleiro) {
		this.tabuleiro = tabuleiro;
	}

	public TelaTabuleiro getTelaTabuleiro() {
		return telaTabuleiro;
	}

	public void setTelaTabuleiro(TelaTabuleiro telaTabuleiro) {
		this.telaTabuleiro = telaTabuleiro;
	}

	public TelaControle getControleTabuleiro() {
		return controleTabuleiro;
	}

	public void setControleTabuleiro(TelaControle controleTabuleiro) {
		this.controleTabuleiro = controleTabuleiro;
	}
}

6.2 Encapsulation

Encapsulation is one of the pillars of object orientation, it serves to protect class data. Encapsulating an application's data means preventing them from being accessed improperly. For this, a structure is created where modifiers such as public, protected, private are used to restrict access to this data. And methods that can be used by any other class, without causing inconsistencies in the development commonly called getters and setters.

Show me the code:
The Campo class which has encapsulated attributes and methods (getters and setters) for the data to be consulted.

package br.ies.aps.jogooito.modelo;

public class Campo {
	private Campo cima;
	private Campo baixo;
	private Campo esquerda;
	private Campo direita;  
	private Integer numero;
	private Tabuleiro tabuleiro;

	public Campo(Integer numero, Tabuleiro tabuleiro) {
		this.setNumero(numero);
		this.cima = this;
		this.baixo = this;
		this.esquerda = this;
		this.direita = this;
		this.tabuleiro = tabuleiro;
	}

	public Integer getNumero() {
		return numero;
	}

	public void setNumero(Integer numero) {
		this.numero = numero;
	}

	public void trocaNumero(Campo origem, Campo destino) {
		Integer temporario = origem.getNumero();
		origem.setNumero(destino.getNumero());
		destino.setNumero(temporario);
	}

	public Campo getCampoDeCima() {
		return cima;
	}

	public void setCampoDeCima(Campo cima) {
		this.cima = cima;
	}

	public Campo getCampoDeBaixo() {
		return baixo;
	}

	public void setCampoDeBaixo(Campo baixo) {
		this.baixo = baixo;
	}

	public Campo getCampoDaEsquerda() {
		return esquerda;
	}

	public void setCampoDaEsquerda(Campo esquerda) {
		this.esquerda = esquerda;
	}

	public Campo getCampoDaDireita() {
		return direita;
	}

	public void setCampoDaDireita(Campo direita) {
		this.direita = direita;
	}

	public void moverParaCima() {
		trocaNumero(this, cima);
		this.tabuleiro.notificarObservadores(this.tabuleiro);
	}

	public void moverParaBaixo() {
		trocaNumero(this, baixo);
		this.tabuleiro.notificarObservadores(this.tabuleiro);
	}

	public void moverParaEsquerda() {
		trocaNumero(this, esquerda);
		this.tabuleiro.notificarObservadores(this.tabuleiro);
	}

	public void moverParaDireita() {
		trocaNumero(this, direita);
		this.tabuleiro.notificarObservadores(this.tabuleiro);
	}
}

6.3 Composition

The main reason to use composition is that it allows you to reuse code without modeling an is-a association as you do by using inheritance. That allows stronger encapsulation and makes your code easier to maintain.

Show me the code:
The object created from the Tabuleiro class instance will have objects of the Campo class, in this case when the Tabuleiro object is deleted, the Campo objects will also be deleted.

package br.ies.aps.jogooito.modelo;

import java.util.ArrayList;
import java.util.List;

public class Tabuleiro {
	private Campo ponteiro;
	private Campo campoMeio;
	private Campo campoMeioDireita;
	private Campo campoMeioEsquerda;
	private Campo campoBaixoMeio;
	private Campo campoBaixoDireita;
	private Campo campoBaixoEsquerda;
	private Campo campoCimaMeio;
	private Campo campoCimaDireita;
	private Campo campoCimaEsquerda;

	private Integer idTabuleiro;

	private List<TabuleiroObservador> observadores = new ArrayList<>();

	public Tabuleiro() {
		gerarCampos();
	}

	...

	public void gerarCampos() {
		campoCimaEsquerda = new Campo(Integer.valueOf(7), this);
		campoCimaMeio = new Campo(Integer.valueOf(2), this);
		campoCimaDireita = new Campo(Integer.valueOf(4), this);
		campoMeioEsquerda = new Campo(Integer.valueOf(5), this);
		campoMeio = new Campo(Integer.valueOf(0), this);
		campoMeioDireita = new Campo(Integer.valueOf(6), this);
		campoBaixoEsquerda = new Campo(Integer.valueOf(8), this);
		campoBaixoMeio = new Campo(Integer.valueOf(3), this);
		campoBaixoDireita = new Campo(Integer.valueOf(1), this);

		associarVizinhos();
		setPonteiro(campoMeio);
	}

	...
}

6.4 Inheritance

An object can have methods and attributes from another class by inheritance, this means that the class has all the characteristics of the inherited class, in addition to being able to have its own as well. One of the great advantages of using inheritance is code reuse. This reuse can be triggered when it is identified that the attribute or method of a class will be the same for the others.

Show me the code:
There is inheritance in the class BotaoMovimentoBaixo which inherits the functionalities of the abstract class BotaoMovimento and overrides the method alteraEstadoTabuleiro().

package br.ies.aps.jogooito.view.swing.botao;

import java.awt.event.ActionEvent;
import br.ies.aps.jogooito.modelo.Jogador;
import br.ies.aps.jogooito.modelo.Tabuleiro;
import br.ies.aps.jogooito.view.swing.tela.TelaControle;
import br.ies.aps.jogooito.view.swing.tela.TelaTabuleiro;

@SuppressWarnings("serial")
public class BotaoMovimentoBaixo extends BotaoMovimento {
	public BotaoMovimentoBaixo(String posicao, Tabuleiro tabuleiro, 
		TelaTabuleiro telaTabuleiro, TelaControle controleTabuleiro, Jogador jogador) {
		super(posicao, tabuleiro, telaTabuleiro, controleTabuleiro, jogador);
	}

	@Override
	public void actionPerformed(ActionEvent event) {
		alteraEstadoTabuleiro();
	}

	@Override
	public void alteraEstadoTabuleiro() {
		this.getTabuleiroControle().moverPraBaixo();
		this.getTelaTabuleiro().atualizarTelaTabuleiro(this.getTabuleiro());
		Integer jogadas = this.getJogador().getJogadas();
		this.getJogador().setJogadas(jogadas + 1);
	}
}

6.5 Polymorphism

Term used to describe specific situations in which something can occur in different ways. There are two types of polymorphism:

The Static or Overload polymorphism occurs when we have the same operation implemented several times in the same class. The choice of which operation to call depends on the signature of the overloaded methods.

The Dynamic or Overlapping polymorphism, which is the principle that allows classes derived from the same superclass to have the same methods (with the same signature) but different behaviors. Same signature = same amount and type of parameters.

Show me the code: The class BotaoMovimentoEsquerda overwrites the method alteraEstadoTabuleiro() inherited from the superclass BotaoMovimento according to its specificity, other classes that inherit from the same superclass will also have to implement the same method, but each one of a different way. Symbolizing the use of the polymorphism of overload.

package br.ies.aps.jogooito.view.swing.botao;

import java.awt.event.ActionEvent;
import br.ies.aps.jogooito.modelo.Jogador;
import br.ies.aps.jogooito.modelo.Tabuleiro;
import br.ies.aps.jogooito.view.swing.tela.TelaControle;
import br.ies.aps.jogooito.view.swing.tela.TelaTabuleiro;

@SuppressWarnings("serial")
public class BotaoMovimentoEsquerda extends BotaoMovimento {
	public BotaoMovimentoEsquerda(String posicao, Tabuleiro tabuleiro, TelaTabuleiro telaTabuleiro,
		TelaControle controleTabuleiro, Jogador jogador) {
		super(posicao, tabuleiro, telaTabuleiro, controleTabuleiro, jogador);
	}

	@Override
	public void actionPerformed(ActionEvent event) {
		alteraEstadoTabuleiro();
	}

	@Override
	public void alteraEstadoTabuleiro() {
		this.getTabuleiroControle().moverPraEsquerda();
		this.getTelaTabuleiro().atualizarTelaTabuleiro(this.getTabuleiro());
		Integer jogadas = this.getJogador().getJogadas();
		this.getJogador().setJogadas(jogadas + 1);
	};
}

7 Playing

You need to have Java 8 or above version. Download the file 8puzzle.jar, open the terminal and issue the following command.

java -jar 8puzzle.jar

8 References

https://refactoring.guru/pt-br/design-patterns/factory-method
https://refactoring.guru/pt-br/design-patterns/observer
http://www.gqferreira.com.br/artigos/ver/mvc-com-java-desktop-parte1
http://www.dsc.ufcg.edu.br/~jacques/cursos/map/html/arqu/mvc/mvc.htm


9 Conclusion

This project helped me a lot to put into practice concepts of Object Orientation, as well as to exercise the logic of how software architecture works in a project using MVC and some Design Patterns. So, what did you think of this project? Do you have any suggestions or criticism? Leave a reaction or a comment below. And thanks for visiting! 😉

Comments