[Patrones de diseño - Notas de estudio] 23 Patrones de diseño - Modo Memento Memento (explicación del principio + introducción al escenario de la aplicación + introducción al caso + implementación del código Java)

Introducción del caso

El personaje del juego tiene poder de ataque y poder de defensa. Guarda su propio estado (poder de ataque y poder de defensa) antes de luchar contra el jefe. Cuando el poder de ataque y el poder de defensa disminuyen después de luchar contra el jefe, se pueden restaurar desde el objeto de nota al estado antes de la batalla.

diseño tradicional

Para cada rol, diseñe una clase para almacenar el estado del rol.

【analizar】

  • Un objeto corresponde a un objeto que guarda el estado del objeto, de esta forma, cuando hay muchos objetos en nuestro juego, no es propicio para la gestión y el costo es alto.
  • La forma tradicional es simplemente hacer una copia de seguridad, crear otro objeto y luego colocar los datos que deben respaldarse en este nuevo objeto, pero esto expone los detalles internos del objeto.
  • Método de optimización: usar el modo nota

introducir

introducción básica

  • El patrón memento captura el estado interno de un objeto y guarda este estado fuera del objeto sin destruir la encapsulación. De esta manera podrá restaurar posteriormente el objeto a su estado original guardado.
  • El patrón de memorando se puede entender de esta manera: los memorandos en la vida real se utilizan para registrar ciertas cosas por hacer o para registrar opiniones comunes a las que se ha llegado para evitar el olvido. A nivel de software, el objeto memo se utiliza principalmente para registrar un determinado estado de un objeto o ciertos datos. Al retroceder, los datos originales se pueden obtener del objeto memo para operaciones de recuperación.
  • El modo Memo es un modo de comportamiento.

Caracteres

Insertar descripción de la imagen aquí

  • Originator(生成者): El personaje Originador generará el personaje Memento al guardar su último estado. Al pasar un personaje Memento previamente guardado al personaje Originador, se restaurará al estado en el que se encontraba cuando se generó el personaje Memento.
  • Memento(备忘录): El rol Memento integrará la información interna del rol Originador. Aunque la información del rol de Originador se guarda en el rol de Memento, no expone esta información al exterior (al establecer los caracteres de permiso del método)
  • Caretaker(负责人): Se le notifica cuando Caretaker角色desea guardar el estado actual . Se genera una instancia y se regresa a ella después de recibir la notificación . Dado que se puede utilizar para restaurarlo a su estado original más adelante, siempre se guarda en el programa de muestra. Solo puede usar la interfaz estrecha entre las dos interfaces (API), lo que significa que no puede acceder a toda la información interna (por ejemplo, en el caso tres, solo se puede obtener la información monetaria y no se pueden realizar otras operaciones). Simplemente guarda el generado como un cuadro negro. Aunque existe una fuerte correlación entre y , existe una correlación débil entre y . Ocultar la propia información internaOriginator角色Originator角色Originator角色Memento角色Caretaker角色Memento实例OriginatorCaretaker角色Memento实例Caretaker角色Memento角色Memento角色Caretaker角色Memento角色创建Memento实例Originator角色Memento角色Originator角色Memento角色Caretaker角色Memento角色Memento角色Caretaker角色

[El significado de división Caretaker角色y suma Originator角色]

La responsabilidad del rol de Cuidador es decidir cuándo tomar una instantánea, cuándo deshacer y guardar el rol de Memento. Por otro lado, la responsabilidad del rol de Originador es generar roles de Memento y utilizar los roles de Memento recibidos para restaurar su propio estado. Con este tipo de responsabilidad compartida, no es necesario modificar el rol del Originador cuando necesitamos responder a los siguientes cambios de demanda:

  • Los cambios se pueden deshacer varias veces
  • No sólo se pueden deshacer los cambios, sino que también se puede guardar el estado actual en un archivo.

(La función de Cuidador solo puede operar la función Memento a través de una interfaz estrecha (API). Si la función de Cuidador puede operar libremente la función Memento, ¿qué pasará?]

  • Destruir la encapsulación del objeto: la función Memento es responsable de almacenar los datos de estado de la función Originador. Si la función Cuidador puede operar la función Memento a voluntad, entonces la encapsulación del objeto Memento y el objeto Originador se destruye.
  • Recuperación de estado insegura: si la función Caretaker puede operar la función Memento a voluntad, puede cambiar los datos de estado almacenados en el objeto Memento, lo que provocará errores en la recuperación del estado.
  • No se puede garantizar la integridad de los datos: si el rol de Cuidador puede operar el rol de Memento a voluntad, los datos del estado pueden guardarse en momentos incorrectos, lo que resulta en datos incompletos.

Implementación de casos

Caso 1 (Caso Básico)

Diagrama de clase

Insertar descripción de la imagen aquí

  • Originator: Objeto que necesita ser guardado
  • Memento: Objeto Memo, responsable de registrar el estado del objeto Originador.
  • Caretaker: Objeto guardián, responsable de guardar múltiples objetos memo. Generalmente, las colecciones se utilizan para la gestión para mejorar la eficiencia de la gestión.

lograr

【Autor】

package com.atguigu.memento.theory;

public class Originator {
    
    

    /**
     * 角色的状态信息
     */
    private String state;

    public String getState() {
    
    
        return state;
    }

    public void setState(String state) {
    
    
        this.state = state;
    }

    /**
     * 编写一个方法,可以保存一个状态对象 Memento
     * 因此编写一个方法,返回 Memento
     *
     * @return
     */
    public Memento saveStateMemento() {
    
    
        return new Memento(state);
    }

    /**
     * 通过备忘录对象获取原有状态信息,恢复状态
     *
     * @param memento
     */
    public void getStateFromMemento(Memento memento) {
    
    
        state = memento.getState();
    }
}

【Recuerdo】

package com.atguigu.memento.theory;

public class Memento {
    
    
    /**
     * 用来保存状态信息
     */
    private String state;

    /**
     * 构造器
     *
     * @param state
     */
    public Memento(String state) {
    
    
        super();
        this.state = state;
    }

    /**
     * 获取保存的状态信息
     * @return
     */
    public String getState() {
    
    
        return state;
    }

}

【Vigilante】

package com.atguigu.memento.theory;

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

/**
 * 统一管理备忘录对象
 */
public class Caretaker {
    
    

    /**
     * 在 List 集合中会有很多的备忘录对象
     * 如果想要保存多个Originator的多个状态,可以使用HashMap<Originator的ID,List<Memento>>
     */
    private List<Memento> mementoList = new ArrayList<Memento>();

    public void add(Memento memento) {
    
    
        mementoList.add(memento);
    }

    /**
     * 获取 Originator 的 第 index 个 备忘录对象(即所保存的状态)
     *
     * @param index
     * @return
     */
    public Memento get(int index) {
    
    
        return mementoList.get(index);
    }
}

【Categoria principal】

package com.atguigu.memento.theory;

public class Client {
    
    

   public static void main(String[] args) {
    
    

      Originator originator = new Originator();
      // 备忘录对象管理器
      Caretaker caretaker = new Caretaker();

      originator.setState(" 状态#1 攻击力 100 ");

      //保存了当前的状态
      caretaker.add(originator.saveStateMemento());

      originator.setState(" 状态#2 攻击力 80 ");

      caretaker.add(originator.saveStateMemento());

      originator.setState(" 状态#3 攻击力 50 ");
      caretaker.add(originator.saveStateMemento());

      System.out.println("当前的状态是 =" + originator.getState());

      //希望得到状态 1, 将 originator 恢复到状态1
      originator.getStateFromMemento(caretaker.get(0));
      System.out.println("恢复到状态1 , 当前的状态是 =" + originator.getState());

   }

}

【correr】

当前的状态是 = 状态#3 攻击力 50 
恢复到状态1 , 当前的状态是 = 状态#1 攻击力 100 

Process finished with exit code 0

Caso 2

Diagrama de clase

Insertar descripción de la imagen aquí

lograr

【Originador: GameRole】

package com.atguigu.memento.game;

public class GameRole {
    
    

    private int vit;
    private int def;

    /**
     * 创建Memento,即根据当前的状态得到Memento
     *
     * @return
     */
    public Memento createMemento() {
    
    
        return new Memento(vit, def);
    }

    /**
     * 从备忘录对象,恢复GameRole的状态
     *
     * @param memento
     */
    public void recoverGameRoleFromMemento(Memento memento) {
    
    
        this.vit = memento.getVit();
        this.def = memento.getDef();
    }

    /**
     * 显示当前游戏角色的状态
     */
    public void display() {
    
    
        System.out.println("游戏角色当前的攻击力:" + this.vit + " 防御力: " + this.def);
    }

    public int getVit() {
    
    
        return vit;
    }

    public void setVit(int vit) {
    
    
        this.vit = vit;
    }

    public int getDef() {
    
    
        return def;
    }

    public void setDef(int def) {
    
    
        this.def = def;
    }


}

【Recuerdo】

package com.atguigu.memento.game;

public class Memento {
    
    

    /**
     * 攻击力
     */
    private int vit;
    /**
     * 防御力
     */
    private int def;

    public Memento(int vit, int def) {
    
    
        this.vit = vit;
        this.def = def;
    }

    public int getVit() {
    
    
        return vit;
    }

    public void setVit(int vit) {
    
    
        this.vit = vit;
    }

    public int getDef() {
    
    
        return def;
    }

    public void setDef(int def) {
    
    
        this.def = def;
    }

}

【Vigilante】

package com.atguigu.memento.game;

/**
 * 守护者对象, 保存游戏角色的状态
 */
public class Caretaker {
    
    

    /**
     * 因为大战Boss之前只有一个状态,所以这里保存一次状态,不需要使用集合
     */
    private Memento memento;
    //对GameRole保存多次状态
    //private ArrayList<Memento> mementos;
    //对多个GameRole保存多个状态
    //private HashMap<String, ArrayList<Memento>> rolesMementos;

    public Memento getMemento() {
    
    
        return memento;
    }

    public void setMemento(Memento memento) {
    
    
        this.memento = memento;
    }

}

【Categoria principal】

package com.atguigu.memento.game;

public class Client {
    
    

   public static void main(String[] args) {
    
    
      //创建游戏角色
      GameRole gameRole = new GameRole();
      gameRole.setVit(100);
      gameRole.setDef(100);

      System.out.println("和boss大战前的状态");
      gameRole.display();

      //把当前状态保存caretaker
      Caretaker caretaker = new Caretaker();
      caretaker.setMemento(gameRole.createMemento());

      System.out.println("和boss大战~~~");
      gameRole.setDef(30);
      gameRole.setVit(30);

      gameRole.display();

      System.out.println("大战后,使用备忘录对象恢复到站前");

      gameRole.recoverGameRoleFromMemento(caretaker.getMemento());
      System.out.println("恢复后的状态");
      gameRole.display();
   }

}

【correr】

和boss大战前的状态
游戏角色当前的攻击力:100 防御力: 100
和boss大战~~~
游戏角色当前的攻击力:30 防御力: 30
大战后,使用备忘录对象恢复到站前
恢复后的状态
游戏角色当前的攻击力:100 防御力: 100

Process finished with exit code 0

Caso tres

Descripción del caso

  • El juego está automatizado.
  • El protagonista del juego decide el siguiente estado tirando dados.
  • Cuando el número del dado sea 1, el dinero del protagonista aumentará.
  • Cuando el número del dado sea 2, el dinero del protagonista disminuirá.
  • Cuando el número del dado sea 6, el protagonista obtendrá fruta.
  • El juego terminará cuando el protagonista no tenga dinero.

Diagrama de clase

Insertar descripción de la imagen aquí

lograr

【Creador: Jugador】

package com.atguigu.memento.Sample.game;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;

public class Gamer {
    
    
    /**
     * 所持金钱
     */
    private int money;
    /**
     * 获得的水果
     */
    private List fruits = new ArrayList();
    /**
     * 随机数生成器
     */
    private Random random = new Random();
    /**
     * 表示水果种类的数组
     */
    private static String[] fruitsName = {
    
    
            "苹果", "葡萄", "香蕉", "橘子",
    };

    /**
     * 构造函数
     *
     * @param money
     */
    public Gamer(int money) {
    
    
        this.money = money;
    }

    /**
     * 获取当前所持金钱
     *
     * @return
     */
    public int getMoney() {
    
    
        return money;
    }

    /**
     * 投掷骰子进行游戏
     */
    public void bet() {
    
    
        // 掷骰子,随机获取一个点数
        int dice = random.nextInt(6) + 1;

        if (dice == 1) {
    
    
            //  骰子结果为1…增加所持金钱
            money += 100;
            System.out.println("所持金钱增加了。");
        } else if (dice == 2) {
    
    
            // 骰子结果为2…所持金钱减半
            money /= 2;
            System.out.println("所持金钱减半了。");
        } else if (dice == 6) {
    
    
            // 骰子结果为6…获得水果
            String f = getFruit();
            System.out.println("获得了水果(" + f + ")。");
            fruits.add(f);
        } else {
    
    
            // 骰子结果为3、4、5则什么都不会发生
            System.out.println("什么都没有发生。");
        }
    }

    /**
     * 拍摄快照
     *
     * @return
     */
    public Memento createMemento() {
    
    
        Memento m = new Memento(money);
        Iterator it = fruits.iterator();
        while (it.hasNext()) {
    
    
            String f = (String) it.next();
            if (f.startsWith("好吃的")) {
    
    
                // 只保存好吃的水果
                m.addFruit(f);
            }
        }
        return m;
    }

    /**
     * 撤销到指定状态
     *
     * @param memento
     */
    public void restoreMemento(Memento memento) {
    
    
        this.money = memento.money;
        this.fruits = memento.getFruits();
    }

    /**
     * 用字符串输出主人公的状态
     *
     * @return
     */
    public String toString() {
    
    
        return "[money = " + money + ", fruits = " + fruits + "]";
    }

    /**
     * 随机获取一个水果
     *
     * @return
     */
    private String getFruit() {
    
    
        String prefix = "";
        if (random.nextBoolean()) {
    
    
            prefix = "好吃的";
        }
        return prefix + fruitsName[random.nextInt(fruitsName.length)];
    }
}

【Recuerdo】

package com.atguigu.memento.Sample.game;

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

public class Memento {
    
    
    /**
     * 游戏主人公所持金钱
     */
    int money;
    /**
     * 游戏主人公当前获得的水果
     */
    ArrayList fruits;
    /**
     * 获取当前所持金钱(narrow interface)
     * narrow interface:Memento角色为外部的 Caretaker 角色提供了“窄接口(API)”。可以通过窄接口(API)获取的Memento角色的内部信息非常有限,因此可以有效地防止信息泄露
     * @return
     */
    public int getMoney() {
    
    
        return money;
    }

    /**
     * 构造函数(wide interface),只有相同包的Gamer类才使用该构造方法,因为方法修饰类型是default
     *
     * @param money
     */
    Memento(int money) {
    
    
        this.money = money;
        this.fruits = new ArrayList();
    }

    /**
     * 添加水果(wide interface)
     *
     * @param fruit
     */
    void addFruit(String fruit) {
    
    
        fruits.add(fruit);
    }

    /**
     * 获取当前所持所有水果(wide interface)
     * wide interface:“宽接口(API)”是指所有用于获取恢复对象状态信息的方法的集合。由于宽接口(API)会暴露所有Memento角色的内部信息,因此能够使用宽接口(API)的只有Originator 角色
     * @return
     */
    List getFruits() {
    
    
        return (List) fruits.clone();
    }
}

【Vigilante】

package com.atguigu.memento.Sample;


import com.atguigu.memento.Sample.game.Gamer;
import com.atguigu.memento.Sample.game.Memento;

public class Main {
    
    
    public static void main(String[] args) {
    
    
        // 最初的所持金钱数为100
        Gamer gamer = new Gamer(100);
        // 保存最初的状态
        Memento memento = gamer.createMemento();
        for (int i = 0; i < 10; i++) {
    
    
            // 显示掷骰子的次数
            System.out.println("==== " + i);
            // 显示主人公现在的状态
            System.out.println("当前状态:" + gamer);

            // 进行游戏
            gamer.bet();

            System.out.println("所持金钱为" + gamer.getMoney() + "元。");

            // 决定如何处理Memento
            if (gamer.getMoney() > memento.getMoney()) {
    
    
                System.out.println("    (所持金钱增加了许多,因此保存游戏当前的状态)");
                memento = gamer.createMemento();
            } else if (gamer.getMoney() < memento.getMoney() / 2) {
    
    
                System.out.println("    (所持金钱减少了许多,因此将游戏恢复至以前的状态)");
                gamer.restoreMemento(memento);
            }

            // 等待一段时间
            try {
    
    
                Thread.sleep(1000);
            } catch (InterruptedException e) {
    
    
            }
            System.out.println("");
        }
    }
}

【correr】

==== 0
当前状态:[money = 100, fruits = []]
什么都没有发生。
所持金钱为100元。

==== 1
当前状态:[money = 100, fruits = []]
所持金钱增加了。
所持金钱为200元。
    (所持金钱增加了许多,因此保存游戏当前的状态)

==== 2
当前状态:[money = 200, fruits = []]
什么都没有发生。
所持金钱为200元。

==== 3
当前状态:[money = 200, fruits = []]
什么都没有发生。
所持金钱为200元。

==== 4
当前状态:[money = 200, fruits = []]
什么都没有发生。
所持金钱为200元。

==== 5
当前状态:[money = 200, fruits = []]
什么都没有发生。
所持金钱为200元。

==== 6
当前状态:[money = 200, fruits = []]
什么都没有发生。
所持金钱为200元。

==== 7
当前状态:[money = 200, fruits = []]
什么都没有发生。
所持金钱为200元。

==== 8
当前状态:[money = 200, fruits = []]
所持金钱减半了。
所持金钱为100元。

==== 9
当前状态:[money = 100, fruits = []]
所持金钱增加了。
所持金钱为200元。


Process finished with exit code 0

expandir

Utilice la función Serialización para guardar instancias de la clase Memento como archivos. Modifique el programa de muestra para implementar las siguientes funciones.

  • Cuando se inicia la aplicación, si se descubre que game.datel archivo no existe, el juego comenzará con la cantidad de dinero retenida como 100; si se descubre que game.dat ya existe, el juego comenzará con el estado guardado en el archivo.
  • Cuando la cantidad de dinero retenida aumente significativamente, guarde una instancia de la clase Memento en game.datun archivo
  1. Herede Memento de la interfaz Serializable para implementar operaciones de serialización. Aparte de eso, no se requieren otras modificaciones.
public class Memento implements Serializable {
    
    }
  1. Si desea ampliar la función de guardar instancias en archivos, solo necesita modificar Caretakerla clase
package com.atguigu.memento.A4;


import com.atguigu.memento.A4.game.Gamer;
import com.atguigu.memento.A4.game.Memento;

import java.io.*;

public class Main {
    
    
    /**
     * 数据保存的文件名
     */
    public static final String SAVE_FILE_NAME = "game.dat";

    public static void main(String[] args) {
    
    
        // 最初的所持金钱数为100
        Gamer gamer = new Gamer(100);
        // 从文件中读取起始状态
        Memento memento = loadMemento();
        if (memento != null) {
    
    
            System.out.println("读取上次保存存档开始游戏。");
            gamer.restoreMemento(memento);
        } else {
    
    
            System.out.println("新游戏。");
            memento = gamer.createMemento();
        }
        for (int i = 0; i < 100; i++) {
    
    
            // 显示次数
            System.out.println("==== " + i);
            // 显示当前主人公的状态
            System.out.println("当前状态:" + gamer);
            // 进行游戏
            gamer.bet();   

            System.out.println("所持金钱为" + gamer.getMoney() + "元。");

            // 决定如何处理Memento
            if (gamer.getMoney() > memento.getMoney()) {
    
    
                System.out.println("    (所持金钱增加了许多,因此保存游戏当前的状态)");
                memento = gamer.createMemento();
                // 将实例保存至文件中
                saveMemento(memento);
            } else if (gamer.getMoney() < memento.getMoney() / 2) {
    
    
                System.out.println("    (所持金钱减少了许多,因此将游戏恢复至以前的状态)");
                gamer.restoreMemento(memento);
            }

            // 等待一段时间
            try {
    
    
                Thread.sleep(1000);
            } catch (InterruptedException e) {
    
    
            }
            System.out.println("");
        }
    }

    /**
     * 保存实例到文件中
     * @param memento
     */
    public static void saveMemento(Memento memento) {
    
       
        try {
    
    
            ObjectOutput out = new ObjectOutputStream(new FileOutputStream(SAVE_FILE_NAME));
            out.writeObject(memento);
            out.close();
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }

    /**
     * 从文件中读取实例
     * @return
     */
    public static Memento loadMemento() {
    
                   
        Memento memento = null;
        try {
    
    
            ObjectInput in = new ObjectInputStream(new FileInputStream(SAVE_FILE_NAME));
            memento = (Memento)in.readObject();
            in.close();
        } catch (FileNotFoundException e) {
    
    
            System.out.println(e.toString());
        } catch (IOException e) {
    
    
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
    
    
            e.printStackTrace();
        }
        return memento;
    }
}

Resumir

【ventaja】

  • Proporciona a los usuarios un mecanismo para restaurar el estado, lo que les permite regresar más fácilmente a un estado histórico.
  • Se implementa la encapsulación de información para que los usuarios no tengan que preocuparse por los detalles de guardar el estado.

【defecto】

  • Si una clase tiene demasiadas variables miembro, inevitablemente ocupará recursos relativamente grandes y cada vez que guarde consumirá una cierta cantidad de memoria, esto debe tenerse en cuenta. Para ahorrar memoria, el modo memo se puede utilizar junto con el modo prototipo.

[Escenarios de aplicación aplicables]

  • Guardar archivos mientras juegas
  • en ventanasctrl+z
  • De vuelta en IE
  • Gestión de transacciones de bases de datos

Descripción del articulo

  • Este artículo son mis notas de estudio para estudiar Shang Silicon Valley. La mayor parte del contenido del artículo proviene de videos de Shang Silicon Valley (haga clic para conocer los cursos relacionados con Shang Silicon Valley ), y parte del contenido proviene de mi propio pensamiento. artículo para ayudar a otras personas a estudiar más cómodamente. Organice sus propias notas o aprenda conocimientos relevantes directamente a través de artículos. Si hay alguna infracción, comuníquese con nosotros para eliminarla. Finalmente, me gustaría expresar mi gratitud a Shang Silicon Valley por su cursos de alta calidad.
  • También leí simultáneamente el libro "Patrón de diseño gráfico" (Patrón de diseño gráfico/(japonés) Hiroshi Yuki; traducido por Yang Wenxuan - Beijing: People's Posts and Telecommunications Press, 2017.1), y luego integré los contenidos de los dos para hacer el conocimiento. puntos más completos.

Supongo que te gusta

Origin blog.csdn.net/laodanqiu/article/details/132189714
Recomendado
Clasificación