命令设计模式示例
本文是我們名為“ Java設(shè)計模式 ”的學(xué)院課程的一部分。
在本課程中,您將深入研究大量的設(shè)計模式,并了解如何在Java中實現(xiàn)和利用它們。 您將了解模式如此重要的原因,并了解何時以及如何應(yīng)用模式中的每一個。 在這里查看 !
目錄
1.簡介 2.什么是命令設(shè)計模式 3.實施命令設(shè)計模式 4.何時使用命令設(shè)計模式 5. JDK中的命令設(shè)計模式 6.下載源代碼1.簡介
命令設(shè)計模式是一種行為設(shè)計模式,有助于將調(diào)用者與請求的接收者解耦。
為了理解命令設(shè)計模式,我們創(chuàng)建一個示例來執(zhí)行不同類型的作業(yè)。 作業(yè)可以是系統(tǒng)中的任何內(nèi)容,例如,發(fā)送電子郵件,SMS,日志記錄以及執(zhí)行某些IO功能。
命令模式將有助于將調(diào)用者與接收者解耦,并有助于執(zhí)行任何類型的作業(yè),而無需了解其實現(xiàn)。 讓我們通過創(chuàng)建線程來幫助并發(fā)執(zhí)行這些作業(yè),使該示例更加有趣。 由于這些作業(yè)彼此獨立,因此這些作業(yè)的執(zhí)行順序并不是很重要。 我們將創(chuàng)建一個線程池以限制執(zhí)行作業(yè)的線程數(shù)。 命令對象將封裝作業(yè),并將其從執(zhí)行作業(yè)的池中移交給線程。
在實施示例之前,讓我們進(jìn)一步了解命令設(shè)計模式。
2.什么是命令設(shè)計模式
命令設(shè)計模式的目的是將請求封裝為對象,從而使開發(fā)人員可以將具有不同請求,隊列或日志請求的客戶端參數(shù)化,并支持可撤銷的操作。
通常,面向?qū)ο蟮膽?yīng)用程序由一組交互對象組成,每個對象都提供有限的集中功能。 響應(yīng)于用戶交互,應(yīng)用程序執(zhí)行某種處理。 為此,應(yīng)用程序?qū)⒉煌瑢ο蟮姆?wù)用于處理需求。
在實現(xiàn)方面,應(yīng)用程序可能依賴于指定的對象,該對象通過將所需數(shù)據(jù)作為參數(shù)傳遞來調(diào)用這些對象上的方法。 這個指定的對象可以稱為調(diào)用程序,因為它調(diào)用不同對象上的操作。 調(diào)用方可以視為客戶端應(yīng)用程序的一部分。 實際包含用于提供請求處理所需服務(wù)的實現(xiàn)的對象集可以稱為Receiver對象。
使用“命令”模式,可以將代表客戶端發(fā)出請求的調(diào)用程序和一組服務(wù)呈現(xiàn)Receiver對象分離。 命令模式建議為響應(yīng)客戶請求而執(zhí)行的處理或要采取的動作創(chuàng)建一個抽象。 可以將這種抽象設(shè)計為聲明要由稱為Command對象的不同具體實現(xiàn)者實現(xiàn)的公共接口。 每個Command對象代表不同類型的客戶端請求和相應(yīng)的處理。
給定的Command對象負(fù)責(zé)提供處理它所代表的請求所需的功能,但不包含該功能的實際實現(xiàn)。 Command對象在提供此功能時利用了Receiver對象。
圖1 –命令模式類圖
命令
- 聲明用于執(zhí)行操作的接口。
具體命令
- 定義Receiver對象和操作之間的綁定。
- 通過在Receiver上調(diào)用相應(yīng)的操作來實現(xiàn)Execute 。
客戶
- 創(chuàng)建一個ConcreteCommand對象并設(shè)置其接收者。
召喚者
- 要求命令執(zhí)行請求。
接收者
- 知道如何執(zhí)行與執(zhí)行請求相關(guān)的操作。 任何類都可以充當(dāng)Receiver 。
3.實施命令設(shè)計模式
我們將使用命令對象來實現(xiàn)示例。 該命令對象將由一個公共接口引用,并將包含用于執(zhí)行請求的方法。 具體的命令類將覆蓋該方法,并將提供其自己的特定實現(xiàn)以執(zhí)行請求。
package com.javacodegeeks.patterns.commandpattern;public interface Job {public void run(); }Job接口是命令接口,包含單個方法run ,該方法由線程執(zhí)行。 我們命令的execute方法是run方法,該方法將由線程執(zhí)行以完成工作。
可以執(zhí)行其他類型的作業(yè)。 以下是不同的具體類,它們的實例將由不同的命令對象執(zhí)行。
package com.javacodegeeks.patterns.commandpattern;public class Email {public void sendEmail(){System.out.println("Sending email.......");} }package com.javacodegeeks.patterns.commandpattern;public class FileIO {public void execute(){System.out.println("Executing File IO operations...");} }package com.javacodegeeks.patterns.commandpattern;public class Logging {public void log(){System.out.println("Logging...");} }package com.javacodegeeks.patterns.commandpattern;public class Sms {public void sendSms(){System.out.println("Sending SMS...");} }以下是封裝上述類并實現(xiàn)Job接口的不同命令類。
package com.javacodegeeks.patterns.commandpattern;public class EmailJob implements Job{private Email email;public void setEmail(Email email){this.email = email;}@Overridepublic void run() {System.out.println("Job ID: "+Thread.currentThread().getId()+" executing email jobs.");if(email!=null){email.sendEmail();}try {Thread.sleep(1000);} catch (InterruptedException e) {Thread.currentThread().interrupt();}}}package com.javacodegeeks.patterns.commandpattern;public class FileIOJob implements Job{private FileIO fileIO;public void setFileIO(FileIO fileIO){this.fileIO = fileIO;}@Overridepublic void run() {System.out.println("Job ID: "+Thread.currentThread().getId()+" executing fileIO jobs.");if(fileIO!=null){fileIO.execute();}try {Thread.sleep(1000);} catch (InterruptedException e) {Thread.currentThread().interrupt();}} }package com.javacodegeeks.patterns.commandpattern;public class LoggingJob implements Job{private Logging logging;public void setLogging(Logging logging){this.logging = logging;}@Overridepublic void run() {System.out.println("Job ID: "+Thread.currentThread().getId()+" executing logging jobs.");if(logging!=null){logging.log();}try {Thread.sleep(1000);} catch (InterruptedException e) {Thread.currentThread().interrupt();}} }package com.javacodegeeks.patterns.commandpattern;public class SmsJob implements Job{private Sms sms;public void setSms(Sms sms) {this.sms = sms;}@Overridepublic void run() {System.out.println("Job ID: "+Thread.currentThread().getId()+" executing sms jobs.");if(sms!=null){sms.sendSms();}try {Thread.sleep(1000);} catch (InterruptedException e) {Thread.currentThread().interrupt();}}}上面的類對它們各自的類進(jìn)行引用,這些類將用于完成工作。 這些類將覆蓋run方法并執(zhí)行請求的工作。 例如, SmsJob類用于發(fā)送短信,其運行方法調(diào)用Sms對象的sendSms方法以完成工作。
您可以將一個不同的對象一一設(shè)置到同一command對象。
下面是ThreadPool類,該類用于創(chuàng)建線程池并允許線程從作業(yè)隊列中獲取并執(zhí)行作業(yè)。
package com.javacodegeeks.patterns.commandpattern;import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue;public class ThreadPool {private final BlockingQueue<Job> jobQueue;private final Thread[] jobThreads;private volatile boolean shutdown;public ThreadPool(int n){jobQueue = new LinkedBlockingQueue<>();jobThreads = new Thread[n];for (int i = 0; i < n; i++) {jobThreads[i] = new Worker("Pool Thread " + i);jobThreads[i].start();}}public void addJob(Job r){try {jobQueue.put(r);} catch (InterruptedException e) {Thread.currentThread().interrupt();}}public void shutdownPool(){while (!jobQueue.isEmpty()) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}shutdown = true;for (Thread workerThread : jobThreads) {workerThread.interrupt();}}private class Worker extends Thread{public Worker(String name){super(name);}public void run(){while (!shutdown) {try {Job r = jobQueue.take();r.run();} catch (InterruptedException e) {}}}}}上面的類用于創(chuàng)建n個線程(工作線程)。 每個工作線程將在隊列中等待作業(yè),然后執(zhí)行該作業(yè),并返回到等待狀態(tài)。 該類包含一個作業(yè)隊列; 當(dāng)將新作業(yè)添加到隊列中時,池中的工作線程將執(zhí)行該作業(yè)。
我們還包括一個shutdownPool方法,該方法僅在作業(yè)隊列為空時才通過中斷所有工作線程來關(guān)閉池。 addJob方法用于將作業(yè)添加到隊列。
現(xiàn)在,讓我們測試代碼。
package com.javacodegeeks.patterns.commandpattern;public class TestCommandPattern {public static void main(String[] args){init();}private static void init(){ThreadPool pool = new ThreadPool(10);Email email = null;EmailJob emailJob = new EmailJob();Sms sms = null;SmsJob smsJob = new SmsJob();FileIO fileIO = null;;FileIOJob fileIOJob = new FileIOJob();Logging logging = null;LoggingJob logJob = new LoggingJob();for (int i = 0; i < 5; i++) {email = new Email();emailJob.setEmail(email);sms = new Sms();smsJob.setSms(sms);fileIO = new FileIO();fileIOJob.setFileIO(fileIO);logging = new Logging();logJob.setLogging(logging);pool.addJob(emailJob);pool.addJob(smsJob);pool.addJob(fileIOJob);pool.addJob(logJob);}pool.shutdownPool();}}上面的代碼將導(dǎo)致以下輸出:
Job ID: 9 executing email jobs. Sending email....... Job ID: 12 executing logging jobs. Job ID: 17 executing email jobs. Sending email....... Job ID: 13 executing email jobs. Sending email....... Job ID: 10 executing sms jobs. Sending SMS... Job ID: 11 executing fileIO jobs. Executing File IO operations... Job ID: 18 executing sms jobs. Sending SMS... Logging... Job ID: 16 executing logging jobs. Logging... Job ID: 15 executing fileIO jobs. Executing File IO operations... Job ID: 14 executing sms jobs. Sending SMS... Job ID: 12 executing fileIO jobs. Executing File IO operations... Job ID: 10 executing logging jobs. Logging... Job ID: 18 executing email jobs. Sending email....... Job ID: 16 executing sms jobs. Sending SMS... Job ID: 14 executing fileIO jobs. Executing File IO operations... Job ID: 9 executing logging jobs. Logging... Job ID: 17 executing email jobs. Sending email....... Job ID: 13 executing sms jobs. Sending SMS... Job ID: 15 executing fileIO jobs. Executing File IO operations... Job ID: 11 executing logging jobs. Logging...請注意,后續(xù)執(zhí)行的輸出可能會有所不同。
在上面的類中,我們創(chuàng)建了一個具有10個線程的線程池。 然后,我們使用不同的作業(yè)設(shè)置不同的命令對象,并使用ThreadPool類的addJob方法將這些作業(yè)添加到隊列中。 作業(yè)插入隊列后,線程就會執(zhí)行該作業(yè)并將其從隊列中刪除。
我們設(shè)置了不同類型的作業(yè),但是通過使用命令設(shè)計模式,我們將作業(yè)與調(diào)用程序線程解耦。 線程將執(zhí)行實現(xiàn)Job接口的任何類型的對象。 不同的命令對象封裝了不同的對象,并在這些對象上執(zhí)行了請求的操作。
輸出顯示執(zhí)行不同作業(yè)的不同線程。 通過查看輸出中的作業(yè)ID,您可以清楚地看到單個線程正在執(zhí)行多個作業(yè)。 這是因為執(zhí)行作業(yè)后,線程將發(fā)送回池中。
命令設(shè)計模式的優(yōu)點是您可以添加更多不同種類的作業(yè),而無需更改現(xiàn)有類。 這樣可以提高靈活性和可維護(hù)性,并減少代碼中出現(xiàn)錯誤的機會。
4.何時使用命令設(shè)計模式
當(dāng)您要執(zhí)行以下操作時,請使用命令模式:
- 通過要執(zhí)行的操作對對象進(jìn)行參數(shù)化。
- 在不同的時間指定,排隊和執(zhí)行請求。 Command對象的生存期可以獨立于原始請求。 如果可以以與地址空間無關(guān)的方式表示請求的接收者,則可以將請求的命令對象傳輸?shù)狡渌M(jìn)程,并在那里執(zhí)行請求。
- 支持撤消。 命令的Execute操作可以在命令本身中存儲用于反轉(zhuǎn)其效果的狀態(tài)。 Command接口必須具有添加的Un-execute操作,該操作可以逆轉(zhuǎn)先前對Execute的調(diào)用的效果。 執(zhí)行的命令存儲在歷史列表中。 無限級撤消和重做通過分別向后和向前遍歷此列表來實現(xiàn),分別調(diào)用Un-execute和Execute 。
- 支持日志記錄更改,以便在系統(tǒng)崩潰時可以重新應(yīng)用它們。 通過使用加載和存儲操作擴展Command界面,您可以保留更改的持久日志。 從崩潰中恢復(fù)涉及從磁盤重新加載記錄的命令,并使用Execute操作重新執(zhí)行它們。
- 圍繞基于原始操作的高級操作構(gòu)建系統(tǒng)。 這種結(jié)構(gòu)在支持交易的信息系統(tǒng)中很常見。 事務(wù)封裝了一組數(shù)據(jù)更改。 命令模式提供了一種對事務(wù)進(jìn)行建模的方法。 命令具有一個公共接口,可讓您以相同的方式調(diào)用所有事務(wù)。 該模式還使通過新事務(wù)輕松擴展系統(tǒng)成為可能。
5. JDK中的命令設(shè)計模式
- java.lang.Runnable
- javax.swing.Action
6.下載源代碼
這是有關(guān)命令設(shè)計模式的課程。 您可以在此處下載源代碼: CommandPattern-Project
翻譯自: https://www.javacodegeeks.com/2015/09/command-design-pattern.html
總結(jié)
- 上一篇: gopro5帧数设置(gopro5拍照参
- 下一篇: 设置wps文本框上下居中(wps中,设置