圖解Java設計模式之命令模式

来源:https://www.cnblogs.com/haizai/archive/2020/03/28/12588062.html
-Advertisement-
Play Games

圖解Java設計模式之命令模式 智能生活項目需求 命令模式基本介紹 命令模式的原理類圖 命令模式在Spring框架中JdbcTemplate應用源碼分析 命令模式的註意事項和細節 智能生活項目需求 看一個具體的需求 我們買類一套智能家電,有照明燈、風扇、冰箱、洗衣機,我們只要在手機上安裝app就可以 ...


圖解Java設計模式之命令模式

 

智能生活項目需求

看一個具體的需求
在這裡插入圖片描述

  1. 我們買類一套智能家電,有照明燈、風扇、冰箱、洗衣機,我們只要在手機上安裝app就可以控制對這些家電工作。
  2. 這些智能家電來自不同的廠家,我們不想針對每一種家電都安裝一個App,分別控制,我們希望只要一個App就可以控制全部智能家電。
  3. 要實現一個app控制所有智能家電的需要,則每個智能家電廠家都要提供一個統一的介面給app調用,這時就可以考慮使用命令模式。
  4. 命令模式可將“動作的請求者”從“動作的執行者”對象中解耦出來。
  5. 在我們的例子中,動作的請求者是手機app,動作的執行者是每個廠商的一個家電產品。

命令模式基本介紹

  1. 命令模式(Command Pattern):在軟體設計中,我們經常需要向某些對象發送請求,但是並不知道請求的接收者是誰,也不知道被請求的操作是那個,我們只需在程式運行時具體的請求接收者即可,此時,可以使用命令模式來進行設計
  2. 命令模式使得請求發送者與請求接收者消除彼此之間的耦合,讓對象之間的調用關係更加靈活,實現解耦。
  3. 在命令模式中,會將一個請求封裝為一個對象,以便使用不同參數來表示不同的請求(即命名),同時命令模式也支持可撤銷的操作。
  4. 通俗易懂的理解 :將軍發佈命令,士兵去執行。其中有幾個角色 :將軍(命令發佈者)、士兵(命令的具體執行者)、命令(連接將軍和士兵)。
    Invoker 是調用者(將軍),Receiver 是被調用者(士兵),MyCommand 是命令,實現來Command介面,持有接收對象

命令模式的原理類圖

在這裡插入圖片描述
對原理類圖的說明 :
1)Invoker 是調用者角色
2)Command :是命令角色,需要執行的所有命令都在這裡,可以是或抽象類
3)Receiver :接受者角色,知道如何實施和執行一個請求相關的操作
4)ConcreteCommand :將一個接受者對象與一個動作綁定,調用接受者相應的操作,實現execute

package com.example.demo.command;

/**
 * 創建命令介面
 * @author zhaozhaohai
 *
 */
public interface Command {
	
	/**
	 * 執行動作(操作)
	 */
	public void execute();

	/**
	 * 撤銷動作(操作)
	 */
	public void undo();
}
package com.example.demo.command;

public class LightOffCommand implements Command{
	
	/**
	 * 聚合LightReceiver
	 */
	private LightReceiver LightReceiver;
	
	public LightOffCommand(LightReceiver lightReceiver) {
		this.LightReceiver = lightReceiver;
	}

	@Override
	public void execute() {
		// TODO Auto-generated method stub
		LightReceiver.off();
	}

	@Override
	public void undo() {
		// TODO Auto-generated method stub
		LightReceiver.on();
	}

}
package com.example.demo.command;

public class LightOnCommand implements Command{

	/**
	 * 聚合LightReceiver
	 */
	private LightReceiver LightReceiver;
	
	public LightOnCommand(LightReceiver lightReceiver) {
		this.LightReceiver = lightReceiver;
	}

	@Override
	public void execute() {
		// TODO Auto-generated method stub
		// 調用接收者方法
		LightReceiver.on();
	}

	@Override
	public void undo() {
		// TODO Auto-generated method stub
		// 調用接收者方法
		LightReceiver.off();
	}

}
package com.example.demo.command;

public class LightReceiver {

	public void on() {
		System.out.println(" 電燈打開了。。。 ");
	}
	
	public void off() {
		System.out.println(" 電燈關閉了。。。 ");
	}
}
package com.example.demo.command;

/**
 * 沒有任何命令,即空執行 :用於初始化每個按鈕,當調用空命令時,對象什麼都不做其實,這樣是一種設計模式,可以省掉對空判斷
 * @author zhaozhaohai
 *
 */
public class NoCommand implements Command{

	@Override
	public void execute() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void undo() {
		// TODO Auto-generated method stub
		
	}

}
package com.example.demo.command;

public class RemoteController {
	
	// 開 按鈕的命令數組
	private Command[] onCommands;

	private Command[] offCommands;
	
	// 執行撤銷的命令
	private 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();
		}
	}
	
	/**
	 * 給我們的按鈕設置你需要的命令
	 * @param no
	 * @param onCommand
	 * @param offCommand
	 */
	public void setCommand(int no, Command onCommand, Command offCommand) {
		onCommands[no] = onCommand;
		offCommands[no] = offCommand;
	}
	
	/**
	 * 按下開按鈕
	 * @param no
	 */
	public void onButtonWasPushed(int no) {
		// 找到你按下的開的按鈕,並調用對應的方法
		onCommands[no].execute();
		// 記錄這次的操作,用於撤銷
		undoCommand = onCommands[no];
	}
	
	/**
	 * 按下關的按鈕
	 * @param no
	 */
	public void offButtonWasPushed(int no) {
		// 找到你按下的關的按鈕,並調用對應方法
		offCommands[no].execute();
		// 記錄這次的操作,用於撤銷
		undoCommand = offCommands[no];
	}
	
	/**
	 * 按下撤銷按鈕
	 */
	public void undoButtonWasPushed() {
		undoCommand.undo();
	}
	
}
package com.example.demo.command;

public class TVOffCommand implements Command{
	/**
	 * 聚合TVReceiver
	 */
	private TVReceiver tvReceiver;
	
	public TVOffCommand(TVReceiver tvReceiver) {
		this.tvReceiver = tvReceiver;
	}

	@Override
	public void execute() {
		// TODO Auto-generated method stub
		tvReceiver.off();
	}

	@Override
	public void undo() {
		// TODO Auto-generated method stub
		tvReceiver.on();
	}
}
package com.example.demo.command;

public class TVOnCommand implements Command{
	/**
	 * 聚合TVReceiver
	 */
	private TVReceiver tvReceiver;
	
	public TVOnCommand(TVReceiver tvReceiver) {
		this.tvReceiver = tvReceiver;
	}

	@Override
	public void execute() {
		// TODO Auto-generated method stub
		tvReceiver.on();
	}

	@Override
	public void undo() {
		// TODO Auto-generated method stub
		tvReceiver.off();
	}
}
package com.example.demo.command;

public class TVReceiver {
	public void on() {
		System.out.println(" 電視機打開了。。。 ");
	}
	
	public void off() {
		System.out.println(" 電視機關閉了。。。 ");
	}
}
package com.example.demo.command;

public class Client {
	
	public static void main(String[] args) {
		// 使用命令設計模式,完成通過遙控器,對電燈的操作
		// 創建電燈的對象(接受者)
		LightReceiver lightReceiver = new LightReceiver();
		// 創建電燈相關的開關命令
		LightOnCommand lightOnCommand = new LightOnCommand(lightReceiver);
		LightOffCommand lightOffCommand = new LightOffCommand(lightReceiver);
		// 需要一個遙控器
		RemoteController remoteController = new RemoteController();
		// 給遙控器設置命令,比如 no = 0 是電燈的開和關的操作
		remoteController.setCommand(0, lightOnCommand, lightOffCommand);
		System.out.println("----------按下開的按鈕------------");
		remoteController.onButtonWasPushed(0);
		System.out.println("----------按下關的按鈕------------");
		remoteController.offButtonWasPushed(0);
		System.out.println("----------按下撤銷的按鈕------------");
		remoteController.undoButtonWasPushed();
		System.out.println("----------使用遙控器操作電視機-------");
		
		TVReceiver tvReceiver = new TVReceiver();
		TVOffCommand tvOffCommand = new TVOffCommand(tvReceiver);
		TVOnCommand tvOnCommand = new TVOnCommand(tvReceiver);
		// 給遙控器設置命令,比如 no = 1 是電視機的開和關的操作
		remoteController.setCommand(1, tvOnCommand, tvOffCommand);
		System.out.println("----------按下開的按鈕------------");
		remoteController.onButtonWasPushed(1);
		System.out.println("----------按下關的按鈕------------");
		remoteController.offButtonWasPushed(1);
		System.out.println("----------按下撤銷的按鈕------------");
		remoteController.undoButtonWasPushed();
	}

}

命令模式在Spring框架中JdbcTemplate應用源碼分析

在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述
說明 :

  1. StatementCallback 介面,類似命令介面(Command)
  2. class QueryStatementCallback implements StatementCallback, SqlProvider,匿名內部類,實現了命令介面,同時也充當命令接收者
  3. 命令調用者是JdbcTemplate,其中execute(StatementCallback action)方法中,調用action.doInStatement 方法,不同的實現StatementCallback介面的對象,對應不同的doInStatement實現邏輯
  4. 另外實現StatementCallback命令介面的子類還有QueryStatementCallback、BatchUpdateStatementCallback、ExecuteStatementCallback、UpdateStatementCallback

命令模式的註意事項和細節

1)將發起請求的對象與執行請求的對象解耦。發起請求的對象是調用者,調用者只要命令對象的execute()方法就可以讓接收者工作,而不必知道具體的接收者對象是誰、是如何實現的,命令對象會負責讓接收者執行請求的動作,也就是說 :“請求發起者”和“請求執行者”之間的解耦是通過命令對象實現的,命令對象起到了紐帶橋梁的作用。
2)容易設計一個命令隊列。只要把命令對象放到列隊,就可以多線程的執行命令。
3)容易實現對請求的撤銷和重做
4)命令模式不足 :可能導致某些系統有過多的具體命令類,增加類系統的複雜度,這點在使用的時候要註意
5)空命令也是一種設計模式,它為我們省去判空的操作。在上面的實例中,如果沒有空命令,每按下一個按鍵都要判空,這會給編碼帶來一定麻煩。
6)命令模式經典的應用場景 :界面的一個按鈕都是一條命令、模擬CMD(DOS命令)訂單的撤銷/恢復、觸發-反饋機制


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1.1 JavaScript簡史 在web日益流行的今天,人們對客戶端腳本語言的需求也越來越高。 1995.2 就職於網景公司的Brendan Eich發佈了其開發的LiveScript,改語言同時在瀏覽器和伺服器使用。為了趕工期,在Netscape Navigator 2發佈前夕,Netscape ...
  • HTML佈局標簽,定義文檔區塊,塊級(block-level);定義 span,用來組合文檔中的行內元素。雖然我們可以使用HTML table標簽來設計出漂亮的佈局,但是table標簽是不建議作為佈局工具使用的,設計表格的目的是呈現表格化數據 - 表格不是佈局工具! ...
  • html中的標簽元素大體被分為三種不同的類型:塊狀元素、內聯元素(又叫行內元素)和內聯塊狀元素。HTML網頁基本標簽的嵌套規則:1.塊元素可以嵌套行元素2.行元素可以嵌套行元素3.行元素不可以嵌套塊元素4.文字類塊元素不可以嵌套塊元素5.容器類塊元素可以嵌套塊元素 ...
  • <ol>定義有序列表;<ul>定義無序列表;<li>定義列表項,<dl>定義列表,<dt>自定義列表項目,<dd>定義自定義列表的描述;同時列表項內部可以使用段落、換行符、圖片、鏈接以及其他列表等等 ...
  • 路由原理 傳統開發方式 url改變後 立刻發起請求,響應整個頁面,渲染整個頁面 SPA 錨點值改變後 不會發起請求,發起ajax請求,局部改變頁面數據 頁面不跳轉 用戶體驗更好 SPA single page application(單頁應用程式) 前端路由 錨點值監視 ajax獲取動態數據 核心點 ...
  • 全局安裝/配置API更改 在Vue2.x中對全局屬性和全局API函數是這麼玩的 現在,讓我們看看它如何在Vue 3中運行: 您可能已經註意到,每個配置都限於使用定義的某個Vue應用程式createApp。 它可以使您的代碼更易於理解,並且不易出現由第三方插件引起的意外問題。當前,如果某些第三方解決方 ...
  • 關於HTML5 廣義上對HTML5前端的解釋是包含HTML、CSS和JavaScript在內的一套技術組合。隨著最近幾年來互聯網的迅猛發展和普及,HTML5前端工程師已經成為互聯網時代軟體產品研發中不可缺少的一種專業的研發角色,“錢”途不可限量! HTML5能做什麼? 第一:最近火爆的微信小程式,非 ...
  • 一、模板模式 1、模板模式(Template Method pattern):指定義一個演算法的骨架,並允許子類為一個或者多個步驟提供實現。模板方法使得子類可以在不改變演算法結構的情況下,重新定義演算法的某些步驟。(屬於行為型模式) 2、適用場景 一次性實現一個演算法的不變的部分,並將可變的行為留給子類來實 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...