AI Chat 设计模式:11. 状态模式

本文是该系列的第十一篇,采用问答式的方式展开,问题由我提出,答案由 Chat AI 作出,灰色背景的文字则主要是我的一些思考和补充。

问题列表

  • Q.1 你知道状态模式吗
  • A.1
  • Q.2 它与有限状态机有什么联系吗?
  • A.2
  • Q.3 知道了,我是第一次学习状态模式,你能由浅入深给我讲解一下吗
  • A.3
  • Q.4 状态模式中,状态的切换应该放到具体状态类里还是上下文类里呢
  • A.4
  • Q.5 上面的例子不错,就是给我换成c++语言吧
  • A.5
  • Q.6 有一个问题,这些状态每次都是 new 出来的,但我却没看见它们在哪里销毁
  • A.6
  • Q.7 如果不使用智能指针呢
  • A.7
  • Q.8 明白,现在每次切换状态都要删除老的状态类,创建新的状态类,如此的反复创建、销毁,有办法改进吗
  • A.8
  • Q.9 不错,最后需要你对状态模式做一个全面的总结
  • A.9

Q.1 你知道状态模式吗

A.1

是的,我知道状态模式。状态模式是一种行为设计模式,它允许对象在不同的内部状态下改变其行为。在状态模式中,对象的行为取决于其状态,并且可以在运行时更改对象的状态。

状态模式包括三个主要角色:上下文(Context)、状态接口(State)和具体状态(Concrete State)。上下文是包含状态的对象,并将请求委派给当前状态处理。状态接口定义了所有具体状态必须实现的方法。具体状态是实现状态接口的具体类,每个具体状态负责处理特定的请求。

使用状态模式可以实现将复杂的条件语句转换为清晰的状态类,提高代码的可维护性和可扩展性。状态模式常用于需要根据对象内部状态改变行为的情况,例如状态机、游戏状态管理等。

Q.2 它与有限状态机有什么联系吗?

A.2

状态模式和有限状态机(FSM,Finite State Machine)之间存在一定联系,但它们是不同的概念。

有限状态机是一种数学模型,用于描述对象、系统或者程序的状态以及状态之间的转换。它由一组状态事件转移条件组成,当满足特定的条件时,会发生状态之间的转移。有限状态机可以是确定的(确定性有限状态机)或者非确定的(非确定性有限状态机),并可以用于解决不同的问题,如自动控制、编译器设计等。

而状态模式是一种软件设计模式,它用于将对象的行为与其内部状态相关联。对象在不同的状态下可以具有不同的行为,而状态模式允许对象在运行时根据状态的变化而改变其行为。与有限状态机不同,状态模式更关注对象的内部状态和行为之间的关系。

尽管状态模式和有限状态机都涉及到状态和状态之间的转换,但它们的关注点和应用领域有所不同。状态模式更加注重对象的内部行为和状态之间的关系,而有限状态机更关注的是问题的建模和状态转换过程。

一般认为状态模式是状态机的一种实现方式。

状态机由状态事件动作三部分组成。事件是触发状态转移的条件或原因,动作是进入该状态后产生的行为。当事件发生时,会根据当前状态作出动作,然后进入下一状态。
我们简单举一个倒霉大学生的例子,以毕业了为初始状态,“找到工作了”这一事件发生后,他会进入“兴奋”状态,此时他可能会产生“吃顿好的”这个动作(图里并未画出);有一天发生了“被老板骂”这一事件,他进入了“低落状态”,在该状态下会作出什么动作呢?忍了,或者硬刚,或者啥也不做(状态转换后,不一定有动作发生);过了一阵子,他被炒了,“失业”这一事件导致他进入了“沮丧”状态;再后来,“老婆跑了”,这一事件直接让他进入了“绝望”状态;然后又发生了一些事,这个大学生进入了“无”的状态。
如果想更多了解状态机,就需要去专门学习一下相关文章了,这里只是简单介绍。
在这里插入图片描述

Q.3 知道了,我是第一次学习状态模式,你能由浅入深给我讲解一下吗

A.3

当然可以!我可以帮你由浅入深地理解状态模式。

状态模式的核心思想是将对象的行为和状态分离,通过定义不同的状态类来管理对象的状态并实现相应的行为。这使得对象的行为可以根据状态的变化而灵活改变,同时也减少了大量的条件语句,提高了代码的可读性和可维护性。

让我们以一个简单的示例来说明状态模式的使用。

假设我们要设计一个电梯系统,其中电梯可以处于三种状态:关闭状态开启状态运行状态。不同状态下,电梯的行为是不同的。

首先,我们创建一个状态接口(State),该接口定义了所有具体状态类必须实现的方法,比如opencloserun等方法。

public interface State {
    void open();
    void close();
    void run();
}

open()、close()、run() 这三个方法,定义了电梯的三个行为,同时也是我们前面提到的状态转移条件(事件)。

然后,我们创建具体的状态类,分别是OpenStateCloseStateRunState,它们实现了状态接口,负责实现对应状态下的行为。

public class OpenState implements State {
    @Override
    public void open() {
        System.out.println("电梯已经是开启状态,无需再次开门");
    }

    @Override
    public void close() {
        System.out.println("电梯关闭门");
        // 切换到关闭状态
        context.setState(new CloseState());
    }

    @Override
    public void run() {
        System.out.println("电梯正在运行,无法切换到运行状态");
    }
}

public class CloseState implements State {
    @Override
    public void open() {
        System.out.println("电梯开门");
        // 切换到开启状态
        context.setState(new OpenState());
    }

    @Override
    public void close() {
        System.out.println("电梯已经是关闭状态,无需再次关闭");
    }

    @Override
    public void run() {
        System.out.println("电梯开始运行");
        // 切换到运行状态
        context.setState(new RunState());
    }
}

public class RunState implements State {
    @Override
    public void open() {
        System.out.println("电梯在运行中,无法开门");
    }

    @Override
    public void close() {
        System.out.println("电梯关门");
        // 切换到关闭状态
        context.setState(new CloseState());
    }

    @Override
    public void run() {
        System.out.println("电梯已经是运行状态,无需再次运行");
    }
}

接下来,我们创建一个上下文类(Context),它包含了一个当前状态的引用,并提供对外的操作方法。

public class Context {
    private State currentState;

    public Context() {
        // 默认初始状态是关闭状态
        currentState = new CloseState();
    }

    public void setState(State state) {
        currentState = state;
    }

    public void open() {
        currentState.open();
    }

    public void close() {
        currentState.close();
    }

    public void run() {
        currentState.run();
    }
}

发现没有,上下文类里也有open()、close()、run()这三个方法,它并不自己实现,而是直接调用状态对象的同名方法。简单说,我们将上下文本该自己实现的方法放到了状态类里来实现,当然,这些方法的行为得受到状态的影响,不同状态下行为不同。

最后,我们可以使用上述的状态模式来模拟电梯的行为。

public class Main {
    public static void main(String[] args) {
        Context context = new Context();

        context.open();  // 输出:"电梯开门"
        context.close();  // 输出:"电梯关闭门"
        context.run();  // 输出:"电梯开始运行"
        context.run();  // 输出:"电梯已经是运行状态,无需再次运行"
        context.open();  // 输出:"电梯在运行中,无法开门"
        context.close();  // 输出:"电梯关门"
        context.open();  // 输出:"电梯关闭门"
    }
}

通过上述示例,我们可以看到电梯根据不同的状态执行了相应的行为,而这些状态和行为的切换是相对灵活的。

这里,状态转换是在具体状态类中进行的,在严格的状态模式定义中,状态切换就是放在具体状态类中,这样的缺点是状态类之间有耦合,好处就是上下文里不用管状态的切换了。但也并不是只能如此。

Q.4 状态模式中,状态的切换应该放到具体状态类里还是上下文类里呢

A.4

在状态模式中,状态的切换可以放到具体状态类里,也可以放到上下文类里,具体取决于具体应用的需求和设计的考虑。

如果状态的变化是由外部条件驱动的,如用户的操作、外部事件等,那么通常将状态切换的逻辑放到上下文类中更为合适。上下文类可以根据外部条件来决定状态转换的时机,并调用具体状态类的方法来进行状态切换。

如果状态的变化是由内部条件驱动的,即状态类自身的逻辑或标记决定了状态的切换,那么将状态切换的逻辑放到具体状态类中更为合适。具体状态类知道自己的状态和状态转换的规则,可以自主地改变上下文对象的状态。

需要注意的是,无论状态的切换逻辑放在哪里,上下文对象和具体状态类之间都需要有适当的通信机制,以便完成状态切换后的行为执行。

综上所述,根据具体的应用需求和设计考虑,可以将状态的切换逻辑放到具体状态类或上下文类中。这也是状态模式的灵活性之一,可以根据不同的情况进行具体的设计选择。

希望这个解答对你有帮助。如果还有其他问题,请随时提问。

Q.5 上面的例子不错,就是给我换成c++语言吧

A.5

当然,没有问题!这是一个使用C++语言实现状态模式的电梯示例:

#include <iostream>

// 前向声明
class Context;

// 状态基类
class State {
public:
    virtual void open(Context* context) = 0;
    virtual void close(Context* context) = 0;
    virtual void run(Context* context) = 0;
};

// 具体状态类:开启状态
class OpenState : public State {
public:
    void open(Context* context) override {
        std::cout << "电梯已经是开启状态,无需再次开门" << std::endl;
    }

    void close(Context* context) override {
        std::cout << "电梯关闭门" << std::endl;
        // 切换到关闭状态
        context->setState(new CloseState());
    }

    void run(Context* context) override {
        std::cout << "电梯正在运行,无法切换到运行状态" << std::endl;
    }
};

// 具体状态类:关闭状态
class CloseState : public State {
public:
    void open(Context* context) override {
        std::cout << "电梯开门" << std::endl;
        // 切换到开启状态
        context->setState(new OpenState());
    }

    void close(Context* context) override {
        std::cout << "电梯已经是关闭状态,无需再次关闭" << std::endl;
    }

    void run(Context* context) override {
        std::cout << "电梯开始运行" << std::endl;
        // 切换到运行状态
        context->setState(new RunState());
    }
};

// 具体状态类:运行状态
class RunState : public State {
public:
    void open(Context* context) override {
        std::cout << "电梯在运行中,无法开门" << std::endl;
    }

    void close(Context* context) override {
        std::cout << "电梯关门" << std::endl;
        // 切换到关闭状态
        context->setState(new CloseState());
    }

    void run(Context* context) override {
        std::cout << "电梯已经是运行状态,无需再次运行" << std::endl;
    }
};

// 上下文类
class Context {
public:
    Context() {
        // 默认初始状态是关闭状态
        currentState = new CloseState();
    }

    void setState(State* state) {
        currentState = state;
    }

    void open() {
        currentState->open(this);
    }

    void close() {
        currentState->close(this);
    }

    void run() {
        currentState->run(this);
    }

private:
    State* currentState;
};

int main() {
    Context context;

    context.open();  // 输出:"电梯开门"
    context.close();  // 输出:"电梯关闭门"
    context.run();  // 输出:"电梯开始运行"
    context.run();  // 输出:"电梯已经是运行状态,无需再次运行"
    context.open();  // 输出:"电梯在运行中,无法开门"
    context.close();  // 输出:"电梯关门"
    context.open();  // 输出:"电梯关闭门"

    return 0;
}

在C++的实现中,我们使用了类和虚函数来表示状态和行为,实现了上下文类以及具体状态类。通过创建具体状态类的对象并切换状态,来模拟电梯的行为。

这里补充一下这个例子的类图。容易发现,上下文类 Context 拥有 state 类中定义的三个方法,它们分别调用了 state 类中的同名方法。状态的转换是在三个具体状态类中进行的,图中并未体现。

是否发现状态模式的类图和策略模式的类图是一样的呢?仔细看,确实是一样的。但要注意两者的应用场合是不一样的,在状态模式中,上下文类不需要了解各个状态对象,也不需要主动指定具体状态类,而在策略模式中,通常需要指定环境类使用的策略是哪一个。
可以说,脱落了应用场景,谈策略模式就意义不大了。

在这里插入图片描述

Q.6 有一个问题,这些状态每次都是 new 出来的,但我却没看见它们在哪里销毁

A.6

这是一个很好的问题!在这个示例中,确实没有明确的地方将状态对象销毁。在实际应用中,可能需要在适当的时机销毁状态对象,以便释放内存和管理资源。

在状态模式中,通常有以下几种方式来处理状态对象的销毁:

  1. 手动销毁:在合适的时机手动删除状态对象。例如,在上下文类的析构函数中释放状态对象的内存。
  2. 使用智能指针:可以使用C++智能指针来管理状态对象的生命周期。例如,可以使用std::unique_ptr<State>来持有状态对象,并且在需要切换状态时,更新智能指针指向的对象。

这里是一种使用智能指针的示例代码:

// 上下文类
class Context {
public:
    Context() {
        // 默认初始状态是关闭状态
        currentState = std::make_unique<CloseState>();
    }

    void setState(std::unique_ptr<State> state) {
        currentState = std::move(state);
    }

    // ...

private:
    std::unique_ptr<State> currentState;
};

使用智能指针可以自动管理状态对象的生命周期,无需手动调用delete来释放内存。

记住,在使用智能指针时,需要确保在涉及到多个类之间的所有权传递时,正确地使用std::move()来转移所有权。

希望这个解答能解决你的疑问!如果还有其他问题,请随时提问。

Q.7 如果不使用智能指针呢

A.7

如果不使用智能指针,你可以手动管理状态对象的生命周期。这意味着你需要在适当的时候手动创建和销毁状态对象。

下面是一个示例代码,展示了如何手动管理状态对象的生命周期:

// 上下文类
class Context {
public:
    Context() {
        // 默认初始状态是关闭状态
        currentState = new CloseState();
    }

    void setState(State* state) {
        delete currentState; // 销毁当前状态对象
        currentState = state;
    }

    ~Context() {
        delete currentState; // 在销毁上下文对象时,销毁当前状态对象
    }

    // ...

private:
    State* currentState;
};

在这个示例中,我们使用了new来创建状态对象,并在需要切换状态时使用delete来销毁先前的状态对象。在上下文类的析构函数中,我们也进行了一次对象销毁

但需要注意的是,手动管理生命周期需要确保在正确的时机销毁对象,并避免出现内存泄漏或错误的删除对象的情况。在实际项目中,请谨慎使用并仔细考虑对象所有权的管理。

将状态对象的释放在了setState() 方法中,每次先删除旧状态。

Q.8 明白,现在每次切换状态都要删除老的状态类,创建新的状态类,如此的反复创建、销毁,有办法改进吗

A.8

是的,如果在状态切换过程中频繁创建和销毁状态对象,可能会导致性能问题。为了改进这个情况,可以考虑使用对象池(Object Pool)模式。

对象池模式是一种创建和维护对象池的设计模式,它可以重复使用已经创建的对象,而不是频繁地创建和销毁对象。通过对象池,可以减少对象创建和销毁的开销,提高性能。

下面是一个简化的示例代码,展示了如何使用对象池模式来管理状态对象:

// 对象池类
class ObjectPool {
public:
    ObjectPool() {
        // 初始化对象池,创建一定数量的状态对象
        for (int i = 0; i < POOL_SIZE; ++i) {
            pool.push(new State());
        }
    }

    State* acquireState() {
        if (pool.empty()) {
            // 如果对象池为空,可以选择扩展对象池大小或者返回一个默认的状态对象
            return new State();
        }
        else {
            State* state = pool.top();
            pool.pop();
            return state;
        }
    }

    void releaseState(State* state) {
        pool.push(state);
    }

private:
    std::stack<State*> pool;
    static const int POOL_SIZE = 10; // 对象池的大小,可以根据需求调整
};

// 上下文类
class Context {
public:
    Context() {
        // 默认初始状态是关闭状态
        currentState = objectPool.acquireState();
    }

    void setState(State* state) {
        objectPool.releaseState(currentState); // 释放先前的状态对象
        currentState = state;
    }

    ~Context() {
        objectPool.releaseState(currentState); // 在销毁上下文对象时,释放当前状态对象
    }

    // ...

private:
    State* currentState;
    static ObjectPool objectPool; // 共享的对象池
};

ObjectPool Context::objectPool; // 静态成员变量需要在类外进行定义

在这个示例中,我们创建了一个对象池(ObjectPool),它在初始化时创建了一定数量的状态对象,并提供了acquireStatereleaseState方法来获取和释放状态对象。

在上下文类(Context)中,通过调用acquireState方法来获取状态对象,并在需要切换状态时使用releaseState方法来释放先前的状态对象。这样可以重复使用对象,减少了创建和销毁的开销。

使用对象池模式可以改善频繁创建和销毁状态对象的性能问题。然而,需要根据具体的应用场景和需求来决定对象池的大小,以及在对象池不足时的处理方式。

现在切换状态时,不是直接 new 一个状态对象了,而是从对象池获取。
销毁旧状态对象时,其实并没有真正销毁,只是放回来对象池。

这个例子是有一定问题的,比如 acquireState() 方法应该加个参数,从而能获取不同的状态对象,例子里显然不能做到这一点。

记住优化思路就行了,就是将所有状态对象的创建和销毁交给另一个类来管理,切换状态时不再直接创建和销毁状态对象。

Q.9 不错,最后需要你对状态模式做一个全面的总结

A.9

状态模式是一种行为型设计模式,它允许对象在其内部状态发生改变时改变其行为。该模式将对象的状态封装成独立的状态类,使得状态的变化可以彼此替换而不影响使用状态的对象。通过使用状态模式,可以实现状态转换和行为的解耦,使得代码更加可维护、可扩展和可复用。

主要角色:

  1. 上下文(Context):上下文是包含了状态对象的环境类。它定义了一个接口,用于客户端和具体状态类之间的交互。
  2. 抽象状态(State):抽象状态是一个接口或抽象类,定义了具体状态类需要实现的方法,并且为上下文对象提供一个统一的接口。
  3. 具体状态(Concrete State):具体状态是抽象状态的具体实现,它根据上下文的状态变化来改变自己的行为。

工作原理:

  1. 定义状态:首先,需要识别对象的状态以及每个状态的行为。将每种状态都抽象成一个独立的状态类。
  2. 建立上下文:创建一个上下文类,它包含一个指向当前状态的引用,并提供可以触发状态转换和执行对应行为的方法。
  3. 实现状态:为每个状态实现相应的行为方法,并在需要时,从一个状态切换到另一个状态。

优点:

  1. 将状态转换的逻辑从上下文对象中分离出来,使得代码更加可读、可维护和可扩展。
  2. 将状态行为封装在独立的状态类中,可实现状态的复用,并且可以方便地添加新的状态。
  3. 减少了使用条件语句进行状态判断的情况,使得代码更加简洁和易于理解。

适用场景:

  1. 当一个对象的行为取决于其内部状态,并且需要在运行时根据状态改变行为时,可以考虑使用状态模式。
  2. 当对象具有大量重复的条件语句,以便根据不同的状态执行不同的行为时,可以使用状态模式简化代码结构。

总结起来,状态模式通过将状态封装成独立的状态类,使得状态的变化可以彼此替换而不影响使用状态的对象。它提供了一种可扩展、可维护和可复用的解决方案,能够简化代码结构,并将状态行为与上下文对象的交互进行解耦。

希望这个总结对你有帮助。如果还有任何问题,请随时提问。

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

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

相关文章

Redis—持久化

这里写目录标题 AOF三种写回策略写回策略的优缺点AOF 重写机制AOF后台重写AOF优缺点使用命令 RDBRDB 持久化的工作原理执行快照时&#xff0c;数据能被修改吗RDB 持久化的优点RDB 持久化的缺点 混合持久化大key对持久化的影响 AOF 保存写操作命令到日志的持久化方式&#xff0…

[LeetCode - Python] 11.乘最多水的容器(Medium);26. 删除有序数组中的重复项(Easy)

1.题目&#xff1a; 11.乘最多水的容器&#xff08;Medium&#xff09; 1.代码 1.普通双指针对撞 贪心算法 class Solution:def maxArea(self, height: List[int]) -> int:# 对撞双指针# 对比记录最大面积&#xff0c;并移动短板&#xff0c;重新计算&#xff1b;left,…

Netty:ChannelHandler的两个生命周期监听事件方法:handlerAdded 和 handlerRemoved

说明 io.netty.channel.ChannelHandler有两个生命周期监听事件方法&#xff1a; handlerAdded(ChannelHandlerContext ctx)&#xff1a;当ChannelHandler被添加到实际的上下文、并且已经准备就绪等待处理事件的时候被调用。 handlerRemoved(ChannelHandlerContext ctx)&#…

【云原生•监控】基于Prometheus实现自定义指标弹性伸缩(HPA)

【云原生•监控】基于Prometheus实现自定义指标弹性伸缩(HPA) 什么是弹性伸缩 「Autoscaling即弹性伸缩&#xff0c;是Kubernetes中的一种非常核心的功能&#xff0c;它可以根据给定的指标&#xff08;例如 CPU 或内存&#xff09;自动缩放Pod副本&#xff0c;从而可以更好地管…

Prometheus技术文档-概念

Prometheus是一个开源的项目连接如下&#xff1a; Prometheus首页、文档和下载 - 服务监控系统 - OSCHINA - 中文开源技术交流社区 基本概念&#xff1a; Prometheus是一个开源的系统监控和告警系统&#xff0c;由Google的BorgMon监控系统发展而来。它主要用于监控和度量各种…

带你认识红黑树

红黑树 一、什么是红黑树&#xff1f;1.1 AVL树1.2 红黑树 二、红黑树的特点三、红黑树的insert、delete3.1 insert3.1.1 父节点为空3.1.2 父节点为Black节点3.1.3 父节点为Red节点3.1.3.1 叔叔节点为Red节点3.1.3.2 叔叔节点为Black节点 3.2 delete3.2.1 删除节点有两个子节点…

Scratch 之 TurboWarp 常用插件介绍-1

今天带来2篇 TurboWarp 常用插件介绍。 什么你还没有 TurboWarp &#xff1f;快去下载一个吧 TurboWarp&#xff08;简称TW&#xff09; 在线版 | 离线版下载 TurboWarp优点 编译速度快于原版 Scratch 至少10倍拥有自定义帧的功能&#xff08;比如60 FPS&#xff09;造型编…

【博客691】VictoriaMetrics如何支持Multi Retention

VictoriaMetrics如何支持Multi Retention 场景&#xff1a; 实现Multi Retention Setup within VictoriaMetrics Cluster&#xff0c;使得为不同的监控数据采用不同的保存时间 Multi Retention实现方式 方式&#xff1a; VictoriaMetrics 的社区版本通过 -retentionPeriod 命…

【工具插件类教学】电脑端移动端缩放大图自适应Simple Zoom

目录 简介 1.创建Canvas并设置 2.使用预制体Zoom 3.商店地址 简介 特点: •易于使用和高度可定制。 •支持鼠标(桌面)和触摸(移动)。 •指定最小和最大缩放的限制。 •缩放指针(鼠标/手指)或屏幕上预定义的自定义位置。 •变焦时使用夹紧/弹性变焦类型。 •定义缩…

基于PHP的轻量级博客typecho

本文完成于 5 月中旬&#xff0c;发布时未在最新版本上验证&#xff1b; 什么是 typecho &#xff1f; Typecho 是一款基于 PHP 的博客软件&#xff0c;旨在成为世界上最强大的博客引擎。Typecho 在 GNU 通用公共许可证 2.0 下发布。支持多种数据库&#xff0c;原生支持 Markdo…

征稿 | 第三届粤港澳大湾区人工智能与大数据论坛(AIBDF 2023)

第三届粤港澳大湾区人工智能与大数据论坛&#xff08;AIBDF 2023&#xff09; 2023 3rd Guangdong-Hong Kong-Macao Greater Bay Area Artificial Intelligence And Big Data Forum 本次高端论坛围绕建设国家数字经济创新发展试验区进行选题。全面贯彻落实党的二十大精神&…

【C++进阶之路】继承与多态的概念考察

文章目录 一、问答题二、概念题三、答案与解析问答题概念题 一、问答题 什么是菱形继承&#xff1f;菱形继承的问题是什么&#xff1f;什么是菱形虚拟继承&#xff1f;如何解决数据冗余和二义性的。继承和组合的区别&#xff1f;什么时候用继承&#xff1f;什么时候用组合&…

linux基于信号量实现多线程生产者消费者模型

基于信号量实现多线程生产者消费者模型。 编程思路&#xff1a; 1.食物的初始化编号为100&#xff1a; beginnum 100&#xff1b; 2.仓库有5个空碗&#xff0c;最多保存5个食物&#xff1a;queue[5]&#xff1b; 3.初始化空碗的数量为5&#xff0c;食物的数量为0&#xff1a…

FFmpeg中AVIOContext的使用

通过FFmpeg对视频进行编解码时&#xff0c;如果输入文件存在本机或通过USB摄像头、笔记本内置摄像头获取数据时&#xff0c;可通过avformat_open_input接口中的第二个参数直接指定即可。但如果待处理的视频数据存在于内存块中时&#xff0c;该如何指定&#xff0c;可通过FFmpeg…

《孙子兵法》快速概览,有哪些章节?趣讲《孙子兵法》【第2讲】

《孙子兵法》快速概览&#xff0c;有哪些章节&#xff1f;趣讲《孙子兵法》【第2讲】 《孙子兵法》十一家注是一个有名的版本&#xff0c;十一家注是曹操、杜牧等十一人注释&#xff0c;曹操是真正的军事家&#xff0c;是名副其实的大咖。总共三卷十三篇&#xff0c;比较难记住…

3个月快速入门LoRa物联网传感器开发

在这里插入图片描述 快速入门LoRa物联网传感器开发 LoRa作为一种LPWAN(低功耗广域网络)无线通信技术,非常适合物联网传感器和行业应用。要快速掌握LoRa开发,需要系统学习理论知识,并通过实际项目积累经验。 摘要: 先学习LoRa基础知识:原理、网络架构、协议等,大概需要2周时间…

Java加密算法的应用与实现(MD5、SHA、DES、3DES、AES、RSA、ECC)

文章目录 一、散列加密算法1、概述2、常见算法&#xff08;MD5、SHA&#xff09;3、应用4、Java实现 二、对称加密算法1、概述2、常见算法&#xff08;DES、3DES、AES&#xff09;3、应用4、Java实现AES 三、非对称加密算法1、概述2、常见算法&#xff08;RSA、ElGamal、Rabin、…

【linux】ssh 和adb connect区别

问&#xff1a;ssh 与ping的区别 答&#xff1a;SSH&#xff08;Secure Shell&#xff09;和Ping是两种完全不同的网络工具。 SSH是一种加密的网络协议&#xff0c;用于安全地远程管理或访问远程计算机。它提供了一种安全的通信方式&#xff0c;可以在不安全的网络上进行远程登…

淘宝API接口为开发者提供了与淘宝平台进行数据交互和操作的便捷途径

淘宝API接口是指淘宝开放平台提供的一套接口&#xff0c;用于与淘宝网进行数据交互和操作。通过使用淘宝API接口&#xff0c;第三方开发者可以实现商品搜索、店铺信息获取、订单管理、商家服务等功能&#xff0c;从而实现与淘宝平台的对接和数据共享。 淘宝API接口的使用可以帮…

备忘录模式(Memento)

备忘录模式是一种行为设计模式&#xff0c;在不破坏封装性的前提下&#xff0c;允许在不暴露对象实现细节的情况下保存和恢复对象之前的状态。 Memento is a behavior design pattern. Without compromising encapsulation, it can reserve and restore of the previous stat…