文档章节

设计模式-命令模式

hell03W
 hell03W
发布于 2016/11/18 11:47
字数 2424
阅读 17
收藏 1
点赞 0
评论 0

1. 命令模式概述

在软件开发中, 我们经常会在一个类中调用另一个类的方法, 简单地说, 就是给另一个类发送一条命令, 这时候, 这两个类之间耦合性是非常高的, 任何一方的任何变化都会引起另一个的变化, 为了解决这个问题, 引入了命令模式的概念, 简单说, 命令模式就是在这两个类之间引入一个抽象命令类, 命令的发送者针对抽象命令类编程, 这时候, 命令的发送者并不需要知道命令接收者到底是谁, 也不需要知道指定会被怎样执行, 只需要简单的发送出去即可; 实际命令类包含对命令接收者的引用, 当命令发送者调用发送命令时候, 实际命令类负责转发命令给命令接收者, 命令接收者负责具体的操作.(以上纯属个人见解)

命令模式可以将请求发送者和接收者完全解耦, 命令发送者和接收者之间没有直接引用关系, 发送请求的对象只需要知道如何发送请求, 而不必知道如何完成请求.

命令模式: 将请求封装成一个对象, 从而可用不同的请求对客户进行参数化; 对请求队列或者记录请求日志, 以及支持可撤销的操作. 命令模式是一种行为型模式, 其别名是动作模式或事务模式.

命令模式的核心在于引入了命令类, 通过命令类来降低发送者和接收者的耦合度, 请求发送者只需指定一个命令对象, 再通过命令对象来调用请求接收者的处理方法.

在命令模式结构图中包含如下几个角色:

  • Command(抽象命令类):抽象命令类一般是一个抽象类或接口,在其中声明了用于执行请求的execute()等方法,通过这些方法可以调用请求接收者的相关操作。
  • ConcreteCommand(具体命令类):具体命令类是抽象命令类的子类,实现了在抽象命令类中声明的方法,它对应具体的接收者对象,将接收者对象的动作绑定其中。在实现execute()方法时,将调用接收者对象的相关操作(Action)。
  • Invoker(调用者):调用者即请求发送者,它通过命令对象来执行请求。一个调用者并不需要在设计时确定其接收者,因此它只与抽象命令类之间存在关联关系。在程序运行时可以将一个具体命令对象注入其中,再调用具体命令对象的execute()方法,从而实现间接调用请求接收者的相关操作。
  • Receiver(接收者):接收者执行与请求相关的操作,它具体实现对请求的业务处理。

命令模式的本质是对请求进行封装, 一个请求对应一个命令, 将发出命令的责任和执行命令的责任分隔开. 每一个命令都是一个操作: 请求的一方发出请求要求执行一个操作; 接收的一方收到请求, 并执行相应的操作. 命令模式允许请求的一方和接收的一方单独开来, 🙆请求的一方必须知道接收请求的一方的接口, 更不必知道请求如何被接收, 操作是否被执行, 何时被执行, 以及怎么被执行的.

命令模式的关键在于引入了抽象命令类, 请求发送者针对抽象命令类编程, 只有实现了抽象命令类具体命令才与请求接收者相关联.

2. 命令模式完整的swift实现

Swift版命令模式类图:

// 命令发送者类
class Invoker: NSObject {
    
    var command: Command
    
    init(command: Command) {
        
        self.command = command
    }
    
    func onClick() {
        command.execute()
    }

}



// 抽象命令类, 定义执行命令的方法
protocol Command {
    
    func execute();
}



// 最小化窗口的具体命令类
class MiniWindowCommand: NSObject, Command {
    
    var handler: ReceiveA
    
    init(handler: ReceiveA) {
        
        self.handler = handler
    }
    
    func execute() {
        handler.miniSize()
    }
}



// 展示帮助详情信息的命令类
class HelpCommand: NSObject, Command {
    
    var handler: ReceiveB
    
    init(handler: ReceiveB) {
        
        self.handler = handler
    }
    
    func execute() {
        self.handler.display()
    }
}



// 命令接收者
class ReceiveA: NSObject {
    
    func miniSize() {
        print("窗口被最小化")
    }
}



// 命令接收者
class ReceiveB: NSObject {
    
    func display() {
        print("展示帮助信息")
    }
}

客户端代码以及结果

3. 命令队列的实现

有时候我们需要将多个请求排队, 当一个请求发送者发送一个请求时, 将不止一个请求接收者产应响应, 这些请求接收者将逐个执行业务方法, 完成请求的处理. 此时我们可以通过请求队列来实现.

命令队列的实现方法有多重, 最常用,最灵活最好的一种方式是增减一个增加一个CommandQueue类, 由该类负责存储多个命令对象, 而不同的命令对象可以对应不同的请求接收者.

简单的说, 命令队列就是在抽象命令类和命令发送者之间加入一个CommandQueue对象来统一管理所有的命令类, 接收来自命令发送者的命令, 转发命令到所有命令接收者之中.(本段为个人理解)

Swift版本代码实现如下:

// swift中的数组, 当前还不能直接从数组中删除元素, 所以这里使用NSMutableArray.
class CommandQueue: NSObject, Command {
    
    lazy var commandQueue = NSMutableArray()
    
    func addCommand(command: Command) {
        commandQueue.add(command)
    }
    
    func removeCommand(command: Command) {
        commandQueue.remove(command)
    }
    
    func execute() {
        for command in commandQueue {
            (command as! Command).execute()
        }
    }
}

4. 宏命令

宏命令又称为组合命令, 它是组合模式和命令模式联合的产物. 宏命令是一个具体的命令类, 她拥有一个集合属性, 在该集合中包含了对其他命令对象的引用. 通常宏命令不直接与请求接收者交互, 而是通过它的成员来调用接收者的方法, 当调用宏命令的execute()方法时 ,将递归调用它所包含的每个成员命令的execute方法, 一个宏命令的成员可以是简单命令, 还可以继续是宏命令, 执行一个宏命令将处罚多个具体命令的执行, 从而实现对命令的批处理.

首先, 宏命令, 听这名字叼的一逼, 但是仔细分析后发现, 其实就那么回事, 如果命令队列实现了抽象命令类其实就可以看成是一个宏命令, 一个宏命令类可以包含其它具体宏命令类和具体命令类, 因为都遵循抽象类嘛! 不过这名字还是可以的.

5. 命令模式总结

命令模式是一种使用频率非常高的设计模式,它可以将请求发送者与接收者解耦,请求发送者通过命令对象来间接引用请求接收者,使得系统具有更好的灵活性和可扩展性。在基于GUI的软件开发,无论是在电脑桌面应用还是在移动应用中,命令模式都得到了广泛的应用。

主要优点

  1. 降低系统的耦合度。由于请求者与接收者之间不存在直接引用,因此请求者与接收者之间实现完全解耦,相同的请求者可以对应不同的接收者,同样,相同的接收者也可以供不同的请求者使用,两者之间具有良好的独立性。
  2. 新的命令可以很容易地加入到系统中。由于增加新的具体命令类不会影响到其他类,因此增加新的具体命令类很容易,无须修改原有系统源代码,甚至客户类代码,满足“开闭原则”的要求。
  3. 可以比较容易地设计一个命令队列或宏命令(组合命令)。
  4. 为请求的撤销(Undo)和恢复(Redo)操作提供了一种设计和实现方案。

主要缺点

使用命令模式可能会导致某些系统有过多的具体命令类。因为针对每一个对请求接收者的调用操作都需要设计一个具体命令类,因此在某些系统中可能需要提供大量的具体命令类,这将影响命令模式的使用。

适用场景

  1. 系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。请求调用者无须知道接收者的存在,也无须知道接收者是谁,接收者也无须关心何时被调用。
  2. 系统需要在不同的时间指定请求、将请求排队和执行请求。一个命令对象和请求的初始调用者可以有不同的生命期,换言之,最初的请求发出者可能已经不在了,而命令对象本身仍然是活动的,可以通过该命令对象去调用请求接收者,而无须关心请求调用者的存在性,可以通过请求日志文件等机制来具体实现。
  3. 系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作。
  4. 系统需要将一组操作组合在一起形成宏命令。

更多详情请参考原文:

Reference: http://blog.csdn.net/lovelion/article/details/879673

本文转载自:http://blog.csdn.net/lovelion/article/details/8796736

共有 人打赏支持
hell03W
粉丝 9
博文 143
码字总数 108663
作品 0
朝阳
程序员

暂无文章

Android 获取各大音乐平台的真实下载地址

废话 电脑使用谷歌浏览器或者QQ浏览器的时候。。。。。。。说不清楚,还是看图吧 大概意思就是,只要网页上需要播放,只要能播放并且开始播放,这个过程就肯定会请求到相关的音乐资源,然后就...

她叫我小渝
33分钟前
0
0
shell中的函数、shell中的数组、告警系统需求分析

shell中的函数 格式: 格式: function f_name() { command } 函数必须要放在最前面 示例1(用来打印参数) 示例2(用于定义加法) 示例3(用于显示IP) shell中的数组 shell中的数组1 定义数...

Zhouliang6
今天
2
0
用 Scikit-Learn 和 Pandas 学习线性回归

      对于想深入了解线性回归的童鞋,这里给出一个完整的例子,详细学完这个例子,对用scikit-learn来运行线性回归,评估模型不会有什么问题了。 1. 获取数据,定义问题     没有...

wangxuwei
今天
1
0
MAC安装MAVEN

一:下载maven压缩包(Zip或tar可选),解压压缩包 二:打开终端输入:vim ~/.bash_profile(如果找不到该文件新建一个:touch ./bash_profile) 三:输入i 四:输入maven环境变量配置 MAVEN_HO...

WALK_MAN
今天
0
0
33.iptables备份与恢复 firewalld的9个zone以及操作 service的操作

10.19 iptables规则备份和恢复 10.20 firewalld的9个zone 10.21 firewalld关于zone的操作 10.22 firewalld关于service的操作 10.19 iptables规则备份和恢复: ~1. 保存和备份iptables规则 ~2...

王鑫linux
今天
2
0
大数据教程(2.11):keeperalived+nginx高可用集群搭建教程

上一章节博主为大家介绍了目前大型互联网项目的系统架构体系,相信大家应该注意到其中很重要的一块知识nginx技术,在本节博主将为大家分享nginx的相关技术以及配置过程。 一、nginx相关概念 ...

em_aaron
今天
1
0
Apache Directory Studio连接Weblogic内置LDAP

OBIEE默认使用Weblogic内置LDAP管理用户及组。 要整理已存在的用户及组,此前办法是导出安全数据,文本编辑器打开认证文件,使用正则表达式获取用户及组的信息。 后来想到直接用Apache Dire...

wffger
今天
2
0
HFS

FS,它是一种上传文件的软件。 专为个人用户所设计的 HTTP 档案系统 - Http File Server,如果您觉得架设 FTP Server 太麻烦,那么这个软件可以提供您更方便的档案传输系统,下载后无须安装,...

garkey
今天
1
0
Java IO类库之BufferedInputStream

一、BufferedInputStream介绍 /** * A <code>BufferedInputStream</code> adds * functionality to another input stream-namely, * the ability to buffer the input and to * sup......

老韭菜
今天
0
0
STM 32 窗口看门狗

http://bbs.elecfans.com/jishu_805708_1_1.html https://blog.csdn.net/a1985831055/article/details/77404131...

whoisliang
昨天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部