Encapsular uma solicitação como um objeto, desta forma permitindo parametrizar clientes em diferentes solicitações, enfileirar ou fazer registro (log) de solicitações e suportar operações que podem ser desfeitos.
Uma Entidade de Pagamento, por exemplo uma casa loterica, efetua pagamentos de boletos dos bancos A e B e seu sistema possui um módulo para efetuar o pagamento de cada um deles.Como para cada banco o registro de pagamento é realizado de forma diferente, o uso do padrao Command permite que uma solicitação seja encapsulada em um objeto command, permitindo parametrizar clientes com diferentes solicitações. No exemplo abaixo, a solicitação supracitada é a operação registerPayment que pode ser efetuada por um cliente do tipo PaymentBankA, que registra pagamentos de boletos do Banco A, ou por um cliente do tipo PaymentBankB que registra pagamentos de boletos do Banco B.
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package commandBRI;
/**
*
* @author patrickbelem
*/
public class BankSlip {
protected int code;
protected String shopName;
protected double value;
protected int bankCode;
protected String status;
public BankSlip(int code, String shopName, int bankCode, double value, String status) {
this.code = code;
this.shopName = shopName;
this.bankCode = bankCode;
this.value = value;
this.status = status;
}
public void setCode(int code) {
this.code = code;
}
public void setShopName(String shopName) {
this.shopName = shopName;
}
public void setcodBanco(int bankCode) {
this.bankCode = bankCode;
}
public void setValue(double value) {
this.value = value;
}
public void setStatus(String status) {
this.status = status;
}
public int getCode() {
return code;
}
public String getShopName() {
return shopName;
}
public int getBankCode() {
return bankCode;
}
public double getValue() {
return value;
}
public String getStatus() {
return status;
}
}
package commandBRI;
/**
*
* @author patrickbelem
*/
public class Client {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
PaymentEntity pe1 = new PaymentEntity("Loteria Cavaleiros", "lottery");
BankSlip bankSlip1 = new BankSlip(2520,"Casas Bahia", 1, 1000,"Not Paid");
BankSlip bankSlip2 = new BankSlip(3250,"Eletrocenter", 2, 2000,"Not Paid");
PaymentBankA bankA = new PaymentBankA();
PaymentBankB bankB = new PaymentBankB();
pe1.makePayment(bankSlip1, bankA);
pe1.makePayment(bankSlip2, bankB);
/*
Uma loteria efetua pagamentos de boletos de diversos bancos e seu sistema possui um módulo para efetuar o pagamento de cada um deles.
Assim todo boleto do banco A, cujo código do banco seja 1, este boleto deverá ser recebido pelo módulo do banco A, caso o boleto seja
do banco B, cujo código do banco seja 2, deverá ser pago pelo módulo do banco B.
*/
}
}
package commandBRI;
/**
*
* @author patrickbelem
*/
public class PaymentBankA implements PaymentCommand {
double value;
@Override
public void registerPayment(BankSlip bankSlip) {
if(bankSlip.getBankCode()==1){
value = bankSlip.getValue() - bankSlip.getValue() * 0.02;
bankSlip.setValue(value);
bankSlip.setStatus("Paid");
System.out.println("Bank slip of bank A:\nBank Slip:"+ bankSlip.getCode() +"\nShop:"+ bankSlip.getShopName()+
"\nAmount Received:"+ bankSlip.getValue()+ "\nBank Code:"+ bankSlip.getBankCode() +"\nStatus Current:"+ bankSlip.getStatus());
System.out.println("========================");
}
}
}
package commandBRI;
/**
*
* @author patrickbelem
*/
public class PaymentBankB implements PaymentCommand {
double value;
@Override
public void registerPayment(BankSlip bankSlip){
if(bankSlip.getBankCode()==2){
value = bankSlip.getValue() - bankSlip.getValue() * 0.03;
bankSlip.setValue(value);
bankSlip.setStatus("Paid");
System.out.println("Bank slip of bank B:\nBank Slip:"+ bankSlip.getCode() +"\nShop:"+ bankSlip.getShopName()+
"\nAmount Received:"+ bankSlip.getValue()+ "\nBank Code:"+ bankSlip.getBankCode() +"\nStatus Current:"+ bankSlip.getStatus());
System.out.println("========================");
}
}
}
package commandBRI;
/**
*
* @author patrickbelem
*/
public interface PaymentCommand {
void registerPayment(BankSlip bankSlip);
}
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package commandBRI;
/**
*
* @author patrickbelem
*/
public class PaymentEntity {
protected String name;
protected String type;
public PaymentEntity(String name, String type) {
this.name = name;
this.type = type;
}
public void setType(String type) {
this.type = type;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public String getType() {
return type;
}
public void makePayment(BankSlip bankSlip, PaymentCommand paymentCommand ) {
paymentCommand.registerPayment(bankSlip);
}
}
Durante a concepção de um modelo de carro, testes precisam ser realizados em protótipos para garantir a qualidade, a segurança e o desempenho do veículo. Parâmetros de dimensão como largura, altura, volume do porta-malas e distância entre eixos podem ser ajustados em cada teste na expectativa de melhorar o produto final. No entanto, tais ajustes podem também piorar o modelo não só na eficiência como na estética, um parâmetro relevante para o consumidor. Nesse caso, o modelo de carro deve ser revertido para o estado anterior ao teste. Uma interface CarAdjustment é definida com as operações execute e unexecute. Para cada ação de ajuste de parâmetros em um teste, uma classe que implementa CarAdjustment é definida (HeightWidthAdjustment, CargoVolumeAdjustment, WheelbaseAdjustment). Cada uma delas deve receber os valores dos parâmetros que devem ser alterados e a referência do objeto receiver do tipo CarModel alvo da operação. Também são definidas as implementações das operações execute e unexecute, que realizam e revertem os ajustes, respectivamente. A classe CarModelChecker armazena um grupo de instâncias de classes do tipo CarAdjustment e chama as operações execute de cada uma delas através da operação doAdjustments. Caso a aplicação encontre uma piora nos ajustes realizados no teste, a operação undoAdjustments é responsável por percorrer esses ajustes salvos em CarModelChecker e executar a operação unexecute de todos eles. Quando um novo teste é realizado, resetAdjustments é chamado para remover todas as referências de CarAdjustment salvas em CarModelChecker. A classe CarModelDefiner é responsável por criar os objetos do tipo CarAdjustment, definindo seus parâmetros de ajuste e a instância de CarModel que será alvo dessas alterações.
package command;
//Classe que representa um modelo de carro
public class CarModel {
private float height;
private float width;
private float cargoVolume;
private float wheelbase;
public String getCarInfo(){
return "Height: " + this.height +
"\nWidth: " + this.width +
"\nCargo volume: " + this.cargoVolume +
"\nWheelbase: " + this.wheelbase;
}
public float getHeight() {
return height;
}
public void setHeight(float height) {
this.height = height;
}
public float getWidth() {
return width;
}
public void setWidth(float width) {
this.width = width;
}
public float getCargoVolume() {
return cargoVolume;
}
public void setCargoVolume(float cargoVolume) {
this.cargoVolume = cargoVolume;
}
public float getWheelbase() {
return wheelbase;
}
public void setWheelbase(float wheelbase) {
this.wheelbase = wheelbase;
}
}
package command;
//Interface que declara as operações para executar e reverter um ajuste
public interface CarAdjustment {
public boolean execute();
public void unexecute();
}
package command;
//Classe abstrata que implementa CarAdjustment e
//armazena uma instância de CarModel em seus atributos
public abstract class CarAdjustmentImpl implements CarAdjustment{
CarModel carModel;
public CarAdjustmentImpl(CarModel carModel){
this.carModel = carModel;
}
}
package command;
public class CargoVolumeAdjustment extends CarAdjustmentImpl{
private final float cargoVolumeAdd;
//Recebe como parâmetro no construtor o quanto o atributo
//deve ser ajustado e o objeto alvo do ajuste
public CargoVolumeAdjustment(float cargoVolume, CarModel model){
super(model);
this.cargoVolumeAdd = cargoVolume;
}
//Executa o ajuste no objeto recebido
public boolean execute(){
float cargoVolume = carModel.getCargoVolume();
carModel.setCargoVolume(cargoVolume + cargoVolumeAdd);
return true;
}
//Reverte o ajuste no objeto recebido
public void unexecute(){
float cargoVolume = carModel.getCargoVolume();
carModel.setCargoVolume(cargoVolume - cargoVolumeAdd);
}
}
package command;
import java.util.ArrayList;
public class CarModelChecker {
private ArrayList carAdjustments; //Armazena uma lista de ajustes
private ArrayList executedAdjustments; //Armazena se o ajuste na posição equivalente foi executado
private int adjustmentsLength; //Armazena o tamanho da lista de ajustes
public CarModelChecker(){
carAdjustments = new ArrayList<>();
executedAdjustments = new ArrayList<>();
adjustmentsLength = 0;
}
//Adiciona um ajuste à lista
public void addAdjustment(CarAdjustment adjustment){
carAdjustments.add(adjustment);
executedAdjustments.add(false);
adjustmentsLength++;
}
//Reseta os ajustes, limpando as listas
public void resetAdjustment(){
carAdjustments.clear();
executedAdjustments.clear();
adjustmentsLength = 0;
}
//Percorre a lista, executando os ajustes salvos.
public void doAdjustments(){
for(int iterator = 0; iterator < adjustmentsLength; iterator++){
executedAdjustments.set(iterator, carAdjustments.get(iterator).execute());
}
}
//Percorre a lista inversamente, revertendo os ajustes executados.
public void undoAdjustments(){
for (int iterator = adjustmentsLength-1; iterator >=0; iterator--){
if(executedAdjustments.get(iterator)){
carAdjustments.get(iterator).unexecute();
executedAdjustments.set(iterator, false);
}
}
}
}
package command;
//Classe responsável por criar os objetos que realizam ajustes
//na instância de CarModel armazenada em seus atributos.
public class CarModelDefiner {
private CarModel carModel;
public CarModelDefiner(CarModel carModel){
this.carModel = carModel;
}
public CarAdjustment createHeightWidthAdjustment(float heigth,
float width){
return new HeightWidthAdjustment(heigth, width, carModel);
}
public CarAdjustment createCargoVolumeAdjustment(float cargoVolume){
return new CargoVolumeAdjustment(cargoVolume, carModel);
}
public CarAdjustment createWheelbaseAdjustment(float wheelbase){
return new WheelbaseAdjustment(wheelbase, carModel);
}
public CarModel getCarModel() {
return carModel;
}
}
package command;
public class Main {
public static void main(String[] args) {
CarModel carModel = new CarModel();
carModel.setHeight(1.4f);
carModel.setWidth(1.8f);
carModel.setCargoVolume(1.3f);
carModel.setWheelbase(3.9f);
CarModelDefiner carModelDefiner = new CarModelDefiner(carModel);
System.out.println("Model before adjustments: \n" + carModel.getCarInfo() + "\n\n");
CarModelChecker carModelChecker = new CarModelChecker();
carModelChecker.addAdjustment(
carModelDefiner.createHeightWidthAdjustment(0.4f, -0.6f)
);
carModelChecker.addAdjustment(
carModelDefiner.createCargoVolumeAdjustment(1)
);
carModelChecker.addAdjustment(
carModelDefiner.createWheelbaseAdjustment(-1.8f)
);
carModelChecker.doAdjustments();
System.out.println("Car after adjustments: \n" + carModel.getCarInfo() + "\n\n");
if(!testModel(carModel)) {
carModelChecker.undoAdjustments();
System.out.println("Car after test failure: \n" + carModel.getCarInfo() + "\n\n");
}
}
public static boolean testModel(CarModel carModel){
float value = (carModel.getWheelbase()/carModel.getHeight())
* carModel.getWidth();
return value < 2*carModel.getWheelbase()
&& value > 2.5*carModel.getCargoVolume();
}
}
package command;
public class HeightWidthAdjustment extends CarAdjustmentImpl{
private final float heightAdd;
private final float widthAdd;
//Recebe como parâmetro no construtor o quanto os atributos
//devem ser ajustados e o objeto alvo do ajuste
public HeightWidthAdjustment(float height, float width, CarModel model){
super(model);
this.heightAdd = height;
this.widthAdd = width;
}
//Executa o ajuste no objeto recebido
public boolean execute(){
float height = carModel.getHeight();
float width = carModel.getWidth();
carModel.setHeight(height + heightAdd);
carModel.setWidth(width + widthAdd);
return true;
}
//Reverte o ajuste no objeto recebido
public void unexecute(){
float height = carModel.getHeight();
float width = carModel.getWidth();
carModel.setHeight(height - heightAdd);
carModel.setWidth(width - widthAdd);
}
}
package command;
public class WheelbaseAdjustment extends CarAdjustmentImpl {
private final float wheelbaseAdd;
//Recebe como parâmetro no construtor o quanto o atributo
//deve ser ajustado e o objeto alvo do ajuste
public WheelbaseAdjustment(float wheelbase, CarModel model){
super(model);
this.wheelbaseAdd = wheelbase;
}
//Executa o ajuste no objeto recebido
public boolean execute(){
float wheelbase = carModel.getWheelbase();
carModel.setWheelbase(wheelbase + wheelbaseAdd);
return true;
}
//Reverte o ajuste no objeto recebido
public void unexecute(){
float wheelbase = carModel.getWheelbase();
carModel.setWheelbase(wheelbase - wheelbaseAdd);
}
}