【设计模式——学习笔记】23种设计模式——备忘录模式Memento(原理讲解+应用场景介绍+案例介绍+Java代码实现)

news2024/9/29 9:24:52

案例引入

游戏角色有攻击力和防御力,在大战Boss前保存自身的状态(攻击力和防御力),当大战Boss后攻击力和防御力下降,可以从备忘录对象恢复到大战前的状态

传统设计方案

针对每一种角色,设计一个类来存储该角色的状态

【分析】

  • 一个对象,就对应一个保存对象状态的对象, 这样当我们游戏的对象很多时,不利于管理,开销也很大
  • 传统的方式是简单地做备份,new出另外一个对象出来,再把需要备份的数据放到这个新对象,但是这样暴露了对象内部的细节
  • 优化方式:使用备忘录模式

介绍

基本介绍

  • 备忘录模式在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态
  • 可以这样理解备忘录模式: 现实生活中的备忘录是用来记录某些要去做的事情或者是记录已经达成的共同意见的事情,以防忘记。而在软件层面,备忘录对象主要用来记录一个对象的某种状态,或者某些数据,当要做回退时,可以从备忘录对象里获取原来的数据进行恢复操作
  • 备忘录模式属于行为型模式

登场角色

在这里插入图片描述

  • Originator(生成者):Originator角色会在保存自己的最新状态时生成Memento角色。当把以前保存的Memento角色传递给Originator角色时,它会将自已恢复至生成该Memento角色时的状态
  • Memento(备忘录):Memento角色会将Originator 角色的内部信息整合在一起。在 Memento 角色中虽然保存了Originator 角色的信息,但它不会向外部公开这些信息(通过对方法的权限字符进行设置)
  • Caretaker(负责人):当Caretaker角色想要保存当前的Originator角色的状态时,会通知Originator角色Originator角色在接收到通知后会生成Memento角色的实例并将其返回给Caretaker角色。由于以后可能会用Memento实例来将Originator恢复至原来的状态,因此Caretaker角色会一直保存 Memento实例在示例程序中。Caretaker角色只能使用Memento角色的两种接口(API)中的窄接口,也就是说它无法访问Memento角色内部的所有信息(比如案例三中Caretaker角色只能获取Memento角色的金钱信息,无法进行创建Memento实例等操作)。它只是将Originator角色生成的Memento角色当作一个黑盒子保存起来。虽然Originator角色Memento角色之间是强关联关系,但Caretaker角色Memento角色之间是弱关联关系。Memento角色Caretaker角色隐藏了自身的内部信息

【划分Caretaker角色Originator角色的意义】

Caretaker角色的职责是决定何时拍摄快照,何时撤销以及保存Memento角色。另一方面,Originator 角色的职责则是生成Memento角色和使用接收到的Memento角色来恢复自己的状态。有了这样的职责分担,当我们需要应对以下的需求变更时,就不用修改Originator角色

  • 变更为可以多次撤销
  • 变更为不仅可以撤销,还可以将现在的状态保存在文件中

【Caretaker角色只能通过窄接口(API)来操作 Memento角色,如果Caretaker角色可以随意地操作Memento角色,会发生什么问题呢】

  • 破坏对象的封装性:Memento角色负责存储Originator角色的状态数据,如果Caretaker角色可以随意地操作Memento角色,那么就破坏了Memento对象和Originator对象的封装性
  • 不安全的状态恢复:如果Caretaker角色可以随意地操作Memento角色,可能会改变Memento对象中存储的状态数据,导致状态恢复产生错误
  • 无法保证数据的完整性:如果Caretaker角色可以随意地操作Memento角色,可能会在不正确的时间点保存状态数据,导致数据的不完整性

案例实现

案例一(基础案例)

类图

在这里插入图片描述

  • Originator:需要被保存状态的对象
  • Memento:备忘录对象,负责记录Originator对象的状态
  • Caretaker:守护者对象,负责保存多个备忘录对象,一般使用集合进行管理,提高管理效率

实现

【Originator】

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();
    }
}

【Memento】

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;
    }

}

【Caretaker】

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);
    }
}

【主类】

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());

   }

}

【运行】

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

Process finished with exit code 0

案例二

类图

在这里插入图片描述

实现

【Originator: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;
    }


}

【Memento】

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;
    }

}

【Caretaker】

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;
    }

}

【主类】

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();
   }

}

【运行】

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

Process finished with exit code 0

案例三

案例说明

  • 游戏是自动进行的
  • 游戏的主人公通过掷骰子来决定下一个状态
  • 当骰子点数为1的时候,主人公的金钱会增加
  • 当骰子点数为2的时候,主人公的金钱会减少
  • 当骰子点数为6的时候,主人公会得到水果
  • 主人公没有钱时游戏就会结束

类图

在这里插入图片描述

实现

【Originator:Gamer】

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)];
    }
}

【Memento】

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();
    }
}

【Caretaker】

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("");
        }
    }
}

【运行】

==== 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

拓展

使用序列化(Serialization)功能来将Memento类的实例保存为文件。请修改示例程序以实现下列功能。

  • 在应用程序启动时,如果发现不存在game.dat文件时,以所持金钱数目为100开始游戏;如果发现 game.dat 已经存在,则以文件中所保存的状态开始游戏
  • 当所持金钱大量增加后,将Memento类的实例保存到game.dat文件中
  1. 将Memento继承Serializable接口来实现序列化操作,除此之外,不需要进行其他修改
public class Memento implements Serializable {}
  1. 想要扩展实例保存到文件的功能,只需要修改Caretaker
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;
    }
}

总结

【优点】

  • 给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回退到某个历史的状态
  • 实现了信息的封装,使用户不需要关心状态的保存细节

【缺点】

  • 如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存,这个需要注意。为了节约内存,备忘录模式可以和原型模式配合使用

【适用应用场景】

  • 打游戏时的存档
  • Windows 里的ctrl+z
  • IE 中的后退
  • 数据库的事务管理

文章说明

  • 本文章为本人学习尚硅谷的学习笔记,文章中大部分内容来源于尚硅谷视频(点击学习尚硅谷相关课程),也有部分内容来自于自己的思考,发布文章是想帮助其他学习的人更方便地整理自己的笔记或者直接通过文章学习相关知识,如有侵权请联系删除,最后对尚硅谷的优质课程表示感谢。
  • 本人还同步阅读《图解设计模式》书籍(图解设计模式/(日)结城浩著;杨文轩译–北京:人民邮电出版社,2017.1),进而综合两者的内容,让知识点更加全面

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/853006.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

cpu util margin,cpu freq margin

【cpufreq governor】cpu util 和 cpu margin怎么计算的_悟空明镜的博客-CSDN博客 cpu util margin&#xff0c;cpu freq margin 根据policy_util schedtune_margin 作为算力选对应的cpu cluster或调频

EXCEL表格操作

1.带格式合并&#xff1a;D6&"欢迎光临"&E6 2.带格式复制粘贴&#xff1a;ctrlc 复制&#xff0c;选择对于单元格点击选择性粘贴&#xff1a;粘贴值和数字格式

docker-compose 安装kafka集群

点击关注《golang技术实验室》公众号****&#xff0c;将****获取更多干货 介绍 Kafka是一种高性能的分布式流处理平台&#xff0c;它的集群工作原理如下&#xff1a; 假设你是一个快递员&#xff0c;Kafka集群就是一个快递中转站。在这个中转站中&#xff0c;有很多个小窗口…

基于TF-IDF+TensorFlow+词云+LDA 新闻自动文摘推荐系统—深度学习算法应用(含ipynb源码)+训练数据集

目录 前言总体设计系统整体结构图系统流程图 运行环境Python 环境TensorFlow环境方法一方法二 模块实现1. 数据预处理1&#xff09;导入数据2&#xff09;数据清洗3&#xff09;统计词频 2. 词云构建3. 关键词提取4. 语音播报5. LDA主题模型6. 模型构建 系统测试工程源代码下载…

分布式 - 消息队列Kafka:Kafka生产者发送消息流程和3种方式

文章目录 1. Kafka 生产者2. kafaka 命令行操作3. Kafka 生产者发送消息流程4. Kafka 生产者发送消息的3种方式1. 发送即忘记2. 同步发送3. 异步发送 5. Kafka 消息对象 ProducerRecord 1. Kafka 生产者 Kafka 生产者是指使用 Apache Kafka 消息系统的应用程序&#xff0c;它们…

wsl(在windows中使用呢linux系统)适用于windows的linux子系统

步骤可参考微软官方文档https://learn.microsoft.com/zh-cn/windows/wsl/install-manual#step-4—download-the-linux-kernel-update-package 在这里主要列举一些需要注意的点 wsl2的要求 一定要检查下windows版本&#xff0c;版本不对的先升级版本不然无法使用wsl2 wsl支持…

P4381 [IOI2008] Island (求基环树直径)

也许更好的阅读体验 D e s c r i p t i o n \mathcal{Description} Description 给一个基环树森林&#xff0c;求每棵树的直径的和&#xff0c;基环树的直径定义为&#xff0c;从一个点出发只能走到没走过的点&#xff08;即一个环不能把所有边都选&#xff09;&#xff0c;所经…

史上最细,自动化测试-logging日志采集详细实战(二)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、测试场景 给登…

固态硬盘数据恢复方法有哪些?三种恢复方法助您解忧

近年来固态硬盘比较流行&#xff0c;因为工作的需要我也在使用固态硬盘&#xff0c;它真的给我带来了很多的方便。但是最近&#xff0c;我固态硬盘里的文件有些不知道怎么就丢失了&#xff0c;这给我带来了很大的困扰。有什么方法可以找回来吗&#xff1f; 固态硬盘&#xff08…

Netty客户端同步获取结果

上次服务间通信是异步的&#xff0c;现在想实现客户端同步拿到服务端响应结果。实现如下&#xff1a; 在NettyClientHandler类中增加一个结果缓存器 Map<Long,Protocol<ResponseMsg>> resultMap new ConcurrentHashMap<>();修改方法 Override protected vo…

【文献阅读笔记】深度异常检测模型

文章目录 导读相关关键词及其英文描述记录深度异常检测模型Supervised deep anomaly detection 有监督深度异常检测Semi-Supervised deep anomaly detection 半监督深度异常检测Hybrid deep anomaly detection 混合深度异常检测One-class neural network for anomaly detection…

VR全景的盈利模式你知道吗?VR全景能用在哪些领域?

引言&#xff1a; 随着科技的迅猛发展&#xff0c;虚拟现实技术已经逐渐走进我们的生活。这项令人惊叹的技术让我们能够穿越时间与空间的限制&#xff0c;重新定义人们与世界互动的方式。 一&#xff0e;什么是VR全景&#xff1f; VR全景&#xff0c;是一种通过虚拟现实技术&…

万应低代码受邀参加上海电信“大干一场 科创沙龙”活动

7月28日&#xff0c;由上海市宝山区大场镇政府指导、中国电信上海北区局主办的“大干一场 科创沙龙”系列第九期沙龙活动顺利举办。大场镇“数字化转型”领导小组办公室&#xff08;以下简称“数字办”&#xff09;邀请了来自镇域内外的数十家科创服务企业。万应低代码作为天翼…

D5渲染器有多强大?给你10个选择它的理由

使用此分步指南&#xff0c;可以轻松快速地使用 D5 Render 创建专业视觉效果。使用 D5 Render 创建的视觉效果能够快速有效地传达信息。例如&#xff0c;简单的图形或图表可用于比较不同的数据集或显示变量之间的关系。使用 D5 Render&#xff0c;您可以创建既美观又信息丰富的…

公检系统创新:利用校对软件优化法律文书流程

公检系统可以通过利用校对软件来优化法律文书的流程&#xff0c;从而提高效率和准确性。以下是在创新方面利用校对软件的一些方法&#xff1a; 1.自动校对和修正&#xff1a;校对软件可以与公检系统集成&#xff0c;自动检测文书中的拼写、语法和标点符号错误&#xff0c;并提供…

msvcp120.dll丢失的4种解决方法,教你修复msvcp120.dll文件

当你在使用计算机时&#xff0c;经常会遇到各种各样的错误&#xff0c;最近遇到的应该就是dll文件缺失的错误吧&#xff0c;今天主要来跟大家讲解一下msvcp120.dll这个文件&#xff0c;教大家msvcp120.dll丢失的4种解决方法&#xff0c;好了&#xff0c;废话不多说&#xff0c;…

北京多铁克FPGA笔试题目

1、使用D触发器来实现二分频 2、序列检测器&#xff0c;检测101&#xff0c;输出1&#xff0c;其余情况输出0 module Detect_101(input clk,input rst_n,input data, //输入的序列output reg flag_101 //检测到101序列的输出标志 );parameter S0 2d0;S1 2d1;S2 2d2;S4 …

从金蝶云星空到金蝶云星空通过接口配置打通数据

从金蝶云星空到金蝶云星空通过接口配置打通数据 对接系统金蝶云星空 金蝶K/3Cloud结合当今先进管理理论和数十万家国内客户最佳应用实践&#xff0c;面向事业部制、多地点、多工厂等运营协同与管控型企业及集团公司&#xff0c;提供一个通用的ERP服务平台。K/3Cloud支持的协同应…

【LeetCode】数据结构题解(12)[用栈实现队列]

用栈实现队列 &#x1f609; 1.题目来源&#x1f440;2.题目描述&#x1f914;3.解题思路&#x1f973;4.代码展示 所属专栏&#xff1a;玩转数据结构题型❤️ &#x1f680; >博主首页&#xff1a;初阳785❤️ &#x1f680; >代码托管&#xff1a;chuyang785❤️ &…

flask使用cookie (设置cookie与查看cookie内容)

1.flask包cookie的使用 设置cookie app.route(/set_cookie) def set_cookie():resp make_response(Setting cookie)resp.set_cookie(username, John)return resp查看cookie: app.route(/get_cookie) def get_cookie():username request.cookies.get(username)return Welco…