Memento

Vantagens

Desvantagens

Exemplos

Exemplo #1

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 classe CarModelSnapshot é responsável por armazenar em seus atributos as propriedades de um CarModel, recebidas através do construtor e recuperáveis através de operações getters. CarModel representa um modelo de carro e possui uma operação que cria um novo CarModelSnapshot e outra que recupera o estado de um snapshot existente. É responsabilidade de CarModelChecker armazenar uma instância de CarModelSnapshot sem conhecer seu estado interno e, quando é necessário retornar CarModel ao estado de algum CarModelSnapshot, recuperar esse estado e passá-lo a CarModel através da operação restoreCarSnapshot.

Diagrama de Classe

Exemplo #1 - Diagrama

Participantes

  • Originator(CarModel): Amazena o estado atual de um objeto e é responsável pela criação de um memento que armazena esse estado.
  • Memento(CarModelSnapshot): Armazena o estado interno de um Originator, protegendo-o contra acessos externos.
  • Caretaker(CarModelChecker): Amazena um memento e o fornece para CarModel quando é necessário recuperá-lo. Não possui acesso ao estado interno de um memento.
 

Código

package memento;

public class Client {

    public static void main(String[] args) {
        CarModel carModel = new CarModel();

        //Um modelo de carro é criado e seus atributos são definidos
        carModel.setHeight(1.4f);
        carModel.setWidth(1.8f);
        carModel.setCargoVolume(1.3f);
        carModel.setWheelbase(3.9f);

        //Uma instância de CarModelChecker armazena o CarModel criado
        CarModelChecker modelChecker = new CarModelChecker(carModel);

        System.out.println("Model before adjustments: \n" + modelChecker.getCarModel().getCarInfo() + "\n\n");

        //O método doAdjustments é chamado para alterar os valores do carro dentro de CarModelChecker.
        modelChecker.doAdjustments(0.4f, -0.6f, 1, -1.8f);

        System.out.println("Car after adjustments: \n" + modelChecker.getCarModel().getCarInfo() + "\n\n");

        //Um teste é realizado e se o mesmo falhar, significa que o modelo deve retornar ao
        //seu estado anterior, salvo em CarModelChecker
        if(!testModel(modelChecker.getCarModel())){
            modelChecker.undoAdjustments(); //método que retorna carModel ao estado salvo em CarModelSnapshot

            System.out.println("Car after test failure: \n" + modelChecker.getCarModel().getCarInfo() + "\n\n");
        }
    }

    //método que realiza um teste em CarModel para validar seus atributos.
    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 memento;

public class CarModel {
    private float height;
    private float width;
    private float cargoVolume;
    private float wheelbase;

    //método que restaura o estado de CarModel a partir de um Snapshot
    public void restoreCarModel(CarModelSnapshot cp){
        this.height = cp.getHeight();
        this.width = cp.getWidth();
        this.cargoVolume = cp.getCargoVolume();
        this.wheelbase = cp.getWheelbase();
    }

    //Armazena os atributos atuais de CarModel em uma classe CarModelSnapshot,
    //armazenando seu estado.
    public CarModelSnapshot createSnapshot(){
        return new CarModelSnapshot(
                this.height,
                this.width,
                this.cargoVolume,
                this.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 memento;

public class CarModelSnapshot {
    private final float height;
    private final float width;
    private final float cargoVolume;
    private final float wheelbase;

    //Ao instanciar o Snapshot, este recebe os atributos da classe CarModel
    //que o criou
    CarModelSnapshot(float height, float width, float cargoVolume, float wheelbase){
        this.height = height;
        this.width = width;
        this.cargoVolume = cargoVolume;
        this.wheelbase = wheelbase;
    }

    float getHeight() {
        return height;
    }

    float getWidth() {
        return width;
    }

    float getCargoVolume() {
        return cargoVolume;
    }

    float getWheelbase() {
        return wheelbase;
    }
}
package memento;

public class CarModelChecker {

    private CarModel carModel;
    private CarModelSnapshot carModelSnapshot;

    public CarModelChecker(CarModel carModel){
        this.carModel = carModel;
    }

    public CarModelSnapshot getCarModelSnapshot() {
        return carModelSnapshot;
    }

    public void setCarModelSnapshot(CarModelSnapshot carModelSnapshot) {
        this.carModelSnapshot = carModelSnapshot;
    }

    public CarModel getCarModel() {
        return carModel;
    }

    //método chamado quando os atributos de CarModel precisam ser modificados.
    public void doAdjustments(float heightAdd, float widthAdd,
                              float cargoVolumeAdd, float wheelbaseAdd){
        //A partir de CarModel, um snapshot com seu estado é criado
        //e armazenado em CarModelChecker.
        this.carModelSnapshot = carModel.createSnapshot();

        carModel.setHeight(carModel.getHeight() + heightAdd);
        carModel.setWidth(carModel.getWidth() + widthAdd);
        carModel.setCargoVolume(carModel.getCargoVolume() + cargoVolumeAdd);
        carModel.setWheelbase(carModel.getWheelbase() + wheelbaseAdd);
    }

    //Restaura o estado de CarModel para o do snapshot salvo
    public void undoAdjustments(){
        this.carModel.restoreCarModel(this.carModelSnapshot);
    }

}
Clique aqui para fazer o download do código completo de implementação deste Design Pattern.

Padrões Relacionados

Este Padrão pode ser usado para resolver os seguintes problemas: