【备忘录模式】设计模式系列:掌握状态回溯的艺术(设计详解)

文章目录

  • 备忘录设计模式详解
    • 引言
    • 1. 设计模式概述
    • 2. 备忘录模式的基本概念
      • 2.1 备忘录模式的定义
      • 2.2 备忘录模式的关键角色
    • 3. 备忘录模式的实现原理
      • 3.1 备忘录模式的工作流程
      • 3.2 模式的优缺点分析
      • 3.3 与其他模式的对比
    • 4. 实际案例分析
      • 4.1 游戏状态保存与恢复
      • 4.2 文档编辑器撤销功能
      • 4.3 图形用户界面的状态管理
    • 5. 设计考虑
      • 5.1 封装性的重要性
      • 5.2 内部状态与外部状态的区别
      • 5.3 对象的生命周期管理
    • 6. Java实现备忘录模式
      • 6.1 Java代码示例
      • 6.2 关键类和接口的定义
      • 6.3 测试和验证
    • 7. 性能考量
    • 8. 备忘录模式的变体
      • 8.1 有限历史记录
      • 8.2 自动保存与定时备份
      • 8.3 多级撤销
    • 9. 模式组合
      • 9.1 与命令模式结合
      • 9.2 与迭代器模式结合
      • 9.3 与观察者模式结合
    • 10. 最佳实践
      • 10.1 如何避免内存泄漏
      • 10.2 使用工厂模式创建备忘录对象
      • 10.3 避免过度使用备忘录模式
    • 11. 常见问题及解答
    • 12 结论


备忘录设计模式详解


引言

软件开发中的挑战
在软件开发过程中,开发人员经常面临各种挑战,如需求变更、复杂性增加、性能瓶颈等。为了应对这些挑战,开发者需要采用一系列有效的技术和方法。其中一种重要的技术就是设计模式,它能够帮助解决特定的问题并提高代码的质量。

设计模式的重要性
设计模式是一套被广泛接受的解决方案,用于解决软件设计中经常出现的问题。它们不仅简化了代码的编写过程,还提高了系统的可维护性和可扩展性。通过使用设计模式,开发者可以学习到前辈们的经验教训,避免重复造轮子,从而加快开发速度。

备忘录模式的简介
备忘录模式是一种行为型设计模式,它允许在不破坏封装性的前提下捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将该对象恢复到原先保存的状态。这种模式特别适用于需要撤销或恢复操作的情况,比如实现类似“撤销”功能的应用程序。

1. 设计模式概述

1.1 设计模式定义
设计模式是在软件设计过程中反复出现的问题的解决方案。这些解决方案并不是具体的代码,而是一种通用的描述,用来说明如何在特定情况下解决问题。设计模式可以被看作是一种模板,帮助开发者更好地组织代码,并使代码更易于理解和复用。

1.2 常见的设计模式分类
设计模式通常分为三大类:

  1. 创建型模式:关注于对象的创建机制,使得我们可以根据具体情况创建对象。
  2. 结构型模式:关注于如何组合类或对象来获得更大的结构。
  3. 行为型模式:关注于算法和对象间职责的分配。

1.3 设计模式的应用场景
设计模式适用于多种不同的情况,包括但不限于:

  • 当你需要重用相同的解决方案来解决相同的问题时。
  • 当你需要简化复杂的代码结构,使其更加清晰易懂时。
  • 当你需要提高代码的可维护性和可扩展性时。
  • 当你需要提高代码的复用性,减少冗余时。

2. 备忘录模式的基本概念

2.1 备忘录模式的定义

备忘录模式允许在不破坏封装性的前提下捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将该对象恢复到原先保存的状态。它提供了一种回滚机制,可以撤销到之前的状态。

2.2 备忘录模式的关键角色

Originator (发起者)
发起者是一个包含有重要内部状态的对象。当需要保存当前状态时,发起者会创建一个备忘录对象,并在需要时使用该备忘录恢复状态。

Memento (备忘录)
备忘录对象负责存储发起者的内部状态。为了保持封装性,备忘录对象通常不允许发起者以外的对象访问其内部状态。

Caretaker (管理者)
管理者负责保存和管理备忘录对象。它并不对备忘录的内容感兴趣,只负责提供保存和获取备忘录的方法。
在这里插入图片描述


3. 备忘录模式的实现原理

3.1 备忘录模式的工作流程

  1. 创建备忘录:当发起者需要保存当前状态时,它创建一个备忘录对象,并将当前的内部状态复制到备忘录中。
  2. 保存备忘录:管理者接收备忘录对象,并将其保存在某个容器中,以备后续使用。
  3. 恢复状态:当需要恢复到先前的状态时,发起者从管理者处请求备忘录,并使用备忘录中的信息恢复自己的状态。

3.2 模式的优缺点分析

优点:

  • 封装性:备忘录模式保护了发起者的内部状态,因为备忘录对外隐藏了这些状态,只有发起者本身才能访问。
  • 灵活性:备忘录模式提供了一种灵活的方式来恢复状态,这对于需要撤销或恢复功能的应用程序非常有用。
  • 易于实现:备忘录模式的概念简单,实现起来也相对容易。

缺点:

  • 内存消耗:如果应用频繁地保存备忘录,则可能会导致较大的内存开销,尤其是在状态较大的情况下。
  • 性能问题:频繁地创建和销毁备忘录可能会带来一定的性能开销。

3.3 与其他模式的对比

  • 与命令模式的对比:命令模式主要用于封装一个请求作为对象,以便使用不同的请求、队列或者日志请求。备忘录模式则侧重于保存和恢复状态。
  • 与迭代器模式的对比:迭代器模式提供了一种访问集合元素的方法,而不会暴露底层表示。备忘录模式则专注于对象状态的保存和恢复。
  • 与观察者模式的对比:观察者模式定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都会得到通知并自动更新。备忘录模式则用于在对象之间传递状态信息。

4. 实际案例分析

4.1 游戏状态保存与恢复

在游戏开发中,备忘录模式可以用于保存玩家的游戏进度,例如角色的位置、生命值、装备等。当玩家想要返回到某个之前的存档点时,可以通过备忘录恢复游戏状态。

4.2 文档编辑器撤销功能

文档编辑器中的撤销功能通常使用备忘录模式实现。每当用户进行一次编辑操作,就会创建一个备忘录来保存当前文档的状态。用户可以撤销到之前的状态,只需从备忘录中恢复文档状态即可。

4.3 图形用户界面的状态管理

在图形用户界面(GUI)应用程序中,备忘录模式可以用来保存用户的界面配置,例如窗口大小、位置、打开的标签页等。这样用户可以在退出后再次打开应用程序时恢复到之前的状态。

5. 设计考虑

5.1 封装性的重要性

封装是面向对象编程的一个核心原则,它保证了对象的内部状态不会被外部直接访问。在备忘录模式中,发起者对象的内部状态被封装在备忘录对象中,这样就防止了外部对象直接修改这些状态,从而保护了数据的安全性和完整性。

5.2 内部状态与外部状态的区别

  • 内部状态:指的是那些只对发起者有意义的状态,不应该被外部对象访问或修改。这些状态通常是通过备忘录对象来保存的。
  • 外部状态:是指那些在备忘录外部并且在恢复时不需要重新设置的状态。这些状态通常由发起者自行管理,不在备忘录中保存。

5.3 对象的生命周期管理

在备忘录模式中,需要考虑备忘录对象的生命周期管理。一旦不再需要某个备忘录,就应该及时释放,以免占用不必要的内存资源。同时,也需要考虑如何有效地管理多个备忘录,特别是在需要保存多个版本的状态时。


6. Java实现备忘录模式

6.1 Java代码示例

以下是一个简单的Java实现示例,展示了备忘录模式的基本组件:

// Memento class
class Memento {
    private String state;

    public Memento(String state) {
        this.state = state;
    }

    public String getState() {
        return state;
    }
}

// Originator class
class Originator {
    private String state;

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

    public String getState() {
        return state;
    }

    public Memento createMemento() {
        return new Memento(state);
    }

    public void restoreMemento(Memento memento) {
        this.state = memento.getState();
    }
}

// Caretaker class
class Caretaker {
    private List<Memento> mementos = new ArrayList<>();

    public void addMemento(Memento memento) {
        mementos.add(memento);
    }

    public Memento getMemento(int index) {
        return mementos.get(index);
    }
}

public class Main {
    public static void main(String[] args) {
        Originator originator = new Originator();
        Caretaker caretaker = new Caretaker();

        originator.setState("State #1");
        caretaker.addMemento(originator.createMemento());

        originator.setState("State #2");
        caretaker.addMemento(originator.createMemento());

        originator.setState("State #3");
        caretaker.addMemento(originator.createMemento());

        System.out.println("Current State: " + originator.getState());
        originator.restoreMemento(caretaker.getMemento(1));
        System.out.println("Restored State: " + originator.getState());
    }
}

6.2 关键类和接口的定义

  • Memento:用于存储发起者的状态。
  • Originator:包含状态的对象,可以创建备忘录并恢复状态。
  • Caretaker:管理备忘录的类,保存和提供备忘录。

6.3 测试和验证

测试主要涉及创建备忘录、保存备忘录以及恢复状态的过程。可以通过单元测试来验证每个步骤是否正确执行,确保状态的保存和恢复功能正常工作。


7. 性能考量

7.1 内存使用优化

  • 减少备忘录的数量:只保留最近的几个备忘录,或者根据实际需求定期清除旧的备忘录。
  • 压缩备忘录:对于大型状态,可以考虑使用序列化技术来减小备忘录的大小。
  • 按需加载:只在需要的时候加载备忘录,而不是一开始就全部加载到内存中。

7.2 多线程环境下的考虑

  • 同步访问:在多线程环境中,确保对备忘录的访问是线程安全的。可以使用锁机制来控制并发访问。
  • 线程局部存储:如果可能,使用线程局部存储来存储备忘录,以避免多线程间的竞争条件。

7.3 状态持久化策略

  • 文件系统:将备忘录序列化到磁盘文件中,以供之后使用。
  • 数据库存储:使用数据库来存储备忘录,特别是当需要长期保存状态时。
  • 缓存策略:利用缓存机制来提高备忘录的读取速度,减少磁盘I/O操作。

8. 备忘录模式的变体

8.1 有限历史记录

在某些情况下,我们可能希望限制备忘录的数量,以节省内存空间。这可以通过实现一个有限的历史记录列表来实现,当列表达到最大容量时,最旧的备忘录将被删除。

// LimitedHistoryCaretaker class
class LimitedHistoryCaretaker {
    private int maxHistorySize;
    private List<Memento> mementos = new ArrayList<>();

    public LimitedHistoryCaretaker(int maxHistorySize) {
        this.maxHistorySize = maxHistorySize;
    }

    public void addMemento(Memento memento) {
        if (mementos.size() >= maxHistorySize) {
            mementos.remove(0); // Remove the oldest memento
        }
        mementos.add(memento);
    }

    public Memento getMemento(int index) {
        return mementos.get(index);
    }
}

8.2 自动保存与定时备份

自动保存功能可以在特定的时间间隔内自动保存当前状态为备忘录。这种方式可以减少用户手动保存的操作,同时也能够在系统崩溃时帮助用户恢复未保存的工作。

// AutoSaveCaretaker class
class AutoSaveCaretaker extends Thread {
    private Originator originator;
    private Caretaker caretaker;
    private long interval; // Time interval in milliseconds

    public AutoSaveCaretaker(Originator originator, Caretaker caretaker, long interval) {
        this.originator = originator;
        this.caretaker = caretaker;
        this.interval = interval;
    }

    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(interval);
                caretaker.addMemento(originator.createMemento());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

8.3 多级撤销

多级撤销允许用户撤销多个操作。这可以通过保存多个备忘录并在撤销时逐个恢复它们来实现。

// MultiLevelUndoCaretaker class
class MultiLevelUndoCaretaker {
    private List<Memento> mementos = new ArrayList<>();
    private int current = -1;

    public void addMemento(Memento memento) {
        mementos.add(++current, memento);
        mementos.subList(current + 1, mementos.size()).clear(); // Clear future states
    }

    public Memento undo() {
        if (current > 0) {
            return mementos.get(--current);
        }
        return null;
    }

    public Memento redo() {
        if (current < mementos.size() - 1) {
            return mementos.get(++current);
        }
        return null;
    }
}

9. 模式组合

9.1 与命令模式结合

备忘录模式可以与命令模式结合使用,以支持撤销和重做功能。在这种组合中,每个命令都可以保存一个备忘录,在撤销时恢复到命令执行前的状态。

// Command interface
interface Command {
    void execute();
    Memento createMemento();
    void restoreMemento(Memento memento);
}

// ConcreteCommand class
class ConcreteCommand implements Command {
    private Originator originator;
    private Memento previousState;

    public ConcreteCommand(Originator originator) {
        this.originator = originator;
    }

    @Override
    public void execute() {
        previousState = originator.createMemento();
        originator.setState("New State");
    }

    @Override
    public Memento createMemento() {
        return previousState;
    }

    @Override
    public void restoreMemento(Memento memento) {
        originator.restoreMemento(memento);
    }
}

9.2 与迭代器模式结合

迭代器模式可以用来遍历备忘录列表,这样可以更容易地实现撤销和重做功能。

// Iterator interface
interface Iterator {
    boolean hasNext();
    Memento next();
}

// ListIterator class
class ListIterator implements Iterator {
    private List<Memento> mementos;
    private int position;

    public ListIterator(List<Memento> mementos) {
        this.mementos = mementos;
        this.position = 0;
    }

    @Override
    public boolean hasNext() {
        return position < mementos.size();
    }

    @Override
    public Memento next() {
        if (hasNext()) {
            return mementos.get(position++);
        }
        return null;
    }
}

9.3 与观察者模式结合

观察者模式可以用来通知其他对象备忘录的变化,例如在自动保存时通知用户状态已保存。

// Observer interface
interface Observer {
    void update(Memento memento);
}

// Observable interface
interface Observable {
    void addObserver(Observer observer);
    void removeObserver(Observer observer);
    void notifyObservers(Memento memento);
}

// ObservableCaretaker class
class ObservableCaretaker implements Observable {
    private List<Observer> observers = new ArrayList<>();

    @Override
    public void addObserver(Observer observer) {
        observers.add(observer);
    }

    @Override
    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }

    @Override
    public void notifyObservers(Memento memento) {
        for (Observer observer : observers) {
            observer.update(memento);
        }
    }

    public void addMemento(Memento memento) {
        notifyObservers(memento);
    }
}

10. 最佳实践

10.1 如何避免内存泄漏

为了避免内存泄漏,需要注意以下几点:

  • 及时清理:当不再需要备忘录时,应及时将其从内存中移除。
  • 引用计数:使用引用计数来管理备忘录对象的生命周期。
  • 弱引用:使用弱引用或软引用来存储备忘录对象,这样当内存紧张时,垃圾回收器可以自动回收这些对象。

10.2 使用工厂模式创建备忘录对象

使用工厂模式可以统一备忘录的创建过程,有助于管理备忘录的不同类型或状态。

// MementoFactory class
class MementoFactory {
    public static Memento createMemento(String state) {
        return new Memento(state);
    }
}

10.3 避免过度使用备忘录模式

虽然备忘录模式非常有用,但也应注意不要过度使用,否则可能会导致不必要的内存消耗。在决定使用备忘录模式之前,应评估以下因素:

  • 需求分析:确定确实需要撤销/恢复功能。
  • 性能考量:评估备忘录模式对性能的影响。
  • 替代方案:考虑是否有其他模式或技术可以更高效地解决问题。

11. 常见问题及解答

如何处理大量备忘录对象
当需要处理大量的备忘录对象时,可以采取以下措施:

  • 限制备忘录数量:仅保存最近的几个备忘录,以减少内存占用。
  • 使用缓存策略:使用缓存机制来临时存储备忘录,只在真正需要时才将其加载到内存中。
  • 按需加载:只在需要时加载备忘录,而不是一开始就全部加载到内存中。

是否应该使用序列化来保存备忘录
使用序列化来保存备忘录可以有效减少内存占用,特别是在备忘录包含大量数据时。序列化可以将备忘录转换为磁盘上的文件,这样可以显著降低内存使用量。但是,需要注意的是序列化和反序列化的性能开销,特别是在频繁进行这些操作时。

如何在分布式系统中使用备忘录模式
在分布式系统中使用备忘录模式时,需要考虑以下几个方面:

  • 一致性:确保备忘录在所有节点上都是一致的,这可能需要使用分布式事务或其他一致性协议。
  • 状态同步:使用消息队列或发布/订阅模型来同步备忘录状态。
  • 容错机制:设计容错机制以处理节点故障,例如使用副本集来存储备忘录。

12 结论

备忘录模式在软件工程中的价值
备忘录模式提供了一种优雅的方式来处理对象状态的保存和恢复问题,尤其适用于需要撤销或恢复操作的应用程序。它不仅可以提高代码的可维护性和可扩展性,还可以简化实现复杂功能(如撤销/重做功能)的过程。

未来的研究方向
未来的研究可以集中在以下几个方面:

  • 性能优化:研究更高效的备忘录管理和存储策略,以减少内存使用和提高性能。
  • 分布式环境下的应用:探索备忘录模式在分布式系统中的应用场景和技术挑战。
  • 模式组合:探索备忘录模式与其他设计模式的更多组合方式,以解决更复杂的问题。

本文详细介绍了23种设计模式的基础知识,帮助读者快速掌握设计模式的核心概念,并找到适合实际应用的具体模式:
【设计模式入门】设计模式全解析:23种经典模式介绍与评级指南(设计师必备)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Eureka原理与实践:构建高效的微服务架构

Eureka原理与实践&#xff1a;构建高效的微服务架构 Eureka的核心原理Eureka Server&#xff1a;服务注册中心Eureka Client&#xff1a;服务提供者与服务消费者 Eureka的实践应用集成Eureka到Spring Cloud项目中创建Eureka Server创建Eureka Client&#xff08;服务提供者&…

VScode 连接远程服务器

1、 2、 3、免密登录 1、本地生成密钥 ssh-keygen2、生成的密钥默认在 C:\Users\***\.ssh\ 中3、将私钥 C:\Users\***\.ssh\id_rsa 添加到上面的配置文件中的 IdentityFile 项内4、将公钥 C:\Users\***\.ssh\id_rsa\id_rsa.pub 拷贝到远程 ~/.ssh/authorized_keys 中 4、远程…

Python | Leetcode Python题解之第354题俄罗斯套娃信封问题

题目&#xff1a; 题解&#xff1a; class Solution:def maxEnvelopes(self, envelopes: List[List[int]]) -> int:if not envelopes:return 0n len(envelopes)envelopes.sort(keylambda x: (x[0], -x[1]))f [1] * nfor i in range(n):for j in range(i):if envelopes[j]…

分享小诗梦404炫酷单页面html5源码

源码介绍 分享小诗梦404炫酷单页面html5源码&#xff0c;小诗梦的一个很炫酷页面&#xff0c;感觉应该符合一些人的感觉&#xff01;可以用来做404页面。 源码下载 分享小诗梦404炫酷单页面html5源码

uniapp-部分文件中文乱码

一、问题 在开发时遇到&#xff0c;部分页面的中文显示乱码&#xff0c;如图 搜索了一下解决方法&#xff0c;这里记录一下 二、问题原因&#xff1a; 页面的编码格式不是 utf-8 造成的 三、解决方法 打开出现乱码页面选择编译器左上角的文件 > 以指定编码重新打开 选择U…

[C++] C++11详解 (一)

标题&#xff1a;[C] C11详解 (一) 水墨不写bug 目录 前言 一、列表初始化 二、STL的初始化列表&#xff08;initializer_list —— Cplusplus.com&#xff09; 三、声明方式&#xff08;auto、decltype、nullptr&#xff09; 1.auto ​编辑 2.decltype 正文开始&#x…

腾讯无界微前端框架介绍

一、无界微前端框架概述 无界微前端框架是由腾讯团队推出的&#xff0c;旨在解决现有微前端方案中存在的问题&#xff0c;如适配成本高、样式隔离困难、运行性能不佳、页面白屏、子应用通信复杂、子应用保活机制缺乏等。 技术实现 无界微前端的核心技术是基于Web Component…

数据导入导出(EasyExcel)框架入门指南

写在前面 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站 文章目录 EasyExcel 框架概述依赖APIExcel 实体类注解写 Excel概念介绍写 Excel 通用参数WriteWorkbookWriteSheetWriteTable 代码…

GD32双路CAN踩坑记录

GD32双路CAN踩坑记录 目录 GD32双路CAN踩坑记录1 问题描述2 原因分析3 解决办法4 CAN配置参考代码 1 问题描述 GD32的CAN1无法进入接收中断&#xff0c;收不到数据。 注&#xff1a;MCU使用的是GD32E50x&#xff0c;其他型号不确定是否一样&#xff0c;本文只以GD32E50x举例说…

Vue项目-三级联动的路由跳转与传参

三级联动组件的路由的跳转与传参 三级联动&#xff0c;用户可以点击的&#xff1a;一级分类、二级分类和三级分类 以商城项目为例&#xff0c;Home模块跳转到Search模块&#xff0c;以及会把用户选中的产品&#xff08;产品名字、产品ID&#xff09;在路由跳转的时候&#xff…

Q*算法深度猜猜:从Q-learning优化到智能决策

Q*算法深度猜猜&#xff1a;从Q-learning优化到智能决策 引言 在强化学习&#xff08;Reinforcement Learning&#xff09;中&#xff0c;Q-learning算法作为一种无模型的学习方法&#xff0c;被广泛应用于解决各种决策优化问题。然而&#xff0c;尽管Q-learning在许多场景下…

基于ssm+vue+uniapp的医院挂号预约系统小程序

开发语言&#xff1a;Java框架&#xff1a;ssmuniappJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;M…

Matlab处理H5文件

1.读取h5文件 filenamexxx.h5; h5disp(filename) 2.h5文件保存为mat文件 读取 HDF5 文件中的数据 % 指定 HDF5 文件的路径 filename xxx.h5;% 读取 HDF5 文件中的各个数据集 A241_P h5read(filename, /A241_P); A241_W h5read(filename, /A241_W); A242_P h5read(filen…

Ps:首选项 - 性能

Ps菜单&#xff1a;编辑/首选项 Edit/Preferences 快捷键&#xff1a;Ctrl K Photoshop 首选项中的“性能” Performance选项卡允许用户通过调整内存使用、GPU 设置、高速缓存设置以及多线程处理等选项&#xff0c;来优化 Photoshop 的性能。这对于处理大文件、复杂图像或需要…

【NOI-题解】1137 - 纯粹素数1258 - 求一个三位数1140 - 亲密数对1149 - 回文数个数

文章目录 一、前言二、问题问题&#xff1a;1137 - 纯粹素数问题&#xff1a;1258 - 求一个三位数问题&#xff1a;1140 - 亲密数对问题&#xff1a;1149 - 回文数个数 三、感谢 一、前言 欢迎关注本专栏《C从零基础到信奥赛入门级&#xff08;CSP-J&#xff09;》 本章节主要…

二进制安装php

下载php二进制包&#xff1a; 官网地址&#xff1a;https://www.php.net/releases/ PHP: Releaseshttps://www.php.net/releases/在里边可以选择自己要下载的包进行下载&#xff1b; 下载完成后进行解压&#xff1a; tar xvzf php-7.3.12.tar.gz 解压后 进入目录进行预编…

学习yolo+Java+opencv简单案例(三)

主要内容&#xff1a;车牌检测识别&#xff08;什么颜色的车牌&#xff0c;车牌号&#xff09; 模型作用&#xff1a;车牌检测&#xff0c;车牌识别 文章的最后附上我的源码地址。 学习还可以参考我前两篇博客&#xff1a; 学习yoloJavaopencv简单案例&#xff08;一&#xff0…

Cobalt Strike 4.8 用户指南-第二节-用户界面

2.1、概述 Cobalt Strike用户界面分为两部分。界面顶部显示会话或目标的可视化。界面底部显示与你交互的每个 Cobalt Strike 功能或会话的选项卡。可以单击这两个部分之间的区域并根据自己的喜好调整它们的大小。 # 2.2、工具栏 顶部的工具栏提供对常见 Cobalt Strike功能的快…

C/C++实现蓝屏2.0

&#x1f680;欢迎互三&#x1f449;&#xff1a;程序猿方梓燚 &#x1f48e;&#x1f48e; &#x1f680;关注博主&#xff0c;后期持续更新系列文章 &#x1f680;如果有错误感谢请大家批评指出&#xff0c;及时修改 &#x1f680;感谢大家点赞&#x1f44d;收藏⭐评论✍ 前…

LeetCode合并两个有序链表

题目描述&#xff1a; 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1&#xff1a; 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4] 示例 2&#xff1a; 输入&#xff1a;l1 [], l2…