点赞再看,养成习惯,公众号搜一搜【一角钱技术】关注更多原创技术文章。本文 GitHub org_hejianhui/JavaStudy 已收录,有我的系列文章。

前言

  • 23种设计模式速记
  • 单例(singleton)模式
  • 工厂方法(factory method)模式
  • 抽象工厂(abstract factory)模式
  • 建造者/构建器(builder)模式
  • 原型(prototype)模式
  • 享元(flyweight)模式
  • 外观(facade)模式
  • 适配器(adapter)模式
  • 装饰(decorator)模式
  • 观察者(observer)模式
  • 策略(strategy)模式
  • 桥接(bridge)模式
  • 模版方法(template method)模式
  • 责任链(chain of responsibility)模式
  • 组合(composite)模式
  • 代理(proxy)模式
  • 备忘录(memento)模式
  • 持续更新中......

23种设计模式快速记忆的请看上面第一篇,本篇和大家一起来学习命令模式相关内容。

模式定义

将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递、调用、增加与管理。

在软件开发系统中,常常出现“方法的请求者”与“方法的实现者”之间存在紧密的耦合关系。这不利于软件功能的扩展与维护。例如,想对行为进行“撤销、重做、记录”等处理都很不方便,因此“如何将方法的请求者与方法的实现者解耦?”变得很重要,命令模式能很好地解决这个问题。

模板实现如下

package com.niuh.designpattern.command.v1;/*** <p>* 命令模式* </p>*/
public class CommandPattern {public static void main(String[] args) {Command cmd = new ConcreteCommand();Invoker ir = new Invoker(cmd);System.out.println("客户访问调用者的call()方法...");ir.call();}
}//抽象命令
interface Command {public abstract void execute();
}//具体命令
class ConcreteCommand implements Command {private Receiver receiver;ConcreteCommand() {receiver = new Receiver();}public void execute() {receiver.action();}
}//接收者
class Receiver {public void action() {System.out.println("接收者的action()方法被调用...");}
}//调用者
class Invoker {private Command command;public Invoker(Command command) {this.command = command;}public void setCommand(Command command) {this.command = command;}public void call() {System.out.println("调用者执行命令command...");command.execute();}
}

输出结果如下

客户访问调用者的call()方法...
调用者执行命令command...
接收者的action()方法被调用...

解决的问题

在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适。

模式组成

可以将系统中的相关操作抽象成命令,使调用者与实现者相关分离,其结构如下。

实例说明

实例概况

结合命令模式,实现一个课程视频的打开和关闭。

使用步骤

步骤1:声明执行命令的接口,拥有执行命令的抽象方法 execute()

interface Command {void execute();
}

步骤2:定义具体命令角色,创建打开课程链接 和 关闭课程连接

/*** 打开课程链接*/
class OpenCourseVideoCommand implements Command {private CourseVideo courseVideo;public OpenCourseVideoCommand(CourseVideo courseVideo) {this.courseVideo = courseVideo;}@Overridepublic void execute() {courseVideo.open();}
}/*** 关闭课程链接*/
class CloseCourseVideoCommand implements Command {private CourseVideo courseVideo;public CloseCourseVideoCommand(CourseVideo courseVideo) {this.courseVideo = courseVideo;}@Overridepublic void execute() {courseVideo.close();}
}

步骤3:定义接收者角色,执行命令功能的相关操作,是具体命令对象业务的真正实现者

class CourseVideo {private String name;public CourseVideo(String name) {this.name = name;}public void open() {System.out.println(this.name + "课程视频开放。");}public void close() {System.out.println(this.name + "课程视频关闭。");}
}

步骤4:创建User对象为请求的发送者,即请求者角色

class User {private List<Command> commands = new ArrayList<>();public void addCommand(Command command) {commands.add(command);}public void executeCommands() {commands.forEach(Command::execute);commands.clear();}
}

步骤4:测试执行

public class CommandPattern {public static void main(String[] args) {//命令接收者CourseVideo courseVideo = new CourseVideo("设计模式系列");//创建命令OpenCourseVideoCommand openCourseVideoCommand = new OpenCourseVideoCommand(courseVideo);CloseCourseVideoCommand closeCourseVideoCommand = new CloseCourseVideoCommand(courseVideo);//创建执行人User user = new User();//添加命令user.addCommand(openCourseVideoCommand);user.addCommand(closeCourseVideoCommand);//执行user.executeCommands();}
}

输出结果

设计模式系列课程视频开放。
设计模式系列课程视频关闭。

优点

  1. 降低系统的耦合度。命令模式能将调用操作的对象与实现该操作的对象解耦。
  2. 增加或删除命令非常方便。采用命令模式增加与删除命令不会影响其他类,它满足“开闭原则”,对扩展比较灵活。
  3. 可以实现宏命令。命令模式可以与组合模式结合,将多个命令装配成一个组合命令,即宏命令。
  4. 方便实现 Undo 和 Redo 操作。命令模式可以与后面介绍的备忘录模式结合,实现命令的撤销与恢复。

缺点

可能产生大量具体命令类。因为计对每一个具体操作都需要设计一个具体命令类,这将增加系统的复杂性。

应用场景

命令执行过程较为复杂且可能存在变化,需要对执行命令动作本身进行额外操作,此时可以考虑使用命令模式

命令模式的扩展

在软件开发中,有时将命令模式与组合模式联合使用,这就构成了宏命令模式,也叫组合命令模式。宏命令包含了一组命令,它充当了具体命令与调用者的双重角色,执行它时将递归调用它所包含的所有命令,其具体结构图如下:

模版实现如下

package com.niuh.designpattern.command.v2;import java.util.ArrayList;/*** <p>* 组合命令模式* </p>*/
public class CompositeCommandPattern {public static void main(String[] args) {AbstractCommand cmd1 = new ConcreteCommand1();AbstractCommand cmd2 = new ConcreteCommand2();CompositeInvoker ir = new CompositeInvoker();ir.add(cmd1);ir.add(cmd2);System.out.println("客户访问调用者的execute()方法...");ir.execute();}
}//抽象命令
interface AbstractCommand {public abstract void execute();
}//树叶构件: 具体命令1
class ConcreteCommand1 implements AbstractCommand {private CompositeReceiver receiver;ConcreteCommand1() {receiver = new CompositeReceiver();}public void execute() {receiver.action1();}
}//树叶构件: 具体命令2
class ConcreteCommand2 implements AbstractCommand {private CompositeReceiver receiver;ConcreteCommand2() {receiver = new CompositeReceiver();}public void execute() {receiver.action2();}
}//树枝构件: 调用者
class CompositeInvoker implements AbstractCommand {private ArrayList<AbstractCommand> children = new ArrayList<AbstractCommand>();public void add(AbstractCommand c) {children.add(c);}public void remove(AbstractCommand c) {children.remove(c);}public AbstractCommand getChild(int i) {return children.get(i);}public void execute() {for (Object obj : children) {((AbstractCommand) obj).execute();}}
}//接收者
class CompositeReceiver {public void action1() {System.out.println("接收者的action1()方法被调用...");}public void action2() {System.out.println("接收者的action2()方法被调用...");}
}

输出结果如下

客户访问调用者的execute()方法...
接收者的action1()方法被调用...
接收者的action2()方法被调用...

命令模式还可以同备忘录(Memento)模式组合使用,这样就变成了可撤销的命令模式

源码中的应用

  • java.util.Timer类中scheduleXXX()方法
  • java Concurrency Executor execute() 方法
  • java.lang.reflect.Method invoke()方法
  • org.springframework.jdbc.core.JdbcTemplate
  • ......

在 JdbcTemplate 中的应用

在JdbcTemplate中命令模式的使用并没有遵从标准的命令模式的使用,只是思想相同而已。

在 Spring 的 JdbcTemplate 这个类中有 query() 方法,query() 方法中定义了一个内部类 QueryStatementCallback,QueryStatementCallback 又实现了 StatementCallback 接口,另外还有其它类实现了该接口,StatementCallback 接口中又有一个抽象方法 doInStatement()。在 execute() 中又调用了 query()。

StatementCallback充当的是命令角色,JdbcTemplate即充当调用者角色,又充当接收者角色。上面的类图只是为了方便理解,实际上,QueryStatementCallback 与 ExecuteStatementCallback是JdbcTemplate中方法的内部类,具体看源码中的内容。

部分源码分析

StatementCallback接口:

public interface StatementCallback<T> {T doInStatement(Statement stmt) throws SQLException, DataAccessException;
}

JdbcTemplate类:

public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {//相当于调用者发布的一个命令@Overridepublic <T> List<T> query(String sql, RowMapper<T> rowMapper) throws DataAccessException {return query(sql, new RowMapperResultSetExtractor<T>(rowMapper));}//命令发布后由具体的命令派给接收者进行执行@Overridepublic <T> T query(final String sql, final ResultSetExtractor<T> rse) throws DataAccessException {Assert.notNull(sql, "SQL must not be null");Assert.notNull(rse, "ResultSetExtractor must not be null");if (logger.isDebugEnabled()) {logger.debug("Executing SQL query [" + sql + "]");}//内部类,实现StatementCallback,相当于具体的命令class QueryStatementCallback implements StatementCallback<T>, SqlProvider {@Overridepublic T doInStatement(Statement stmt) throws SQLException {ResultSet rs = null;try {rs = stmt.executeQuery(sql);ResultSet rsToUse = rs;if (nativeJdbcExtractor != null) {rsToUse = nativeJdbcExtractor.getNativeResultSet(rs);}return rse.extractData(rsToUse);}finally {JdbcUtils.closeResultSet(rs);}}@Overridepublic String getSql() {return sql;}}return execute(new QueryStatementCallback());}//相当于接收者,命令真正的执行者@Overridepublic <T> T execute(StatementCallback<T> action) throws DataAccessException {Assert.notNull(action, "Callback object must not be null");Connection con = DataSourceUtils.getConnection(getDataSource());Statement stmt = null;try {Connection conToUse = con;if (this.nativeJdbcExtractor != null &&this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativeStatements()) {conToUse = this.nativeJdbcExtractor.getNativeConnection(con);}stmt = conToUse.createStatement();applyStatementSettings(stmt);Statement stmtToUse = stmt;if (this.nativeJdbcExtractor != null) {stmtToUse = this.nativeJdbcExtractor.getNativeStatement(stmt);}T result = action.doInStatement(stmtToUse);handleWarnings(stmt);return result;}catch (SQLException ex) {// Release Connection early, to avoid potential connection pool deadlock// in the case when the exception translator hasn't been initialized yet.JdbcUtils.closeStatement(stmt);stmt = null;DataSourceUtils.releaseConnection(con, getDataSource());con = null;throw getExceptionTranslator().translate("StatementCallback", getSql(action), ex);}finally {JdbcUtils.closeStatement(stmt);DataSourceUtils.releaseConnection(con, getDataSource());}}
}

PS:以上代码提交在 Githubhttps://github.com/Niuh-Study/niuh-designpatterns.git

文章持续更新,可以公众号搜一搜「 一角钱技术 」第一时间阅读, 本文 GitHub org_hejianhui/JavaStudy 已经收录,欢迎 Star。

plsql执行command命令控制台出现乱码_设计模式系列 — 命令模式相关推荐

  1. plsql执行command命令控制台出现乱码_展现“管理员”的神级能力,有趣又实用的我的世界基础命令...

    在上一篇文章<Minecraft基岩版(手机版.便携版.PE版)开服指南>中,我们介绍了<我的世界>基岩版的开服方法.然后这篇文章,就为那些了解更多细节的玩家,介绍一下常见的管 ...

  2. 策略模式和工厂模式的区别_设计模式系列 — 策略模式

    点赞再看,养成习惯,公众号搜一搜[一角钱技术]关注更多原创技术文章. 本文 GitHub org_hejianhui/JavaStudy 已收录,有我的系列文章. 前言 23种设计模式速记 单例(si ...

  3. openfire运行ant命令控制台中文乱码问题

    最近项目要使用openfire来进行插件开发,下载openfire源码导入工程后,运行源码的build目录下的build.xml中的ant命令,其中run命令是启动openfire服务器,结果控制台输 ...

  4. cmd传入命令行打开程序_打开cmd命令行 - 卡饭网

    win7如何打开cmd命令提示符的方法 win7如何打开cmd命令提示符的方法 win7如何打开cmd? 有两种打开cmd命令行窗口的操作方法: 第一,单击"开始"菜单按钮,然后在 ...

  5. 命令创建mysql数据库_怎么在命令行创建一个mysql数据库

    哆啦的时光机 第一步:登陆到MySQL服务器 首先,你需要使用root用户登陆进你的MySQL数据库,如下: $ mysql -u root -h -p 请注意:为了能登进远程的MySQL服务器,你需 ...

  6. cmd命令行mysql乱码解决方法_mysql命令行显示乱码的解决方法

    mysql命令行显示乱码的解决方法:1.打开my.ini配置文件:2.修改配置[default-character-set=utf8]:3.改变数据库的编码方式,如[set character_set ...

  7. java 命令 乱码_解决java 命令行乱码的问题

    虚拟机参数加上 -dfile.encoding=gbk -ddefault.client.encoding=gbk -duser.language=zh -duser.region=cn 补充:jav ...

  8. npm命令运行时报错_npm命令运行时报错_使用npm命令时报错误Error: EACCES: permission denied...

    执行npm link 报错 liuffsunny@liufangfangdeMacBook-Pro 02learnCli % npm link npm ERR! code EACCES npm ERR ...

  9. adb命令查看手机电量_使用adb命令查看电池电量信息

    1. 电池信息 1.1 获取手机电池信息 adb命令:adb shell dumpsys battery 得到信息如下: 1 AC powered: false 2 USB powered: true ...

最新文章

  1. linux 大量的TIME_WAIT解决办法
  2. JedisConnectionException: java.net.SocketException: Broken pipe
  3. 按职称分类统计人数access_建设工程监理从业人员超120万!2019年统计公报发布了!...
  4. spring框架搭建第一天
  5. 【2018.5.12】模拟赛之四-ssl2416 条形图【高精度,dp】
  6. java学习(68):局部内部类
  7. 编写的windows程序,崩溃时产生crash dump文件的办法
  8. Chrome 好玩的插件
  9. 洛克人红色思考型机器人叫什么_如何让机器人“好好说话”?
  10. Spring.NET学习笔记——目录(原)
  11. 其六,函数之一Function
  12. Linux系统重要快捷键 Shell 常用通配符
  13. Atitit 物化视图与触发器性能测试方法 attilax总结 1.1. 触发器主要影响更新性能。。。 1 1.2. 临时打开关闭触发器,如果db不支持可以更改条件使其不触发 1 1.3. 打开定时
  14. PHP CI框架学习
  15. 贝叶斯网络分析软件Netica
  16. 1. 走进Java语言 —— Java SE
  17. IDEA学习篇——finds duplicated code提示
  18. 2058:简单计算器
  19. 迷你股票行情,时刻给你最新行情。
  20. Phpcms黄页yp如何添加企业模板

热门文章

  1. 马斯克蝉联美国CEO薪酬榜No.1,年入41亿,库克皮猜纳德拉加起来都没他高
  2. ECCV2020|超快的车道线检测,代码模型已开源
  3. 北大成功研发 “忘情水” ,可精准删除特定记忆,有望今年进行人体测试
  4. 【每日一算法】求众数
  5. pandas数据清洗(缺失值、异常值和重复值处理)
  6. python浪漫表白代码
  7. python输入input的用法
  8. 基于OpenCV的显著图绘制
  9. 【OpenCV 4开发详解】图像修复
  10. python02-条件语句到循环语句