命令模式,它是一种行为型设计模式,它尝试将请求或操作封装成对象,从而降低系统的耦合度,增加系统的可扩展性,并支持撤销、重做、事务等功能。
在命令模式中,请求被封装为一个独立的对象,称为命令。命令对象包含请求的接收者和对接收者的操作。请求者通过调用命令对象的执行方法来发送请求,接收者则负责执行命令。
命令模式包括以下角色:
命令(Command):封装了一次请求,包括请求的接收者和对接收者的操作。
接收者(Receiver):真正执行命令操作的对象。
请求者(Invoker):负责向命令对象发起请求,并将命令对象设置为接收者的命令。
客户端(Client):创建请求者、命令和接收者对象,并将它们组装起来。
命令模式的使用场景:
1、当系统的某项操作具备命令语义,且命令实现不稳定(变化)时,可以通过命令模式解耦请求与实现。使用抽象命令接口使请求方的代码架构稳定,封装接收方具体命令的实现细节。接收方与抽象命令呈现弱耦合(内部方法无需一致),具备良好的扩展性。
2、当请求调用者需要与请求接收者解耦时,命令模式可以使调用者和接收者不直接交互。
3、系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作时,可以使用命令模式。
4、系统需要在不同的时间指定请求、将请求排队和执行请求时,可以使用命令模式。
5、系统需要执行一组操作时,命令模式可以定义宏命令来实现该功能。
总之,命令模式适用于那些需要将请求和操作封装成对象,降低系统的耦合度,增加系统的可扩展性,并支持撤销、重做、事务等功能的情况。
命令模式的创建步骤:
1、定义接收者类(Receiver),包含执行命令所需要的具体操作。
2、定义命令接口(Command),包含执行命令的方法。
3、创建具体命令类(ConcreteCommand),实现命令接口,并接受接收者作为参数,实现具体命令。
4、创建接收者类,接受具体命令作为参数,实现具体命令。
5、创建请求者类(Invoker),包含执行命令的方法,该方法接受具体命令作为参数。
6、在请求者类中调用具体命令的执行方法,将接收者作为参数传递给具体命令。
7、在客户端中创建请求者对象和具体命令对象,并将它们组装起来。
以上步骤是创建命令模式的基本步骤,可以根据具体情况进行适当的修改和扩展。
命令模式的优点,主要包括:
1、请求发送者和接收者解耦:通过引入中间件(抽象接口)降低系统的耦合度,请求发送者不直接与接收者交互,请求发送者只需要知道如何执行命令,而不需要了解命令的具体实现。
2、支持撤销和重做:命令模式可以容易地实现撤销和重做操作,因为每个命令都可以实现自己的撤销和重做逻辑。
3、事务支持:命令模式可以支持事务操作,即一系列命令的组合,如果其中一个命令执行失败,可以撤销整个事务。
4、命令的参数化:命令模式可以将命令封装成对象,并使用参数来传递数据,从而方便地对命令进行定制和扩展。
5、命令的队列化:命令模式可以较容易地设计一个命令队列,从而按照一定的顺序执行命令。
6、降低类的耦合度:命令模式把请求一个操作的对象与知道怎么执行一个操作的对象分割开,可以让类的单一职责更明确,充分解耦。
7、方便添加新命令:由于加进新的具体命令类不影响其它的类,因此增加新的具体命令类很容易。
命令模式的缺点,主要包括:
1、可能产生大量具体命令类:为了支持各种命令,需要定义大量的具体命令类,这会增加系统的复杂度和维护成本。
2、请求调用者和接收者仍然存在一定的耦合:虽然命令模式使得请求调用者和接收者解耦,但它们仍然存在一定的耦合关系,因为请求调用者需要知道接收者的存在以及如何调用接收者的方法。
3、实现复杂度较高:命令模式需要定义抽象命令接口和具体命令类,以及实现撤销和恢复等功能,实现起来较为复杂。
4、对事务的支持可能不完整:虽然命令模式可以支持事务操作,但对于复杂的事务支持可能不完整,需要额外设计。
以下是一个示例,展示了如何在C#中实现命令模式:
// 定义命令接口
public interface ICommand
{
void Execute();
void Undo();
}
// 定义具体命令类
public class ConcreteCommand : ICommand
{
private Receiver receiver;
public ConcreteCommand(Receiver receiver)
{
this.receiver = receiver;
}
public void Execute()
{
receiver.Operation();
}
public void Undo()
{
receiver.Undo();
}
}
// 定义接收者类
public class Receiver
{
public void Operation()
{
// 执行操作
}
public void Undo()
{
// 撤销操作
}
}
// 定义请求者类
public class Invoker
{
private ICommand command;
public void SetCommand(ICommand command)
{
this.command = command;
}
public void ExecuteCommand()
{
command.Execute();
}
}
在客户端代码中,可以创建具体命令对象和请求者对象,并将它们组装起来,然后通过请求者对象来执行命令。
public class Client
{
public void Test(){
Receiver receiver = new Receiver(); // 创建接收者对象
ConcreteCommand command = new ConcreteCommand(receiver); // 创建具体命令对象,并传入接收者对象作为参数
Invoker invoker = new Invoker(); // 创建请求者对象
invoker.SetCommand(command); // 将具体命令对象设置给请求者对象作为属性使用、回调或引用,从而实现请求者和具体命令对象的解耦。这样客户端代码只需要与请求者对象交互即可执行命令。
}
}
命令模式通常通过以下方式实现:
interface Command {
void execute();
void undo();
}
class ConcreteCommand implements Command {
private Receiver receiver;
public ConcreteCommand(Receiver receiver) {
this.receiver = receiver;
}
@Override
public void execute() {
receiver.operation();
}
@Override
public void undo() {
receiver.undo();
}
}
class Receiver {
public void operation() {
// 执行操作
}
public void undo() {
// 撤销操作
}
}
class Invoker {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void executeCommand() {
command.execute();
}
}
public class Demo {
public static void main(String[] args) {
Receiver receiver = new Receiver(); // 创建接收者对象
ConcreteCommand command = new ConcreteCommand(receiver); // 创建具体命令对象,并传入接收者对象作为参数
Invoker invoker = new Invoker(); // 创建请求者对象
invoker.setCommand(command); // 将具体命令对象设置给请求者对象作为属性使用、回调或引用,从而实现请求者和具体命令对象的解耦。这样客户端代码只需要与请求者对象交互即可执行命令。
}
}
在JavaScript中,命令实现方式如下:
class Command {
constructor(executor) {
this.executor = executor;
}
execute() {
this.executor();
}
}
function executeCommand(command) {
command.execute();
}
function greet() {
console.log('Hello, World!');
}
const greetCommand = new Command(greet);
executeCommand(greetCommand); // 输出:Hello, World!
在上面的代码中,我们定义了一个Command类,它接受一个执行器函数作为构造函数的参数。执行器函数是在Command对象被调用时执行的函数。我们还定义了一个executeCommand函数,它接受一个Command对象作为参数,并调用其execute方法来执行命令。最后,我们创建了一个greet函数作为执行器函数,并将其传递给Command构造函数来创建一个greetCommand对象。然后,我们调用executeCommand函数并将greetCommand对象作为参数传递给它,从而执行了greet函数。
通过将函数或方法封装成对象,我们可以将其传递给其他函数或方法,以便在不同的上下文中使用。这种模式可以用于实现撤销操作、事务处理、日志记录等功能。
以下是在C++中实现命令模式:
class Command {
public:
virtual ~Command() {}
virtual void execute() = 0;
virtual void undo() = 0;
};
class ConcreteCommand : public Command {
public:
ConcreteCommand(Receiver* receiver) : m_receiver(receiver) {}
void execute() override {
m_receiver->operation();
}
void undo() override {
m_receiver->undo();
}
private:
Receiver* m_receiver;
};
class Receiver {
public:
void operation() {
// 执行操作
}
void undo() {
// 撤销操作
}
};
class Invoker {
public:
Invoker(Receiver* receiver) : m_receiver(receiver) {}
void setCommand(Command* command) {
m_command = command;
}
void executeCommand() {
m_command->execute();
}
void undoCommand() {
m_command->undo();
}
private:
Command* m_command;
Receiver* m_receiver;
};
int main() {
Receiver* receiver = new Receiver(); // 创建接收者对象
ConcreteCommand* command = new ConcreteCommand(receiver); // 创建具体命令对象,并传入接收者对象作为参数
Invoker* invoker = new Invoker(command); // 创建请求者对象,并将具体命令对象传递给请求者对象作为属性使用、回调或引用,从而实现请求者和具体命令对象的解耦。这样客户端代码只需要与请求者对象交互即可执行命令。
}
以下是在python中实现命令模式:
class Command:
def execute(self):
pass
def undo(self):
pass
class ConcreteCommand(Command):
def __init__(self, receiver):
self.receiver = receiver
def execute(self):
self.receiver.operation()
def undo(self):
self.receiver.undo()
class Receiver:
def operation(self):
print("Operation executed")
def undo(self):
print("Operation undone")
class Invoker:
def __init__(self):
self.command = None
def set_command(self, command):
self.command = command
def execute_command(self):
self.command.execute()
if __name__ == "__main__":
receiver = Receiver()
command = ConcreteCommand(receiver)
invoker = Invoker()
invoker.set_command(command)
invoker.execute_command() # Output: Operation executed
以下是一个示例,展示了如何在go中实现命令模式:
type Command interface {
Execute()
Undo()
}
type ConcreteCommand struct {
receiver Receiver
}
func (c *ConcreteCommand) Execute() {
c.receiver.Operation()
}
func (c *ConcreteCommand) Undo() {
c.receiver.Undo()
}
type Receiver struct {}
func (r *Receiver) Operation() {
fmt.Println("Operation executed")
}
func (r *Receiver) Undo() {
fmt.Println("Operation undone")
}
type Invoker struct {
command Command
}
func (i *Invoker) SetCommand(cmd Command) {
i.command = cmd
}
func (i *Invoker) ExecuteCommand() {
i.command.Execute()
}
func main() {
receiver := &Receiver{} // 创建接收者对象
command := &ConcreteCommand{receiver} // 创建具体命令对象,并传入接收者对象作为参数
invoker := &Invoker{} // 创建调用者对象
invoker.SetCommand(command) // 将具体命令对象传递给调用者对象作为属性使用、回调或引用,从而实现请求者和具体命令对象的解耦。这样客户端代码只需要与调用者对象交互即可执行命令。
}
以下是一个示例,展示了如何在PHP中实现命令模式:
interface Command {
public function execute();
public function undo();
}
class ConcreteCommand implements Command {
private $receiver;
public function __construct(Receiver $receiver) {
$this->receiver = $receiver;
}
public function execute() {
$this->receiver->operation();
}
public function undo() {
$this->receiver->undo();
}
}
class Receiver {
public function operation() {
echo "Operation executed";
}
public function undo() {
echo "Operation undone";
}
}
class Invoker {
private $command;
public function setCommand(Command $command) {
$this->command = $command;
}
public function executeCommand() {
$this->command->execute();
}
}
在客户端代码中,可以创建具体命令对象和调用者对象,并将它们组装起来。然后通过调用者对象来执行命令。例如:
$receiver = new Receiver(); // 创建接收者对象
$command = new ConcreteCommand($receiver); // 创建具体命令对象,并传入接收者对象作为参数
$invoker = new Invoker(); // 创建调用者对象
$invoker->setCommand($command); // 将具体命令对象传递给调用者对象作为属性使用、回调或引用,从而实现请求者和具体命令对象的解耦。这样客户端代码只需要与调用者对象交互即可执行命令。
《完结》