【设计模式深度剖析】【2】【行为型】【命令模式】| 以打开文件按钮、宏命令、图形移动与撤销为例加深理解
👈️上一篇:模板方法模式 | 下一篇:职责链模式👉️
设计模式-专栏👈️
文章目录
- 命令模式
- 定义
- 英文原话
- 直译
- 如何理解呢?
- 四个角色
- 1. Command(命令接口)
- 2. ConcreteCommand(具体命令类)
- 3. Client(客户端)
- 4. Invoker(调用者)
- 5. Receiver(接收者)
- 类图
- 类图分析
- 代码示例
- 命令接口Command
- 具体命令ConcreteCommand
- 客户端Client
- 调用者Invoker
- 接收者Receiver
- 应用
- 命令模式的应用
- 命令模式的优点
- 命令模式的缺点
- 命令模式的使用场景
- 示例解析1:菜单按钮(UI 控件)- 打开文件按钮
- 类图
- 类图分析
- 代码示例
- 示例解析2:宏命令(组合多个命令)
- 类图
- 类图分析
- 代码示例
- 示例解析3:图形移动/撤销(重做功能)(丐版)
- 类图
- 类图分析
- 代码示例
- 示例解析4:图形移动/撤销(重做功能)
- 类图
- 类图分析
- 代码示例
命令模式
命令模式(Command Pattern)又称为行动(Action)模式或交易(Transaction)模式。
命令模式就像是一个魔法卷轴。在这个奇幻的世界里,魔法师(请求者)不需要亲自施展复杂的法术(接收者),他们可以将法术的咒语和步骤记录在魔法卷轴上(命令对象)。
当魔法师需要施展某个法术时,他们只需挥动魔法卷轴,卷轴上的咒语就会自动激活,释放出强大的魔法力量。这样,魔法师就可以轻松地使用各种法术,而无需每次都记住复杂的咒语和步骤。
魔法卷轴的存在让魔法师的技能得以扩展和保存。他们可以将自己创造的独特法术记录在卷轴上,与他人分享或传授。此外,魔法卷轴还具有撤销和重做的能力,如果魔法师在施展法术时出现了错误,他们可以简单地收回卷轴并重新施展。
因此,命令模式就像是一个魔法卷轴,它让魔法的施展变得更加简单、高效和可控,为魔法师们带来了无尽的便利和乐趣。
本文源码,点击查看👈️
定义
英文原话
Encapsulate a request as an object, allowing you to parameterize clients with different requests, queue or log requests, and support undoable operations.
直译
将请求封装为一个对象,使得你可以使用不同的请求对客户端进行参数化;可以对请求进行排队或记录请求日志,并支持可撤销的操作。
在这个定义中,“Encapsulate a request as an object”指的是将请求(或操作)封装为对象的形式,
这样做的好处是可以将请求与其执行者解耦,提高系统的灵活性和可维护性。同时,通过封装请求为对象,还可以实现诸如请求队列、日志记录、撤销/重做等功能。
如何理解呢?
理解命令模式(Command Pattern)的关键在于认识到它如何帮助我们将请求(或操作)封装为对象,并将请求的发送者和接收者解耦。以下是对命令模式的详细理解:
-
请求封装为对象:
在命令模式中,一个请求被封装为一个对象,即命令对象。这个命令对象包含了执行特定操作的必要信息。通过封装请求为对象,我们可以将请求视为与其他对象一样的实体,并对其进行操作,如传递、存储和组合。 -
发送者与接收者解耦:
命令模式允许我们将请求的发送者(客户端)与请求的接收者(执行者)解耦。发送者不需要直接调用接收者的方法,而是通过命令对象来间接地触发接收者的操作。这种解耦提高了系统的灵活性和可扩展性。例如,我们可以轻松地更换接收者(执行不同的操作)或添加新的命令对象(执行新的操作),而无需修改发送者的代码。
-
支持撤销、重做和日志记录:
由于命令对象封装了请求的信息,我们可以轻松地实现撤销、重做和日志记录等功能。通过存储之前执行的命令对象,我们可以在需要时重新执行它们(实现重做)或反向执行它们(实现撤销)。此外,我们还可以记录命令的执行日志,以便后续的分析和调试。 -
排队和调度:
由于命令对象是可存储和可传递的,我们可以将它们放入队列中并按顺序执行(如命令队列),或者根据一定的调度策略来执行它们(如时间调度器)。这允许我们更灵活地控制操作的执行顺序和频率。 -
简化客户端代码:
通过引入命令对象作为中间层,我们可以简化客户端代码。客户端只需要知道如何创建和发送命令对象,而无需关心命令的具体执行细节。这使得客户端代码更加简洁和易于维护。
命令模式通过封装请求为对象并将发送者与接收者解耦,提供了一种灵活的方式来处理请求和操作。它支持撤销、重做、日志记录、排队和调度等功能,并简化了客户端代码。这些特性使得命令模式在许多场景中都非常有用,如GUI操作、数据库事务、游戏开发等。
四个角色
命令模式涉及以下几个主要角色:
1. Command(命令接口)
声明了一个执行操作的接口。
2. ConcreteCommand(具体命令类)
将一个接收者对象绑定于一个动作
通过调用接收者相应的操作,来实现Commond接口的抽象方法。
实现了Command接口,持有接收者(Receiver)对象,并调用接收者的功能来完成命令要执行的操作。
3. Client(客户端)
创建一个具体命令对象并设定它的接收者。
4. Invoker(调用者)
要求该命令执行这个请求。
5. Receiver(接收者)
知道如何实施与执行一个请求的相关的操作。
真正执行命令的对象。任何类都可能成为一个接收者,只要它能够实现命令要求的功能。
类图
类图分析
从类图中可以分析出:
调用者Invoker
持有命令对象,ConcreteCommand
具体命令持有接收者Receiver
对象。
因此,调用者Invoker
对象就可以间接执行接收者Receiver
对象的动作,通过调用具体命令的执行方法execute()
,而该方法实现方式是通过调用接收者Receiver
对象的动作action()
方法。
通过Command
接口将调用者与接收者解耦,发送者不需要直接调用接收者的方法,而是通过命令对象来间接地触发接收者的操作。这种解耦提高了系统的灵活性和可扩展性。
代码示例
命令接口Command
package com.polaris.designpattern.list3.behavioral.pattern02.command.proto;/*** 命令接口Command:命令(Command)角色*/
public interface Command {// 执行命令的方法void execute();
}
具体命令ConcreteCommand
package com.polaris.designpattern.list3.behavioral.pattern02.command.proto;/*** 具体命令ConcreteCommand:具体命令(Concrete Command)角色*/
public class ConcreteCommand implements Command {private Receiver receiver;public ConcreteCommand(Receiver receiver) {this.receiver = receiver;}// 执行方法@Overridepublic void execute() {this.receiver.action();}
}
客户端Client
package com.polaris.designpattern.list3.behavioral.pattern02.command.proto;/*** 创建一个具体命令对象并设定它的接收者*/
public class Client {public static void main(String[] args) {// 接受者Receiver receiver = new Receiver();// 将一个接收者对象绑定于一个动作Command command = new ConcreteCommand(receiver);// 调用者:要求该命令执行这个请求Invoker invoker = new Invoker();invoker.setCommand(command);invoker.callCommand();}
}
调用者Invoker
package com.polaris.designpattern.list3.behavioral.pattern02.command.proto;/*** 调用者Invoker*/
public class Invoker {private Command command;// 构造函数初始化该命令public void setCommand(Command command) {this.command = command;}// 执行命令public void callCommand() {this.command.execute();}
}
接收者Receiver
package com.polaris.designpattern.list3.behavioral.pattern02.command.proto;/*** 接收者Receiver*/
public class Receiver {// 行动方法public void action() {System.out.println("Command executed.");}
}
输出打印
Command executed.
在这个示例中,
Receiver
是知道如何执行请求的对象,ConcreteCommand
是实现了Command
接口的具体命令类,它持有一个Receiver
对象并在execute
方法中调用其action
方法。
Invoker
是调用者,它持有Command
对象并在需要时调用其execute
方法。
Client
是客户端,负责创建和设置命令对象及其接收者。
应用
命令模式的应用
命令模式广泛应用于许多需要执行命令、撤销命令、记录命令历史或实现宏命令的系统中。以下是几个具体的应用场景:
- 图形用户界面 (GUI) 中的按钮:每个按钮可以关联一个命令对象,当按钮被点击时,执行该命令。
- 文本编辑器:撤销和重做功能可以通过命令模式实现,每个编辑操作(如插入、删除、剪切、粘贴等)都可以是一个命令。
- 请求队列:系统可以接收多个命令请求,并将它们存储在一个队列中,然后按照先进先出(FIFO)的顺序执行这些命令。
- 事务处理:在金融系统中,交易可以被封装为命令,从而可以轻松地回滚(撤销)事务。
- 宏录制:用户可以录制一系列命令,然后在需要时重新执行这些命令,以实现自动化操作。
命令模式的优点
- 解耦:命令模式将请求者与执行者解耦,使得请求者不需要知道如何执行命令,只需要知道如何发送命令。
- 易于扩展:由于请求者和执行者之间的解耦,可以在不修改现有代码的情况下添加新的命令。
- 支持撤销/重做:通过保存命令的历史记录,可以很容易地实现撤销和重做功能。
- 易于实现事务:可以将多个命令组合成一个事务,从而确保所有命令都成功执行或全部回滚。
- 易于记录日志和监控:可以轻松地记录每个命令的执行情况和结果,以便于后续的分析和监控。
命令模式的缺点
- 可能导致过多的具体命令类:对于复杂的系统,可能需要大量的具体命令类来实现各种功能,这可能会增加系统的复杂性。
- 可能增加系统的开销:由于命令对象通常包含对接收者的引用,并且在执行过程中可能需要额外的内存来存储命令历史记录,因此可能会增加系统的内存开销。
命令模式的使用场景
当需要满足以下条件时,可以考虑使用命令模式:
- 请求者和执行者需要解耦:当请求者不需要知道如何执行请求,或者执行者不需要知道请求的来源时,可以使用命令模式。
- 支持撤销/重做:如果系统需要支持撤销和重做功能,那么命令模式是一个很好的选择。
- 需要记录请求的历史:如果系统需要记录每个请求的执行情况和结果,以便于后续的分析和监控,那么可以使用命令模式。
- 需要实现事务:如果需要将多个操作组合成一个事务,以确保它们要么全部成功执行,要么全部回滚,那么命令模式是一个很好的选择。
示例解析1:菜单按钮(UI 控件)- 打开文件按钮
在这个示例中,我们有一个 UI 控件(比如一个按钮),当用户点击这个按钮时,它应该执行某个操作(打开文件)。
我们可以使用命令模式来解耦按钮和它所执行的操作。
类图
类图分析
这个类图可以对比之前的角色分析时候的类图,没错,可以发现是一致的结构,是一种场景的具化,为的是通过该具体示例进一步理解命令模式的各个角色。
Button
调用者角色,持有一个命令对象,至于什么具体的命令对象,看用户给他传入什么命令对象,这里是要给它的构造器传入OpenFileCommand
,用来初始化持有的命令对象,即该按钮的意图是要打开文件。而如何打开,打开哪个文件,由命令决定,调用者只需要负责按照自己的需要传入相应的命令即可;OpenFileCommand
具体命令,构造器初始化了它持有的接收者FileManager
对象,以及要打开的文件名,即该命令的目的就是为了打开该指定的文件。该命令的操作方法execute()
通过调用接收者打开文件的方法openFile(String)
来实现的;- 从上面两步分析,可知,接收者
FileManager
并不知道谁会用到它,不知道谁是调用者,而调用者Button
它只持有命令,具体谁(哪个接收者)来处理这个命令,它不直接关心,或者说它并不关心,他只直接知道我使用这个命令能完成这个事情,而具体这个命令如何操作的,由具体命令进行了封装,调用相应的接收者处理调用者的需求。因此,通过命令接口,解耦了调用者与接收者。 - 客户端就是按照上面分析过程中,调用者需要持有一个具体命令对象,具体命令封装了调用哪个接收者的方法来处理调用者的需求,因此客户端
Client
实例化接收者,作为参数构造出具体命令,然后将具体命令对象作为参数构造出调用者对象,然后就是执行,调用者要求该命令执行这个请求。
代码示例
看完类图,然后把类图分析完,下面的代码就了然了。
package com.polaris.designpattern.list3.behavioral.pattern02.command.openfileuidemo;// 命令接口
interface Command { void execute();
} // 打开文件的具体命令
class OpenFileCommand implements Command { private FileManager fileManager; private String fileName; public OpenFileCommand(FileManager fileManager, String fileName) { this.fileManager = fileManager; this.fileName = fileName; } @Override public void execute() { fileManager.openFile(fileName); }
} // 文件管理器(接收者)
class FileManager { public void openFile(String fileName) { System.out.println("打开文件: " + fileName); } // ... 其他文件管理操作
} // UI 控件(调用者)
class Button { private Command command; public Button(Command command) { this.command = command; } public void onClick() { command.execute(); }
} // 客户端代码
public class Client { public static void main(String[] args) { FileManager fileManager = new FileManager(); Command openCommand = new OpenFileCommand(fileManager, "example.txt"); Button openButton = new Button(openCommand); // 假设这是用户点击按钮的事件 openButton.onClick(); // 输出:打开文件: example.txt }
}/* Output"
打开文件: example.txt
*///~
示例解析2:宏命令(组合多个命令)
在这个示例中,我们将展示如何使用命令模式来组合多个命令,形成一个宏命令。
注意一点即可,宏命令是组合了多个命令,它是要一次执行一组操作的。
类图
类图分析
除了接收者宏命令MacroCommand
持有多个命令对象外,和示例1结构无差别。重点理解一组操作的概念,从类图也可以反映出来。
该示例与示例1的区别,我们模拟宏命令即组合多个命令,可以看到调用者MacroCommand
持有一个命令列表,通过addCommand(Command)
方法可以添加具体命令进去,它的需求就是一次将这一组命令依次执行,因此需要遍历执行每个命令。
代码示例
主要关注组合对象的实现即可,当然也可以再次重复理解一下命令模式的各个角色,及他们间的关系。
package com.polaris.designpattern.list3.behavioral.pattern02.command.macrocommand;import com.polaris.designpattern.list3.behavioral.pattern02.command.proto.Command;import java.util.ArrayList;
import java.util.List;// 命令接口
interface Command {// 执行命令的方法void execute();
}// 宏命令(组合多个命令)
// 宏命令(也是命令的一种,可以包含多个子命令)
class MacroCommand implements Command {private List<Command> commands = new ArrayList<>();public void addCommand(Command command) {commands.add(command);}@Overridepublic void execute() {for (Command command : commands) {command.execute();}}
}// 接收者 TextEditor 类,用于编辑文本
class TextEditor {private String text; // 假设这是编辑器中的文本内容public TextEditor() {this.text = "";}// 获取编辑器中的文本public String getText() {return text;}// 设置编辑器中的文本(通常这不会是一个直接设置的方法,但为了简化示例)public void setText(String text) {this.text = text;}// 调整字体大小的方法(这里只是模拟,实际上可能需要更复杂的逻辑)public void adjustFontSize(int size) {// 在真实应用中,这里可能会更新编辑器的UI或状态来反映新的字体大小// 但在这个示例中,我们只是简单地打印一条消息System.out.println("字体大小已调整为: " + size);}// 其他可能的文本编辑方法...
}// 具体的命令(比如调整字体大小)
class AdjustFontSizeCommand implements Command {private TextEditor textEditor;private int size;public AdjustFontSizeCommand(TextEditor textEditor, int size) {this.textEditor = textEditor;this.size = size;}@Overridepublic void execute() {textEditor.adjustFontSize(size);}
}// 客户端代码
public class Client {public static void main(String[] args) {TextEditor textEditor = new TextEditor(); // 假设 TextEditor 是接收者,用于编辑文本 // 创建具体的命令 Command fontSizeUp = new AdjustFontSizeCommand(textEditor, 12); // 增大字体 Command fontSizeDown = new AdjustFontSizeCommand(textEditor, 10); // 减小字体 // 创建宏命令,并添加具体的命令 MacroCommand macroCommand = new MacroCommand();macroCommand.addCommand(fontSizeUp);macroCommand.addCommand(fontSizeDown); // 注意:这里只是示例,通常不会连续执行增大和减小字体 // 执行宏命令 macroCommand.execute(); // 首先执行 fontSizeUp,然后执行 fontSizeDown }
}/* Output:
字体大小已调整为: 12
字体大小已调整为: 10
*///~
示例解析3:图形移动/撤销(重做功能)(丐版)
命令模式也可以用来实现撤销和重做功能。每个命令对象都可以存储其执行前的状态,以便在需要时撤销操作。
这里是一个简化的示例,图形的状态信息,这里主要关注图形的位置,在这里给简化省略了,我们只是通过打印文字模拟了一下。因此省略掉了接收者角色,直接在具体命令的方法中进行打印。
带有图形位置状态信息,且由接收者处理请求的示例见示例解析4
在这个示例,适应一下一个简单的变体:缺少接收者。
类图
类图分析
撤销,重做需要记录之前的状态信息,在这个示例我们简化了状态信息的记录,执行命令,与撤销命令通过在具体命令中进行打印,因此省略了接收者处理请求逻辑,因此在类图就省略了接收者角色;
UndoManager
调用者,持有一个命令执行记录栈,当需要撤销,从栈中弹出一个上一次操作的命令执行该命令的撤销方法。
这里的命令接口的定义,分了两层,一层是基础命令,用来执行命令,另外扩展的带撤销功能的命令接口,定义了撤销方法,具体命令实现接口,也就实现了执行/撤销的逻辑。
代码示例
package com.polaris.designpattern.list3.behavioral.pattern02.command.undodemo.simple;import java.util.Stack;// 基础的命令接口
interface Command {void execute(); // 执行命令
}// 带有撤销功能的命令接口
interface UndoableCommand extends Command {void undo(); // 撤销操作
}// 具体命令:具体的撤销命令(比如移动图形)
class MoveShapeCommand implements UndoableCommand {// 假设这里包含移动图形所需的状态信息 // 例如:起始位置、目标位置等 @Overridepublic void execute() {// 执行移动操作 System.out.println("执行移动操作");// ... 实际移动图形的代码 ... }@Overridepublic void undo() {// 撤销移动操作,将图形移回原来的位置 System.out.println("撤销移动操作");// ... 实际撤销移动的代码 ... }
}// 撤销管理器(调用者)
class UndoManager {private Stack<UndoableCommand> undoStack = new Stack<>();public void executeCommand(UndoableCommand command) {command.execute();undoStack.push(command); // 将命令添加到撤销栈中 }public void undo() {if (!undoStack.isEmpty()) {UndoableCommand command = undoStack.pop();command.undo(); // 调用命令的撤销方法 System.out.println("撤销成功");} else {System.out.println("没有可撤销的命令");}}// 可能还需要一个检查是否有可撤销命令的方法 public boolean canUndo() {return !undoStack.isEmpty();}// 如果需要重做功能,可以添加相应的栈和逻辑
}// 示例使用
public class Client {public static void main(String[] args) {UndoManager undoManager = new UndoManager();UndoableCommand moveCommand = new MoveShapeCommand();// 执行命令 undoManager.executeCommand(moveCommand);// 撤销命令 if (undoManager.canUndo()) {undoManager.undo();}}
}
/* Output:
执行移动操作
撤销移动操作
撤销成功
*///~
示例解析4:图形移动/撤销(重做功能)
相较于上一个示例(示例3),通过增加接收者Shape
类用来处理图形移动逻辑与Point
类用来记录图形的位置信息。
类图
类图分析
UndoManager
调用者在执行命令时候会将执行的命令压栈到栈中,当需要撤销操作时候,出栈弹出上一次的命令,执行撤销动作- 由于需要支持撤销,因此调用者必须持有的具体命令必须带有可撤销能力,因此需要持有
UndoableCommand
类型对象 - 接收者
Shape
构造函数需要指定Point
对象,用来记录它的位置,它有一个获取图形当前所在位置的方法getPositon()
和 移动到指定位置的方法moveTo(Point)
该方法需要传入一个目标位置 - 具体命令构造器需要参数接收者
Shape
对象用来完成移动/撤销移动操作,同时获取该图形的当前位置信息,即原始位置,记录下来 ,用于撤销时候使用 - 具体命令构造器需要参数接收者
Point
对象targetPosition
用来表示需要移动到的位置
代码示例
package com.polaris.designpattern.list3.behavioral.pattern02.command.undodemo.complex;import java.util.Stack;// 基础的命令接口(如果不需要,可以省略)
interface Command {void execute(); // 执行命令
}// 带有撤销功能的命令接口
interface UndoableCommand extends Command {void undo(); // 撤销操作
}// 图形类:用于记录移动图形所需的状态信息
class Shape {Point position; // 假设有一个Point类表示位置 Shape(Point position) {this.position = position;System.out.println("图形原位置:" + position);}// ... 其他图形相关的方法 ... void moveTo(Point newPosition) {this.position = newPosition;System.out.println("图形移动到: " + newPosition);}Point getPosition() {return position;}
}// 点类,表示位置:用于记录移动图形所需的状态信息
class Point {int x, y;Point(int x, int y) {this.x = x;this.y = y;}@Overridepublic String toString() {return "(" + x + ", " + y + ")";}
}// 具体命令:具体的撤销命令(比如移动图形)
class MoveShapeCommand implements UndoableCommand {private Shape shape;private Point originalPosition; // 原始位置 private Point targetPosition; // 目标位置 public MoveShapeCommand(Shape shape, Point targetPosition) {this.shape = shape;this.originalPosition = new Point(shape.getPosition().x, shape.getPosition().y); // 保存原始位置 this.targetPosition = targetPosition;}@Overridepublic void execute() {// 执行移动操作 System.out.println("执行移动操作");shape.moveTo(targetPosition); // 实际移动图形的代码 }@Overridepublic void undo() {// 撤销移动操作,将图形移回原来的位置 System.out.println("撤销移动操作");shape.moveTo(originalPosition); // 实际撤销移动的代码 }
}// 撤销管理器(调用者)
class UndoManager {private Stack<UndoableCommand> undoStack = new Stack<>();public void executeCommand(UndoableCommand command) {command.execute();undoStack.push(command); // 将命令添加到撤销栈中}public void undo() {if (!undoStack.isEmpty()) {UndoableCommand command = undoStack.pop();command.undo(); // 调用命令的撤销方法System.out.println("撤销成功");} else {System.out.println("没有可撤销的命令");}}// 可能还需要一个检查是否有可撤销命令的方法public boolean canUndo() {return !undoStack.isEmpty();}// 如果需要重做功能,可以添加相应的栈和逻辑
}// 示例使用
public class Client {public static void main(String[] args) {UndoManager undoManager = new UndoManager();Shape shape = new Shape(new Point(0, 0)); // 创建一个在(0, 0)位置的图形 UndoableCommand moveCommand = new MoveShapeCommand(shape, new Point(10, 10)); // 移动到(10, 10)位置 // 执行命令undoManager.executeCommand(moveCommand);// 撤销命令if (undoManager.canUndo()) {undoManager.undo();}}
}
/* Output:
图形原位置:(0, 0)
执行移动操作
图形移动到: (10, 10)
撤销移动操作
图形移动到: (0, 0)
撤销成功
*///~
在这个示例中,我们添加了一个
Shape
类来表示图形,以及一个Point
类来表示位置。MoveShapeCommand
现在保存了图形的原始位置和目标位置,以便在execute
和undo
方法中使用。在Main
类的示例中,我们创建了一个在(0, 0)
位置的图形,并创建了一个命令来将其移动到(10, 10)
位置。然后,我们通过UndoManager
执行该命令并撤销它。
👈️上一篇:模板方法模式 | 下一篇:职责链模式👉️
设计模式-专栏👈️
相关文章:
【设计模式深度剖析】【2】【行为型】【命令模式】| 以打开文件按钮、宏命令、图形移动与撤销为例加深理解
👈️上一篇:模板方法模式 | 下一篇:职责链模式👉️ 设计模式-专栏👈️ 文章目录 命令模式定义英文原话直译如何理解呢? 四个角色1. Command(命令接口)2. ConcreteCommand(具体命令类&…...
【随手记】maplotlib.use函数设置图像的呈现方式
matplotlib.use() 函数用于设置 matplotlib 的后端,这会影响图形的呈现方式。不同的后端适用于不同的环境和需求。下面列出一些常用的后端及其描述: 常见后端参数 Agg: 参数:agg描述:基于Anti-Grain Geometry的后端,适…...
LLVM Cpu0 新后端 系列课程总结
想好好熟悉一下llvm开发一个新后端都要干什么,于是参考了老师的系列文章: LLVM 后端实践笔记 代码在这里(还没来得及准备,先用网盘暂存一下): 链接: https://pan.baidu.com/s/1yLAtXs9XwtyEzYSlDCSlqw?…...
【云原生】Kubernetes----RBAC用户资源权限
目录 引言 一、Kubernetes安全机制概述 二、认证机制 (一)认证方式 1.HTTPS证书认证 1.1 证书颁发 1.2 config文件 1.3 认证类型 1.4 Service Account 1.4.1 作用 1.4.2 包含内容 1.4.3 与Secret的关系 2.Bearer Tokens 3.基本认证 三、鉴…...
ORA-01652 表空间不够解决方案
前章:出现表空间不足不要手动强制删除对应数据文件存储目录下的DBF文件,需要用SQL语句进行数据文件的DROP,否则会导致ORA-01033报错,因为我没有开启数据库的归档所以不能通过RECOVER的形式找回数据文件最后只能重装本地ORACLE。 …...
亚马逊 AWS 视频转码功能、AWS Elemental MediaConvert 中创建和管理转码作业
上传的视频需要转码成不同的编码, 可以直接在 AWS Elemental MediaConvert 中创建和管理转码作业 AWS Elemental MediaConvert 中创建和管理转码作业 /*** 视频转码* return bool* author wzb* data 2024/5/30*/function videoTranscode(&$data){$fileId $data[id] ?? …...
RocketMQ可视化界面安装
RocketMQ可视化界面安装 **起因:**访问rocketmq-externals项目的git地址,下载了源码,在目录中并没有找到rocketmq-console文件夹。 git下面文档提示rocketMQ的仪表板转移到了新的项目中,点击仪表板到新项目地址; 下载…...
【ffmpeg】本地格式转换 mp4转wav||裁剪mp4
个人感受:太爽了!!!(可能用惯了转换网站和无良的转换软件) ———— 使用FFmpeg把mp4文件转换为WAV文件 - 简书 (jianshu.com) FFMPEG 视频分割和合并 - 简书 (jianshu.com) ———— 示例 ffmpeg -i …...
基于Django+MySQL的智慧校园系统
此项目基于Django MySQL HTML CSS JS jQuery bootstrap实现的功能有 学生管理部门管理代办清单管理校园论坛校园医疗服务校园看点校园生活助手常用功能入口 1. 一些注意点 1. 页面body会自动有一些边界距,处理方法: <head><style>b…...
Linux基础指令(一)
前言 Linux基础指令主要学习:对目录、文件、压缩包、匹配查找,权限等操作 第一次接触ubuntu需要知道的基本知识 sudo passwd root 先给root用户设置密码 su root 切换到root用户 su zhangsan …...
三极管十大品牌
三极管十大品牌-三极管品牌-晶体三极管哪个品牌好-Maigoo品牌榜...
需求记录(共享元素)
MainActivity1 列表展示,使用共享元素完成页面间的切换 package com.example.animactivity;import android.annotation.SuppressLint; import android.app.ActivityOptions; import android.content.Intent; import android.os.Build; import android.os.Bundle; i…...
.Net 使用 MongoDB
安装nuget包 MongoDB.Driver 简单代码 using MongoDB.Bson; using MongoDB.Driver; using System.Buffers; using System.Collections.Concurrent; using System.Diagnostics;namespace ConsoleApp4 {internal class Program{static void Main(string[] args){var client = ne…...
【TensorFlow深度学习】值函数估计:蒙特卡洛方法与TD学习
值函数估计:蒙特卡洛方法与TD学习 值函数估计:蒙特卡洛方法与TD学习的深度探索蒙特卡洛方法时序差分学习(TD)Python代码示例结论 值函数估计:蒙特卡洛方法与TD学习的深度探索 在强化学习的奇妙世界里,值函数估计扮演着至关重要的…...
成功解决ModuleNotFoundError: No module named ‘cv2’
成功解决ModuleNotFoundError: No module named ‘cv2’ 🌈 欢迎莅临我的个人主页👈这里是我深耕Python编程、机器学习和自然语言处理(NLP)领域,并乐于分享知识与经验的小天地!🎇 🎓…...
中国蚁剑 安装教程 2024年5月
2024/5/11 中国蚁剑 安装教程 一、下载中国蚁剑的加载器和核心源码(两个都要用到) github官方下载地址:https://github.com/AntSwordProject/ 参考文档:antSword/README_CN.md at master AntSwordProject/antSword GitHub 核…...
Golang-分离式加载器(传参)AES加密
目录 enc.go 生成: dec.go --执行dec.go...--上线 cs生成个c语言的shellcode. enc.go go run .\enc.go shellcode 生成: --key为公钥. --code为AES加密后的数据, ----此脚本每次运行key和code都会变化. package mainimport ("bytes""crypto/aes"&…...
速览三版HTTP的改进策略
HTTP(Hypertext Transfer Protocol)是互联网通信的基础协议,自从其第一个版本推出以来,经历了多个版本的改进,每个版本都针对之前的不足进行了优化和增强。以下是HTTP/1.1、HTTP/2和HTTP/3的主要改进总结: …...
window.open(“.html“,“_blank“) 执行是下载,并没有打开新窗口显示html
window.open() 方法在浏览器中打开一个新窗口或者新标签页。如果你的 .html 文件被下载而不是在新窗口中打开,那可能是因为服务器的响应头设置了 Content-Disposition: attachment,这会导致浏览器把响应的内容作为一个文件下载。 如果你有权限修改服务器…...
【QT5.14.2】编译MQTT库example的时候报No such file or directory
【QT5.14.2】编译MQTT库example的时候报No such file or directory 前几天导师让跑一下MQTT库,用的5.14.2版本的QT,于是就上网搜了一个教程:https://www.bilibili.com/video/BV1dH4y1e7hG/?spm_id_from333.337.search-card.all.click&v…...
【数据结构】前缀树(字典树)汇总
基础 {“a”,“abc”,“bac”,“bbc”,“ca” }的字典树如下图: 最主用的应用:一,字符串编码。二,位运算。 字符串编码 相比利用哈希映射编码,优点如下: 依次查询长度为n的字符串s的前缀时间复杂度是O(…...
Linux:基础开发工具
文章目录 Linux 软件包管理器 yum什么是软件包关于rzsz查看软件包安装软件卸载软件安装扩展源 Linux 编辑器 vimvim的基本概念正常/普通/命令模式(Normal mode)插入模式(Insert mode)底行模式(last line mode) vim的基本操作[命令模式]切换至[插入模式][插入模式]切换至[命令模…...
HarmonyOS NEXT Push接入
接入HarmonyOS NEXT Push 推送功能,相比于 Android 真的是简单太多。不再需要适配接入各个厂家的推送 SDK,真是舒服。 1.开通推送服务与配置Client ID 1.1 创建应用获取Client ID 按照官方文档来就可以了:https://developer.huawei.com/co…...
如何快速入门Element-UI:打造高效美观的前端界面
Element-UI 是一款基于 Vue.js 的开源组件库,提供了丰富的 UI 组件,可以帮助开发者快速构建美观、响应式的前端界面。本文将详细介绍如何快速入门 Element-UI,包括环境搭建、组件使用、样式定制及常见问题解决方法,帮助你高效地使用 Element-UI 进行前端开发。 一、环境搭…...
Langchain的向量存储 - Document示例代码里的疑问
文章目录 前言一、语句分析二、 举例解释三、 完整代码总结 前言 之前的代码里有下面这句话,可能有看不明白的读者。 vectors [embeddings.embed(doc.page_content) for doc in docs]今天一起来看下这句话。 一、语句分析 这句话实际上是一个列表推导式&#x…...
Docker 教程-介绍-2
快速了解docker有什么。 Docker简介 Docker 是一个开源的应用容器引擎,基于Go语言开发,并遵循Apache 2.0协议。它允许开发者将应用及其依赖包打包进一个可移植的容器中,这些容器可以发布到任何支持Docker的Linux或Windows机器上,…...
【2024最新华为OD-C/D卷试题汇总】[支持在线评测] 伐木工(200分) - 三语言AC题解(Python/Java/Cpp)
🍭 大家好这里是清隆学长 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 💻 ACM银牌🥈| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 📎在线评测链接 伐木工(200分) 🌍 评测功能需要订阅专栏后私信联系清隆解…...
UltraScale+系列模块化仪器,可以同时用作控制器、算法加速器和高速数字信号处理器
基于 XCZU7EG / XCZU4EG / XCZU2EG • 灵活的模块组合 • 易于嵌入的紧凑型外观结构 • 高性能的 ARM Cortex 处理器 • 成熟的 FPGA 可编程逻辑 ,基于 IP 核的软件库 基于 Xilinx Zynq UltraScaleMPSoC 的 FPGA 技术,采用 Xilinx Zynq UltraScale&a…...
Python与其他编程语言(如Java、C++)相比有哪些优势?
一、技术难点 在探讨Python与其他编程语言相比的优势时,技术难点在于如何全面、准确地把握并阐述这些优势。这需要对Python、Java、C等编程语言有深入的理解,包括它们的语法特性、应用领域、性能特点、开发效率等。 首先,Python的语法简洁明…...
Edge浏览器双击关闭标签页,双击关闭浏览器选项卡
设置》外观》自定义浏览器,开启“使用双击关闭浏览器选项卡” 设置里面搜索“双击”,这是最快的方式 鼠标滚轮单击 或者进入“设置”-“辅助功能” 呼吁已久的功能来了!Edge浏览器双击关闭标签页功能上线新 国产浏览器大多都有双击关闭标签页…...
dede个人网站模板/七牛云
10574 - Counting Rectangles 题目链接 题意:给定一些点,求可以成几个边平行于坐标轴的矩形 思路:先把点按x排序,再按y排序。然后用O(n^2)的方法找出每条垂直x轴的边,保存这些边两点的y坐标y1, y2。之后把这些边按y1排…...
求个靠谱的网站/怎么制作网页链接
在中国,如果是IT工程师,有工作经验很受企业青睐,这也是很多人参加IT培训的原因,尤其是Java开发工程师都喜欢参加培训机构,他们参加Java培训班好就业吗?待遇怎么样? Java开发是高端职业…...
包包网站建设策划书/什么是网络推广员
人生苦短,我选Python 前文传送门 小白学 Python(1):开篇 小白学 Python(2):基础数据类型(上) 小白学 Python(3):基础数据类型&…...
网站怎么做免费seo搜索/云南百度公司
哈希表1.定义2.哈希函数的构造方法2.1直接定址法2.2 数字分析法2.3 平方取中法2.4 折叠法2.5 除留余数法2.6随机数法3.处理冲突的方法3.1 开放定址法3.2 再哈希法3.3 链地址法3.4 公共溢出区法4.hash表的查找5.python-hash表以及哈希查找的实现6.小结1.定义 \quad \quad哈希表&…...
做网站广告哪家好/国内搜索引擎优化的公司
目录 404: (1)访问资源路径错误: (2)classes项目输出路径错误: (3)tomcat没有配置上下文context: 404: (1)访问资源路径错…...
丽水网站建设/搜索网站哪个好
简介swoft是基于swoole协程2.x的高性能PHP微服务框架,内置http服务器。框架全协程实现,性能优于传统的php-fpm模式。基于swoole易扩展内置http协程服务器MVC分层设计高性能路由全局容器注入高性能RPC服务治理熔断、降级、负载、注册与发现RPC服务连接池M…...