目錄
- 第一章 命令模式介紹
- 第二章 命令模式實作
- 2.1、抽象命令類
- 2.2、具體命令類
- 2.3、命令接收者
- 2.4、命令請求者
- 2.5、最終測驗類
- 第三章 命令模式應用
專案地址:https://gitee.com/caochenlei/design-pattern
第一章 命令模式介紹
命令模式的介紹:
在軟體開發系統中,“方法的請求者”與“方法的實作者”之間經常存在緊密的耦合關系,這不利于軟體功能的擴展與維護,例如,想對方法進行“撤銷、重做、記錄”等處理都很不方便,因此“如何將方法的請求者與實作者解耦”變得很重要,命令模式就能很好地解決這個問題,
在現實生活中,命令模式的例子也很多,比如看電視時,我們只需要輕輕一按遙控器就能完成頻道的切換,這就是命令模式,將換臺請求和換臺處理完全解耦了,電視機遙控器(命令發送者)通過按鈕(具體命令)來遙控電視機(命令接收者),
命令模式(Command Pattern)是一種資料驅動的設計模式,它屬于行為型模式,請求以命令的形式包裹在物件中,并傳給呼叫物件,呼叫物件尋找可以處理該命令的合適的物件,并把該命令傳給相應的物件,該物件執行命令,
命令模式的優點:
- 通過引入中間件(抽象介面)降低系統的耦合度,
- 擴展性良好,增加或洗掉命令非常方便,采用命令模式增加與洗掉命令不會影響其他類,且滿足“開閉原則”,
- 可以實作宏命令,命令模式可以與組合模式結合,將多個命令裝配成一個組合命令,即宏命令,
- 方便實作 Undo 和 Redo 操作,命令模式可以與后面介紹的備忘錄模式結合,實作命令的撤銷與恢復,
- 可以在現有命令的基礎上,增加額外功能,比如日志記錄,結合裝飾器模式會更加靈活,
命令模式的缺點:
- 可能產生大量具體的命令類,因為每一個具體操作都需要設計一個具體命令類,這會增加系統的復雜性,
- 命令模式的結果其實就是接收方的執行結果,但是為了以命令的形式進行架構、解耦請求與實作,引入了額外型別結構(引入了請求方與抽象命令介面),增加了理解上的困難,不過這也是設計模式的通病,抽象必然會額外增加類的數量,代碼抽離肯定比代碼聚合更加難理解,
命令模式的場景:
- 請求呼叫者需要與請求接收者解耦時,命令模式可以使呼叫者和接收者不直接互動,
- 系統隨機請求命令或經常增加、洗掉命令時,命令模式可以方便地實作這些功能,
- 當系統需要執行一組操作時,命令模式可以定義宏命令來實作該功能,
- 當系統需要支持命令的撤銷(Undo)操作和恢復(Redo)操作時,可以將命令物件存盤起來,采用備忘錄模式來實作,
命令模式的角色:
- 抽象命令類(Command)角色:宣告執行命令的介面,擁有執行命令的抽象方法 execute(),
- 具體命令類(Concrete Command)角色:是抽象命令類的具體實作類,它擁有接收者物件,并通過呼叫接收者的功能來完成命令要執行的操作,
- 實作者/接收者(Receiver)角色:執行命令功能的相關操作,是具體命令物件業務的真正實作者,
- 呼叫者/請求者(Invoker)角色:是請求的發送者,它通常擁有很多的命令物件,并通過訪問命令物件來執行相關請求,它不直接訪問接收者,

第二章 命令模式實作
2.1、抽象命令類
Command
public interface Command {
//執行動作(操作)
public void execute();
//撤銷動作(操作)
public void undo();
}
2.2、具體命令類
NoCommand
public class NoCommand implements Command {
@Override
public void execute() {
}
@Override
public void undo() {
}
}
TVOnCommand
public class TVOnCommand implements Command {
TVReceiver tv;
public TVOnCommand(TVReceiver tv) {
this.tv = tv;
}
@Override
public void execute() {
tv.on();
}
@Override
public void undo() {
tv.off();
}
}
TVOffCommand
public class TVOffCommand implements Command {
TVReceiver tv;
public TVOffCommand(TVReceiver tv) {
this.tv = tv;
}
@Override
public void execute() {
tv.off();
}
@Override
public void undo() {
tv.on();
}
}
2.3、命令接收者
TVReceiver
public class TVReceiver {
public void on() {
System.out.println(" 電視機打開了... ");
}
public void off() {
System.out.println(" 電視機關閉了... ");
}
}
2.4、命令請求者
RemoteController
public class RemoteController {
Command[] onCommands; //開命令集合
Command[] offCommands; //關命令集合
Command undoCommand; //撤銷命令
public RemoteController() {
onCommands = new Command[5];
offCommands = new Command[5];
for (int i = 0; i < 5; i++) {
onCommands[i] = new NoCommand();
offCommands[i] = new NoCommand();
}
}
//設定命令按鈕
public void setCommand(int no, Command onCommand, Command offCommand) {
onCommands[no] = onCommand;
offCommands[no] = offCommand;
}
//按下開按鈕
public void onButtonWasPushed(int no) {
//找到你按下的開的按鈕,并呼叫對應方法
onCommands[no].execute();
//記錄這次的操作,用于撤銷
undoCommand = onCommands[no];
}
//按下關按鈕
public void offButtonWasPushed(int no) {
//找到你按下的關的按鈕,并呼叫對應方法
offCommands[no].execute();
//記錄這次的操作,用于撤銷
undoCommand = offCommands[no];
}
//按下撤銷按鈕
public void undoButtonWasPushed() {
undoCommand.undo();
}
}
2.5、最終測驗類
Client
public class Client {
public static void main(String[] args) {
//創建遠程遙控器
RemoteController remoteController = new RemoteController();
//創建電視機接收者
TVReceiver tvReceiver = new TVReceiver();
TVOffCommand tvOffCommand = new TVOffCommand(tvReceiver);
TVOnCommand tvOnCommand = new TVOnCommand(tvReceiver);
remoteController.setCommand(0, tvOnCommand, tvOffCommand);
//呼叫電視機的操作
remoteController.onButtonWasPushed(0);
remoteController.offButtonWasPushed(0);
remoteController.undoButtonWasPushed();
}
}
電視機打開了...
電視機關閉了...
電視機打開了...
第三章 命令模式應用
Spring 框架的 JdbcTemplate 就使用到了命令模式,不過并不是命令模式的標準實作,只是用到了命令模式的思想,
StatementCallback充當了抽象命令類,
@FunctionalInterface
public interface StatementCallback<T> {
@Nullable
T doInStatement(Statement stmt) throws SQLException, DataAccessException;
}
以下圖片中是實作了StatementCallback的所有具體命令類,

這里QueryStatementCallback使用到了匿名內部類而不是單獨寫成一個類檔案,它實作了命令介面, 同時也充當命令接收者,
@Nullable
public <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 (this.logger.isDebugEnabled()) {
this.logger.debug("Executing SQL query [" + sql + "]");
}
class QueryStatementCallback implements StatementCallback<T>, SqlProvider {
QueryStatementCallback() {}
@Nullable
public T doInStatement(Statement stmt) throws SQLException {
ResultSet rs = null;
Object var3;
try {
rs = stmt.executeQuery(sql);
var3 = rse.extractData(rs);
} finally {
JdbcUtils.closeResultSet(rs);
}
return var3;
}
public String getSql() {
return sql;
}
}
return this.execute(new QueryStatementCallback(), true);
}
命令呼叫者是 JdbcTemplate,其中 execute(StatementCallback<T> action, boolean closeResources) 方法中,呼叫 action.doInStatement(stmt) 方法,不同的實作 StatementCallback 介面的物件,對應不同的 doInStatemnt 實作邏輯,
@Nullable
private <T> T execute(StatementCallback<T> action, boolean closeResources) throws DataAccessException {
Assert.notNull(action, "Callback object must not be null");
Connection con = DataSourceUtils.getConnection(this.obtainDataSource());
Statement stmt = null;
Object var12;
try {
stmt = con.createStatement();
this.applyStatementSettings(stmt);
T result = action.doInStatement(stmt);
this.handleWarnings(stmt);
var12 = result;
} catch (SQLException var10) {
String sql = getSql(action);
JdbcUtils.closeStatement(stmt);
stmt = null;
DataSourceUtils.releaseConnection(con, this.getDataSource());
con = null;
throw this.translateException("StatementCallback", sql, var10);
} finally {
if (closeResources) {
JdbcUtils.closeStatement(stmt);
DataSourceUtils.releaseConnection(con, this.getDataSource());
}
}
return var12;
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/275040.html
標籤:其他
