C++设计模式(8)——命令模式

2023-11-18

命令模式

亦称:动作、事务、Action、Transaction、Command

意图

命令模式是一种行为设计模式, 它可将请求转换为一个包含与请求相关的所有信息的独立对象。 该转换让你能根据不同的请求将方法参数化、 延迟请求执行或将其放入队列中, 且能实现可撤销操作。

问题

假如你正在开发一款新的文字编辑器, 当前的任务是创建一个包含多个按钮的工具栏, 并让每个按钮对应编辑器的不同操作。 你创建了一个非常简洁的 按钮类, 它不仅可用于生成工具栏上的按钮, 还可用于生成各种对话框的通用按钮。
在这里插入图片描述
应用中的所有按钮都可以继承相同的类

尽管所有按钮看上去都很相似, 但它们可以完成不同的操作 (打开、 保存、 打印和应用等)。 你会在哪里放置这些按钮的点击处理代码呢? 最简单的解决方案是在使用按钮的每个地方都创建大量的子类。 这些子类中包含按钮点击后必须执行的代码。
在这里插入图片描述
大量的按钮子类。 没关系的。

你很快就意识到这种方式有严重缺陷。 首先, 你创建了大量的子类, 当每次修改基类 按钮时, 你都有可能需要修改所有子类的代码。 简单来说, GUI 代码以一种拙劣的方式依赖于业务逻辑中的不稳定代码。
在这里插入图片描述
多个类实现同一功能。

还有一个部分最难办。 复制/粘贴文字等操作可能会在多个地方被调用。 例如用户可以点击工具栏上小小的 “复制” 按钮, 或者通过上下文菜单复制一些内容, 又或者直接使用键盘上的 Ctrl+C 。

我们的程序最初只有工具栏, 因此可以使用按钮子类来实现各种不同操作。 换句话来说, ​ 复制按钮Copy­Button子类包含复制文字的代码是可行的。 在实现了上下文菜单、 快捷方式和其他功能后, 你要么需要将操作代码复制进许多个类中, 要么需要让菜单依赖于按钮, 而后者是更糟糕的选择。

解决方案

优秀的软件设计通常会将关注点进行分离, 而这往往会导致软件的分层。 最常见的例子: 一层负责用户图像界面; 另一层负责业务逻辑。 GUI 层负责在屏幕上渲染美观的图形, 捕获所有输入并显示用户和程序工作的结果。 当需要完成一些重要内容时 (比如计算月球轨道或撰写年度报告), GUI 层则会将工作委派给业务逻辑底层。

这在代码中看上去就像这样: 一个 GUI 对象传递一些参数来调用一个业务逻辑对象。 这个过程通常被描述为一个对象发送请求给另一个对象。
在这里插入图片描述
GUI 层可以直接访问业务逻辑层。

命令模式建议 GUI 对象不直接提交这些请求。 你应该将请求的所有细节 (例如调用的对象、 方法名称和参数列表) 抽取出来组成命令类, 该类中仅包含一个用于触发请求的方法。

命令对象负责连接不同的 GUI 和业务逻辑对象。 此后, GUI 对象无需了解业务逻辑对象是否获得了请求, 也无需了解其对请求进行处理的方式。 GUI 对象触发命令即可, 命令对象会自行处理所有细节工作。

在这里插入图片描述
通过命令访问业务逻辑层。
下一步是让所有命令实现相同的接口。 该接口通常只有一个没有任何参数的执行方法, 让你能在不和具体命令类耦合的情况下使用同一请求发送者执行不同命令。 此外还有额外的好处, 现在你能在运行时切换连接至发送者的命令对象, 以此改变发送者的行为。

你可能会注意到遗漏的一块拼图——请求的参数。 GUI 对象可以给业务层对象提供一些参数。 但执行命令方法没有任何参数, 所以我们如何将请求的详情发送给接收者呢? 答案是: 使用数据对命令进行预先配置, 或者让其能够自行获取数据。
在这里插入图片描述
GUI 对象将命令委派给命令对象。

让我们回到文本编辑器。 应用命令模式后, 我们不再需要任何按钮子类来实现点击行为。 我们只需在 按钮Button基类中添加一个成员变量来存储对于命令对象的引用, 并在点击后执行该命令即可。

你需要为每个可能的操作实现一系列命令类, 并且根据按钮所需行为将命令和按钮连接起来。

其他菜单、 快捷方式或整个对话框等 GUI 元素都可以通过相同方式来实现。 当用户与 GUI 元素交互时, 与其连接的命令将会被执行。 现在你很可能已经猜到了, 与相同操作相关的元素将会被连接到相同的命令, 从而避免了重复代码。

最后, 命令成为了减少 GUI 和业务逻辑层之间耦合的中间层。 而这仅仅是命令模式所提供的一小部分好处!

真实世界类比

在这里插入图片描述
在餐厅里点餐。

在市中心逛了很久的街后, 你找到了一家不错的餐厅, 坐在了临窗的座位上。 一名友善的服务员走近你, 迅速记下你点的食物, 写在一张纸上。 服务员来到厨房, 把订单贴在墙上。 过了一段时间, 厨师拿到了订单, 他根据订单来准备食物。 厨师将做好的食物和订单一起放在托盘上。 服务员看到托盘后对订单进行检查, 确保所有食物都是你要的, 然后将食物放到了你的桌上。

那张纸就是一个命令, 它在厨师开始烹饪前一直位于队列中。 命令中包含与烹饪这些食物相关的所有信息。 厨师能够根据它马上开始烹饪, 而无需跑来直接和你确认订单详情。

命令模式结构

在这里插入图片描述
1、发送者 (Sender)——亦称 “触发者 (Invoker)”——类负责对请求进行初始化, 其中必须包含一个成员变量来存储对于命令对象的引用。 发送者触发命令, 而不向接收者直接发送请求。 注意, 发送者并不负责创建命令对象: 它通常会通过构造函数从客户端处获得预先生成的命令。

2、命令 (Command) 接口通常仅声明一个执行命令的方法。

3、具体命令 (Concrete Commands) 会实现各种类型的请求。 具体命令自身并不完成工作, 而是会将调用委派给一个业务逻辑对象。 但为了简化代码, 这些类可以进行合并。

接收对象执行方法所需的参数可以声明为具体命令的成员变量。 你可以将命令对象设为不可变, 仅允许通过构造函数对这些成员变量进行初始化。

4、接收者 (Receiver) 类包含部分业务逻辑。 几乎任何对象都可以作为接收者。 绝大部分命令只处理如何将请求传递到接收者的细节, 接收者自己会完成实际的工作。

5、客户端 (Client) 会创建并配置具体命令对象。 客户端必须将包括接收者实体在内的所有请求参数传递给命令的构造函数。 此后, 生成的命令就可以与一个或多个发送者相关联了。

伪代码

在本例中, 命令模式会记录已执行操作的历史记录, 以在需要时撤销操作。

在这里插入图片描述
文本编辑器中的可撤销操作。
有些命令会改变编辑器的状态 (例如剪切和粘贴), 它们可在执行相关操作前对编辑器的状态进行备份。 命令执行后会和当前点备份的编辑器状态一起被放入命令历史 (命令对象栈)。 此后, 如果用户需要进行回滚操作, 程序可从历史记录中取出最近的命令, 读取相应的编辑器状态备份, 然后进行恢复。

客户端代码 (GUI 元素和命令历史等) 没有和具体命令类相耦合, 因为它通过命令接口来使用命令。 这使得你能在无需修改已有代码的情况下在程序中增加新的命令。

// 命令基类会为所有具体命令定义通用接口。
abstract class Command is
    protected field app: Application
    protected field editor: Editor
    protected field backup: text

    constructor Command(app: Application, editor: Editor) is
        this.app = app
        this.editor = editor

    // 备份编辑器状态。
    method saveBackup() is
        backup = editor.text

    // 恢复编辑器状态。
    method undo() is
        editor.text = backup

    // 执行方法被声明为抽象以强制所有具体命令提供自己的实现。该方法必须根
    // 据命令是否更改编辑器的状态返回 true 或 false。
    abstract method execute()


// 这里是具体命令。
class CopyCommand extends Command is
    // 复制命令不会被保存到历史记录中,因为它没有改变编辑器的状态。
    method execute() is
        app.clipboard = editor.getSelection()
        return false

class CutCommand extends Command is
    // 剪切命令改变了编辑器的状态,因此它必须被保存到历史记录中。只要方法
    // 返回 true,它就会被保存。
    method execute() is
        saveBackup()
        app.clipboard = editor.getSelection()
        editor.deleteSelection()
        return true

class PasteCommand extends Command is
    method execute() is
        saveBackup()
        editor.replaceSelection(app.clipboard)
        return true

// 撤销操作也是一个命令。
class UndoCommand extends Command is
    method execute() is
        app.undo()
        return false


// 全局命令历史记录就是一个堆桟。
class CommandHistory is
    private field history: array of Command

    // 后进……
    method push(c: Command) is
        // 将命令压入历史记录数组的末尾。

    // ……先出
    method pop():Command is
        // 从历史记录中取出最近的命令。


// 编辑器类包含实际的文本编辑操作。它会担任接收者的角色:最后所有命令都会
// 将执行工作委派给编辑器的方法。
class Editor is
    field text: string

    method getSelection() is
        // 返回选中的文字。

    method deleteSelection() is
        // 删除选中的文字。

    method replaceSelection(text) is
        // 在当前位置插入剪贴板中的内容。

// 应用程序类会设置对象之间的关系。它会担任发送者的角色:当需要完成某些工
// 作时,它会创建并执行一个命令对象。
class Application is
    field clipboard: string
    field editors: array of Editors
    field activeEditor: Editor
    field history: CommandHistory

    // 将命令分派给 UI 对象的代码可能会是这样的。
    method createUI() is
        // ……
        copy = function() { executeCommand(
            new CopyCommand(this, activeEditor)) }
        copyButton.setCommand(copy)
        shortcuts.onKeyPress("Ctrl+C", copy)

        cut = function() { executeCommand(
            new CutCommand(this, activeEditor)) }
        cutButton.setCommand(cut)
        shortcuts.onKeyPress("Ctrl+X", cut)

        paste = function() { executeCommand(
            new PasteCommand(this, activeEditor)) }
        pasteButton.setCommand(paste)
        shortcuts.onKeyPress("Ctrl+V", paste)

        undo = function() { executeCommand(
            new UndoCommand(this, activeEditor)) }
        undoButton.setCommand(undo)
        shortcuts.onKeyPress("Ctrl+Z", undo)

    // 执行一个命令并检查它是否需要被添加到历史记录中。
    method executeCommand(command) is
        if (command.execute)
            history.push(command)

    // 从历史记录中取出最近的命令并运行其 undo(撤销)方法。请注意,你并
    // 不知晓该命令所属的类。但是我们不需要知晓,因为命令自己知道如何撤销
    // 其动作。
    method undo() is
        command = history.pop()
        if (command != null)
            command.undo()

命令模式适合应用场景

1、如果你需要通过操作来参数化对象, 可使用命令模式。

命令模式可将特定的方法调用转化为独立对象。 这一改变也带来了许多有趣的应用: 你可以将命令作为方法的参数进行传递、 将命令保存在其他对象中, 或者在运行时切换已连接的命令等。

举个例子: 你正在开发一个 GUI 组件 (例如上下文菜单), 你希望用户能够配置菜单项, 并在点击菜单项时触发操作。

2、如果你想要将操作放入队列中、 操作的执行或者远程执行操作, 可使用命令模式。

同其他对象一样, 命令也可以实现序列化 (序列化的意思是转化为字符串), 从而能方便地写入文件或数据库中。 一段时间后, 该字符串可被恢复成为最初的命令对象。 因此, 你可以延迟或计划命令的执行。 但其功能远不止如此! 使用同样的方式, 你还可以将命令放入队列、 记录命令或者通过网络发送命令。

3、如果你想要实现操作回滚功能, 可使用命令模式。

尽管有很多方法可以实现撤销和恢复功能, 但命令模式可能是其中最常用的一种。

为了能够回滚操作, 你需要实现已执行操作的历史记录功能。 命令历史记录是一种包含所有已执行命令对象及其相关程序状态备份的栈结构。

这种方法有两个缺点。 首先, 程序状态的保存功能并不容易实现, 因为部分状态可能是私有的。 你可以使用备忘录模式来在一定程度上解决这个问题。

其次, 备份状态可能会占用大量内存。 因此, 有时你需要借助另一种实现方式: 命令无需恢复原始状态, 而是执行反向操作。 反向操作也有代价: 它可能会很难甚至是无法实现。

实现方式

1、声明仅有一个执行方法的命令接口。

2、抽取请求并使之成为实现命令接口的具体命令类。 每个类都必须有一组成员变量来保存请求参数和对于实际接收者对象的引用。 所有这些变量的数值都必须通过命令构造函数进行初始化。

3、找到担任发送者职责的类。 在这些类中添加保存命令的成员变量。 发送者只能通过命令接口与其命令进行交互。 发送者自身通常并不创建命令对象, 而是通过客户端代码获取。

4、修改发送者使其执行命令, 而非直接将请求发送给接收者。

5、客户端必须按照以下顺序来初始化对象:

5-1、创建接收者。
5-2、创建命令, 如有需要可将其关联至接收者。
5-3、创建发送者并将其与特定命令关联。

命令模式优缺点

在这里插入图片描述

与其他模式的关系

1、责任链模式、 命令模式、 中介者模式和观察者模式用于处理请求发送者和接收者之间的不同连接方式:

责任链按照顺序将请求动态传递给一系列的潜在接收者, 直至其中一名接收者对请求进行处理。

命令在发送者和请求者之间建立单向连接。

中介者清除了发送者和请求者之间的直接连接, 强制它们通过一个中介对象进行间接沟通。

观察者允许接收者动态地订阅或取消接收请求。

2、责任链的管理者可使用命令模式实现。 在这种情况下, 你可以对由请求代表的同一个上下文对象执行许多不同的操作。

还有另外一种实现方式, 那就是请求自身就是一个命令对象。 在这种情况下, 你可以对由一系列不同上下文连接而成的链执行相同的操作。

3、你可以同时使用命令和备忘录模式来实现 “撤销”。 在这种情况下, 命令用于对目标对象执行各种不同的操作, 备忘录用来保存一条命令执行前该对象的状态。

4、命令和策略模式看上去很像, 因为两者都能通过某些行为来参数化对象。 但是, 它们的意图有非常大的不同。

你可以使用命令来将任何操作转换为对象。 操作的参数将成为对象的成员变量。 你可以通过转换来延迟操作的执行、 将操作放入队列、 保存历史命令或者向远程服务发送命令等。

另一方面, 策略通常可用于描述完成某件事的不同方式, 让你能够在同一个上下文类中切换算法。

5、原型模式可用于保存命令的历史记录。

6、你可以将访问者模式视为命令模式的加强版本, 其对象可对不同类的多种对象执行操作。

C++ 命令模式讲解和代码示例

命令是一种行为设计模式, 它可将请求或简单操作转换为一个对象。

此类转换让你能够延迟进行或远程执行请求, 还可将其放入队列中。

使用示例: 命令模式在 C++ 代码中很常见。 大部分情况下, 它被用于代替包含行为的参数化 UI 元素的回调函数, 此外还被用于对任务进行排序和记录操作历史记录等。

识别方法: 命令模式可以通过抽象或接口类型 (发送者) 中的行为方法来识别, 该类型调用另一个不同的抽象或接口类型 (接收者) 实现中的方法, 该实现则是在创建时由命令模式的实现封装。 命令类通常仅限于一些特殊行为。

概念示例

本例说明了命令设计模式的结构并重点回答了下面的问题:

它由哪些类组成?
这些类扮演了哪些角色?
模式中的各个元素会以何种方式相互关联?

main.cc: 概念示例

/**
 * The Command interface declares a method for executing a command.
 */
class Command {
 public:
  virtual ~Command() {
  }
  virtual void Execute() const = 0;
};
/**
 * Some commands can implement simple operations on their own.
 */
class SimpleCommand : public Command {
 private:
  std::string pay_load_;

 public:
  explicit SimpleCommand(std::string pay_load) : pay_load_(pay_load) {
  }
  void Execute() const override {
    std::cout << "SimpleCommand: See, I can do simple things like printing (" << this->pay_load_ << ")\n";
  }
};

/**
 * The Receiver classes contain some important business logic. They know how to
 * perform all kinds of operations, associated with carrying out a request. In
 * fact, any class may serve as a Receiver.
 */
class Receiver {
 public:
  void DoSomething(const std::string &a) {
    std::cout << "Receiver: Working on (" << a << ".)\n";
  }
  void DoSomethingElse(const std::string &b) {
    std::cout << "Receiver: Also working on (" << b << ".)\n";
  }
};

/**
 * However, some commands can delegate more complex operations to other objects,
 * called "receivers."
 */
class ComplexCommand : public Command {
  /**
   * @var Receiver
   */
 private:
  Receiver *receiver_;
  /**
   * Context data, required for launching the receiver's methods.
   */
  std::string a_;
  std::string b_;
  /**
   * Complex commands can accept one or several receiver objects along with any
   * context data via the constructor.
   */
 public:
  ComplexCommand(Receiver *receiver, std::string a, std::string b) : receiver_(receiver), a_(a), b_(b) {
  }
  /**
   * Commands can delegate to any methods of a receiver.
   */
  void Execute() const override {
    std::cout << "ComplexCommand: Complex stuff should be done by a receiver object.\n";
    this->receiver_->DoSomething(this->a_);
    this->receiver_->DoSomethingElse(this->b_);
  }
};

/**
 * The Invoker is associated with one or several commands. It sends a request to
 * the command.
 */
class Invoker {
  /**
   * @var Command
   */
 private:
  Command *on_start_;
  /**
   * @var Command
   */
  Command *on_finish_;
  /**
   * Initialize commands.
   */
 public:
  ~Invoker() {
    delete on_start_;
    delete on_finish_;
  }

  void SetOnStart(Command *command) {
    this->on_start_ = command;
  }
  void SetOnFinish(Command *command) {
    this->on_finish_ = command;
  }
  /**
   * The Invoker does not depend on concrete command or receiver classes. The
   * Invoker passes a request to a receiver indirectly, by executing a command.
   */
  void DoSomethingImportant() {
    std::cout << "Invoker: Does anybody want something done before I begin?\n";
    if (this->on_start_) {
      this->on_start_->Execute();
    }
    std::cout << "Invoker: ...doing something really important...\n";
    std::cout << "Invoker: Does anybody want something done after I finish?\n";
    if (this->on_finish_) {
      this->on_finish_->Execute();
    }
  }
};
/**
 * The client code can parameterize an invoker with any commands.
 */

int main() {
  Invoker *invoker = new Invoker;
  invoker->SetOnStart(new SimpleCommand("Say Hi!"));
  Receiver *receiver = new Receiver;
  invoker->SetOnFinish(new ComplexCommand(receiver, "Send email", "Save report"));
  invoker->DoSomethingImportant();

  delete invoker;
  delete receiver;

  return 0;
}

Output.txt: 执行结果

Invoker: Does anybody want something done before I begin?
SimpleCommand: See, I can do simple things like printing (Say Hi!)
Invoker: ...doing something really important...
Invoker: Does anybody want something done after I finish?
ComplexCommand: Complex stuff should be done by a receiver object.
Receiver: Working on (Send email.)
Receiver: Also working on (Save report.)

来源:https://refactoringguru.cn/design-patterns/command
仅供学习,非商业用途,侵删

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++设计模式(8)——命令模式 的相关文章

  • Windows 10 Mobile (10.0.14393) 地理围栏后台任务 (LocationTrigger)

    自从10 0 14393 周年纪念更新 LocationTrigger似乎不起作用 我有 Windows Phone 8 1 应用程序 也适用于 UWP 应用程序 输出到的便携式库Windows Runtime Component图书馆 w
  • Subversion 和 Visual Studio 项目的最佳实践

    我最近开始在 Visual Studio 中处理各种 C 项目 作为大型系统计划的一部分 该系统将用于替换我们当前的系统 该系统是由用 C 和 Perl 编写的各种程序和脚本拼凑而成的 我现在正在进行的项目已经达到了颠覆的临界点 我想知道什
  • ASP.NET Core 与现有的 IoC 容器和环境?

    我想运行ASP NET 核心网络堆栈以及MVC在已托管现有应用程序的 Windows 服务环境中 以便为其提供前端 该应用程序使用 Autofac 来处理 DI 问题 这很好 因为它已经有一个扩展Microsoft Extensions D
  • 为什么Apache MPM prefork.c 使用互斥体来保护accept()?

    我坐下来读书Apache 的 MPM prefork c http code metager de source xref apache httpd server mpm prefork prefork c这段代码使用了一个名为accept
  • 在 C# 中生成 HMAC-SHA1

    我正在尝试使用 C 来使用 REST API API 创建者提供了以下用于 hmac 创建的伪代码 var key1 sha1 body var key2 key1 SECRET KEY var key3 sha1 key2 var sig
  • 在 C++ 中将成对向量转换为两个独立向量的最快方法

    假设我有一个vector of pair
  • 在 C# Winforms 应用程序中嵌入 Windows XP 主题

    我有一个旧版 C Windows 窗体应用程序 其布局是根据 Windows XP 默认主题设计的 由于需要将其作为 Citrix 应用程序进行分发 该应用程序现在看起来像经典主题应用程序 因为 Citrix 不鼓励使用主题系统服务 所以
  • 如何创建用于 QML 的通用对象模型?

    我想知道是否有任何宏或方法如何将 Qt 模型注册为 QObject 的属性 例如 我有AnimalModel http doc qt io qt 5 qtquick modelviewsdata cppmodels html qabstra
  • SFINAE 如何使用省略号?

    过去 当使用 SFINAE 选择构造函数重载时 我通常使用以下内容 template
  • 两种类型的回发事件

    1 我发现了两篇文章 每篇文章对两种类型的回发事件的分类都略有不同 一位资源说两种类型的回发事件是Changed事件 其中控件实现 IPostbackDataHandler 当数据在回发之间更改时触发 然后Raised事件 其中控件实现 I
  • C# 委托责任链

    为了我的理解目的 我实现了责任链模式 Abstract Base Type public abstract class CustomerServiceDesk protected CustomerServiceDesk nextHandle
  • libxml2 xmlChar * 到 std::wstring

    libxml2似乎将所有字符串存储在 UTF 8 中 如xmlChar xmlChar This is a basic byte in an UTF 8 encoded string It s unsigned allowing to pi
  • C++ 插件的“最适合”动态类型匹配

    我有一个几乎所有东西都是插件的架构 该架构以图形用户界面为基础 其中每个插件都由一个 表面 即用户可以通过其与插件交互的 UI 控件 表示 这些表面也是插件 每当添加新插件时 瘦主机都会自动确定哪个可用表面与其最匹配的 UI 如何在 C 中
  • 分配器感知容器和propagate_on_container_swap

    The std allocator traits模板定义了一些常量 例如propagate on container copy move assign让其他容器知道它们是否应该在复制或移动操作期间复制第二个容器的分配器 我们还有propag
  • C# 中的常量和只读? [复制]

    这个问题在这里已经有答案了 可能的重复 const 和 readonly 之间有什么区别 https stackoverflow com questions 55984 what is the difference between cons
  • WPF。如何从另一个窗口隐藏/显示主窗口

    我有两个窗口 MainWindow 和 Login 显示登录的按钮位于主窗口 this Hide Login li new Login li Show 登录窗口上有一个检查密码的按钮 如果密码正确 我如何显示主窗口 将参数传递给 MainW
  • DataTable:通过 LINQ 或 LAMBDA 进行动态 Group By 表达式

    我有一个数据表 我想在其中对未指定数量的字段进行分组 发生这种情况的原因是用户可以选择他想要分组的字段 所以 实际上 我将选择推入列表中 在这个选择上 我必须对我的数据表进行分组 想象一下这段代码 VB 或 C 都一样 public voi
  • 从 Delphi 调用 C# dll

    我用单一方法编写了 Net 3 5 dll 由Delphi exe调用 不幸的是它不起作用 步骤 1 使用以下代码创建 C 3 5 dll public class MyDllClass public static int MyDllMet
  • 在 C++17 中使用 成员的链接错误

    我在 Ubuntu 16 04 上使用 gcc 7 2 并且需要使用 C 17 中的新文件系统库 尽管确实有一个名为experimental filesystem的库 但我无法使用它的任何成员 例如 当我尝试编译此文件时 include
  • 受限 AppDomain 中的代码访问安全异常

    Goal 我需要在权限非常有限的 AppDomain 中运行一些代码 它不应该访问任何花哨或不安全的内容 except对于我在其他地方定义的一些辅助方法 我做了什么 我正在创建一个具有所需基本权限的沙箱 AppDomain 并创建一个运行代

随机推荐

  • SpringBoot3基础:最简项目示例

    说明 本文建立一个最基本的SpringBoot3项目 依赖项仅包含 spring web SpringMVC 备注 SpringBoot3需要JDK17支持 配置方法参考 SpringBoot3项目中配置JDK17 项目结构图示 POM
  • 现在学UI设计有前途吗?UI设计收入大概多少

    随着互联网的高速发展以及大量的人奔涌进入UI设计行业 我们发现想要通过UI实现高薪就业变得不再容易 这让很多人担忧 现在学UI设计还有前途吗 千锋郑州UI设计老师从市场需求 就业薪资以及职业发展方向三个角度分析后可以负责的告诉你有前途 UI
  • docke的基础入门

    docker基础入门操作 一 如何安装docker 一 如何安装docker 安装docker命令通过一下命令顺序执行 即可进行安装 校验操作系统内核版本 要求是3 10以上的版本 1 安装一些必要的系统工具 其中yum utils包含yu
  • 由于找不到vcomp140.dll无法继续执行此代码?该怎么修复呢?

    在使用计算机过程中 我们有时会遇到缺少vcomp140 dll文件的问题 这可能导致某些应用程序无法正常运行 遇到了缺少vcomp140 dll文件的问题 这给我的正常工作和娱乐带来了一些困扰 经过一番努力 我成功修复了这个问题 并对此总结
  • shell中括号的特殊用法 linux if多条件判断

    shell中括号的特殊用法 linux if多条件判断 一 bash 单双括号 基本要素 两个符号左右都要有空格分隔 内部操作符与操作变量之间要有空格 如 a b 字符串比较中 gt lt 需要写成 gt lt 进行转义 中字符串或者 变量
  • Cause: org.jetbrains.plugins.gradle.tooling.util.ModuleComponentIdentifierImpl.getM的原因及解决办法

    一 原因 从别人那里拿来的gradle项目 然后自己用idea跑的时候报错 Cause org jetbrains plugins gradle tooling util ModuleComponentIdentifierImpl getM
  • linux 解锁用户被锁

    一般我们在开发时部署好了环境 有时候用xshell 登录服务器时我们经常会忘记用户的登录密码 我们经常会遇到用户被锁定 首先 用root 用户查看 查看被锁用户的错误登录次数 pam tally2 u tom tom 为用户 pam tal
  • qt集成cef QWidget

    编译libcef dll wrapper 假设你已经编译出了libcef dll wrapper lib Debug和Release版本 并且对应版本的程序集类型分别是 MDd和MD qt的运行时库是MDd类型的 因此cef3编译的时候也应
  • hive 异常之 MetaException

    直接启动hive后 hive gt show databases FAILED SemanticException org apache hadoop hive ql metadata HiveException java lang Run
  • vue3中对本地存储的数据多次修改并实时页面显示

    背景 项目中遇到切换用户时 对App页面的信息进行实时显示 登录时存储一次 切换时再次存储 解决办法 1 在每次存储的同时存储到pinia中 可解决实时显示问题 import useCommonStore from pinia ls set
  • 软件测试中的43个功能测试点总结

    功能测试就是对产品的各功能进行验证 根据功能测试用例 逐项测试 检查产品是否达到用户要求的功能 针对web系统的常用测试方法如下 1 页面链接检查 每一个链接是否都有对应的页面 并且页面之间切换正确 可以使用一些工具 如LinkBotPro
  • C++学习(四八七)android studio println的输出位置

    程序中调用如下输出 System out println haha1 调试情况下 在Run和LogCat下均看不到输出 运行情况下 在Run下能看到输出 建议如下 可在LogCat中看到信息 android util Log常用的方法有以下
  • 蓝牙之十三-HFPclient JNI层

    JNI到app JAVA
  • 开营

    2021未来杯区块链应用创新大赛已于9月24日正式启动 本届大赛是由中国信息协会主办 中国信息协会教育分会 艾肯文化传媒 北京 有限公司 中软国际教育科技集团 以太坊行星承办 北京大学研发实验服务基地 iCAN国际联盟 STEERTECH科
  • echarts修改折线图样式,总结踩坑以及常用

    以折线图为例 最终呈现的效果是这样的 在最外层可以设置 距离外层box的距离 myChart setOption grid 距离外层box左右位置 x 30 左 y 45 上 x2 45 右 y2 40 下 borderWidth 1 在x
  • 【WSN无线传感器网络恶意节点】使用 MATLAB 进行无线传感器网络部署研究

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 2 运行结果 3 参考文献 4 Matlab代码实现 1 概述 在无线传感器网络部署方面 您计划在一个 1
  • React重新渲染的触发机制及其优化策略

    React是一个用于构建用户界面的JavaScript库 它的核心特点之一是使用虚拟DOM Virtual DOM 来实现高效的组件渲染 那组件重新渲染的机制是如何呢 基于这些机制 如果进行优化呢 虚拟DOM是一个用JavaScript对象
  • 逆向破解学习-登山赛车

    试玩 课程中的内容 Hook代码 import de robv android xposed XC MethodHook import de robv android xposed XposedHelpers import de robv
  • React Router 路由守卫

    React Router 路由守卫 组件内路由守卫 1 下面是使用高阶组件实现路由守卫的示例代码 import React from react import Route Redirect from react router dom con
  • C++设计模式(8)——命令模式

    命令模式 亦称 动作 事务 Action Transaction Command 意图 命令模式是一种行为设计模式 它可将请求转换为一个包含与请求相关的所有信息的独立对象 该转换让你能根据不同的请求将方法参数化 延迟请求执行或将其放入队列中