天天看點

設計模式(14)--Command(指令模式)--行為型

指令模式屬于對象的行為模式。指令模式又稱為行動(Action)模式或交易(Transaction)模式。

作者QQ:1095737364    QQ群:123300273     歡迎加入!

1.模式定義:

    指令模式屬于對象的行為模式。指令模式又稱為行動(Action)模式或交易(Transaction)模式。

    指令模式把一個請求或者操作封裝到一個對象中。指令模式允許系統使用不同的請求把用戶端參數化,對請求排隊或者記錄請求日志,可以提供指令的撤銷和恢複功能。

2.模式特點:

      在軟體設計中,我們經常需要向某些對象發送請求,但是并不知道請求的接收者是誰,也不知道被請求的操作是哪個,我們隻需在程式運作時指定具體的請求接收者即可,此時,可以使用指令模式來進行設計,使得請求發送者與請求接收者消除彼此之間的耦合,讓對象之間的調用關系更加靈活。

  (1)指令模式的本質是對指令進行封裝,将發出指令的責任和執行指令的責任分割開。

  (2)每一個指令都是一個操作:請求的一方送出請求,要求執行一個操作;接收的一方收到請求,并執行操作

  (3)指令模式允許請求的一方和接收的一方獨立開來,使得請求的一方不必知道接收請求的一方的接口,更不必知道請求是怎麼被接收,以及操作是否被執行、何時被執行,以及是怎麼被執行的。

  (4)指令模式使請求本身成為一個對象,這個對象和其他對象一樣可以被存儲和傳遞。

  (5)指令模式的關鍵在于引入了抽象指令接口,且發送者針對抽象指令接口程式設計,隻有實作了抽象指令接口的具體指令才能與接收者相關聯。 

3.使用場景:

    認為是指令的地方都可以使用指令模式,比如: 1、GUI 中每一個按鈕都是一條指令。 2、模拟 CMD。

   (1)希望将行為請求者和行為實作者解耦,不直接打交道;

   (2)希望分離掉行為請求者一部分的責任,行為請求者隻需要将指令發給調用者,不再主動的去讓行為實作者産生行為,符合單一職責原則;

   (3)希望可以控制執行的指令清單,友善記錄,撤銷/重做以及事務等功能;

   (4)期待可以将請求排隊,有序執行;

   (5)希望可以将請求組合使用,即支援宏指令;

4.模式實作:

    指令模式是對指令的封裝。指令模式把發出指令的責任和執行指令的責任分割開,委派給不同的對象。

   每一個指令都是一個操作:請求的一方送出請求要求執行一個操作;接收的一方收到請求,并執行操作。指令模式允許請求的一方和接收的一方獨立開來,使得請求的一方不必知道接收請求的一方的接口,更不必知道請求是怎麼被接收,以及操作是否被執行、何時被執行,以及是怎麼被執行的。

下面以一個示意性的系統,說明指令模式的結構:

設計模式(14)--Command(指令模式)--行為型

指令模式涉及到五個角色,它們分别是:

  (1)用戶端(Client)角色:

  建立一個具體指令(ConcreteCommand)對象并确定其接收者。

public class Client {
    public static void main(String[] args) {
        //建立接收者
        Receiver receiver = new Receiver();
        //建立指令對象,設定它的接收者
        Command command = new ConcreteCommand(receiver);
        //建立請求者,把指令對象設定進去
        Invoker invoker = new Invoker(command);
        //執行方法
        invoker.action();
    }
}      

  (2)指令(Command)角色:

  聲明了一個給所有具體指令類的抽象接口。

public interface Command {
    /**
     * 執行方法
     */
    void execute();
}      

  (3)具體指令(ConcreteCommand)角色:

  定義一個接收者和行為之間的弱耦合;實作execute()方法,負責調用接收者的相應操作。execute()方法通常叫做執行方法。

public class ConcreteCommand implements Command {
    //持有相應的接收者對象
    private Receiver receiver = null;
    /**
     * 構造方法
     */
    public ConcreteCommand(Receiver receiver){
        this.receiver = receiver;
    }
    @Override
    public void execute() {
        //通常會轉調接收者對象的相應方法,讓接收者來真正執行功能
        receiver.action();
    }
}      

  (4)請求者(Invoker)角色:

  負責調用指令對象執行請求,相關的方法叫做行動方法。

public class Invoker {
    /**
     * 持有指令對象
     */
    private Command command = null;
    /**
     * 構造方法
     */
    public Invoker(Command command){
        this.command = command;
    }
    /**
     * 行動方法
     */
    public void action(){    
        command.execute();
    }
}      

  (5)接收者(Receiver)角色:

  負責具體實施和執行一個請求。任何一個類都可以成為接收者,實施和執行請求的方法叫做行動方法。

public class Receiver {
    /**
     * 真正執行指令相應的操作
     */
    public void action(){
        System.out.println("執行操作");
    }
}      

AudioPlayer系統

    小女孩茱麗(Julia)有一個盒式錄音機,此錄音機有播音(Play)、倒帶(Rewind)和停止(Stop)功能,錄音機的鍵盤便是請求者(Invoker)角色;茱麗(Julia)是用戶端角色,而錄音機便是接收者角色。Command類扮演抽象指令角色,而PlayCommand、StopCommand和RewindCommand便是具體指令類。茱麗(Julia)不需要知道播音(play)、倒帶(rewind)和停止(stop)功能是怎麼具體執行的,這些指令執行的細節全都由鍵盤(Keypad)具體實施。茱麗(Julia)隻需要在鍵盤上按下相應的鍵便可以了。

    錄音機是典型的指令模式。錄音機按鍵把用戶端與錄音機的操作細節分割開來。

設計模式(14)--Command(指令模式)--行為型

  (1)接收者角色,由錄音機類扮演

public class AudioPlayer {    
    public void play(){
        System.out.println("播放...");
    }    
    public void rewind(){
        System.out.println("倒帶...");
    }    
    public void stop(){
        System.out.println("停止...");
    }
}      

  (2)抽象指令角色類

public interface Command {
    /**
     * 執行方法
     */
    public void execute();
}      

  (3)具體指令角色類

public class PlayCommand implements Command {
    private AudioPlayer myAudio;    
    public PlayCommand(AudioPlayer audioPlayer){
        myAudio = audioPlayer;
    }
    /**
     * 執行方法
     */
    @Override
    public void execute() {
        myAudio.play();
    }
}
public class RewindCommand implements Command {
    private AudioPlayer myAudio;
    
    public RewindCommand(AudioPlayer audioPlayer){
        myAudio = audioPlayer;
    }
    @Override
    public void execute() {
        myAudio.rewind();
    }
}
public class StopCommand implements Command {
    private AudioPlayer myAudio;    
    public StopCommand(AudioPlayer audioPlayer){
        myAudio = audioPlayer;
    }
    @Override
    public void execute() {
        myAudio.stop();
    }
}      

  (4)請求者角色,由鍵盤類扮演

public class Keypad {
    private Command playCommand;
    private Command rewindCommand;
    private Command stopCommand;    
    public void setPlayCommand(Command playCommand) {
        this.playCommand = playCommand;
    }
    public void setRewindCommand(Command rewindCommand) {
        this.rewindCommand = rewindCommand;
    }
    public void setStopCommand(Command stopCommand) {
        this.stopCommand = stopCommand;
    }
    /**
     * 執行播放方法
     */
    public void play(){
        playCommand.execute();
    }
    /**
     * 執行倒帶方法
     */
    public void rewind(){
        rewindCommand.execute();
    }
    /**
     * 執行播放方法
     */
    public void stop(){
        stopCommand.execute();
    }
}      

  (5)用戶端角色,由茱麗小女孩扮演

public class Julia {
    public static void main(String[]args){
        //建立接收者對象
        AudioPlayer audioPlayer = new AudioPlayer();
        //建立指令對象
        Command playCommand = new PlayCommand(audioPlayer);
        Command rewindCommand = new RewindCommand(audioPlayer);
        Command stopCommand = new StopCommand(audioPlayer);
        //建立請求者對象
        Keypad keypad = new Keypad();
        keypad.setPlayCommand(playCommand);
        keypad.setRewindCommand(rewindCommand);
        keypad.setStopCommand(stopCommand);
        //測試
        keypad.play();
        keypad.rewind();
        keypad.stop();
        keypad.play();
        keypad.stop();
    }
}      

運作結果如下:

設計模式(14)--Command(指令模式)--行為型

5.優缺點:

  (1)指令模式的優點:

  [1]指令模式使新的指令很容易地被加入到系統裡。

  [2]允許接收請求的一方決定是否要否決請求。

  [3]能較容易地設計一個指令隊列。

  [4]可以容易地實作對請求的撤銷和恢複。

  [5]在需要的情況下,可以較容易地将指令記入日志。

  [6]将行為調用者和各種行為分隔開,降低程式的耦合,便于程式擴充;

  [7]将行為的具體實作封裝起來,用戶端無需關心行為的具體實作;

  [9]可為多種行為提供統一的調用入口,便于程式對行為的管理和控制;

  (2)指令模式的缺點:

    [1]使用指令模式的話,不用管指令多簡單,都需要寫一個指令類來封裝,使用指令模式可能會導緻系統有過多的具體指令類;

    [2]使用指令模式可能會導緻某些系統有過多的具體指令類。

    [3]使用指令模式可能會導緻某些系統有過多的具體指令類。因為針對每一個指令都需要設計一個具體指令類,是以某些系統可能需要大量具體指令類,這将影響指令模式的使用。

6.宏指令

  所謂宏指令簡單點說就是包含多個指令的指令,是一個指令的組合。

  設想茱麗的錄音機有一個記錄功能,可以把一個一個的指令記錄下來,再在任何需要的時候重新把這些記錄下來的指令一次性執行,這就是所謂的宏指令集功能。是以,茱麗的錄音機系統現在有四個鍵,分别為播音、倒帶、停止和宏指令功能。此時系統的設計與前面的設計相比有所增強,主要展現在Julia類現在有了一個新方法,用以操作宏指令鍵。

設計模式(14)--Command(指令模式)--行為型

  (1)系統需要一個代表宏指令的接口,以定義出具體宏指令所需要的接口。

public interface MacroCommand extends Command {
    /**
     * 宏指令聚集的管理方法
     * 可以添加一個成員指令
     */
    public void add(Command cmd);
    /**
     * 宏指令聚集的管理方法
     * 可以删除一個成員指令
     */
    public void remove(Command cmd);
}      

  (2)具體的宏指令MacroAudioCommand類負責把個别的指令合成宏指令。

public class MacroAudioCommand implements MacroCommand {
    private List<Command> commandList = new ArrayList<Command>();
    /**
     * 宏指令聚集管理方法
     */
    @Override
    public void add(Command cmd) {
        commandList.add(cmd);
    }
    /**
     * 宏指令聚集管理方法
     */
    @Override
    public void remove(Command cmd) {
        commandList.remove(cmd);
    }
    /**
     * 執行方法
     */
    @Override
    public void execute() {
        for(Command cmd : commandList){
            cmd.execute();
        }
    }
}      

  (3)用戶端類Julia

public class Julia {    
    public static void main(String[]args){
        //建立接收者對象
        AudioPlayer audioPlayer = new AudioPlayer();
        //建立指令對象
        Command playCommand = new PlayCommand(audioPlayer);
        Command rewindCommand = new RewindCommand(audioPlayer);
        Command stopCommand = new StopCommand(audioPlayer);        
        MacroCommand marco = new MacroAudioCommand();       
        marco.add(playCommand);
        marco.add(rewindCommand);
        marco.add(stopCommand);
        marco.execute();
    }
}      

 

設計模式(14)--Command(指令模式)--行為型

7.撤銷指令

 有兩種基本的思路來實作可撤銷的操作:

    (1)逆向操作實作:比如被撤銷的操作是添加功能,那撤消的實作就變成删除功能;同理被撤銷的操作是删除功能,那麼撤銷的實作就變成添加功能;

    (2)存儲恢複實作,意思就是把操作前的狀态記錄下來,然後要撤銷操作的時候就直接恢複回去就可以了,可使用備忘錄模式(Memento Pattern)來實作(稍後會講到備忘錄模式);

這裡主要講解通過逆向操作來實作撤銷(undo):

  具體操作:在指令模式中,我們可以通過調用一個指令對象的execute()方法來實作對請求的處理,如果需要撤銷(undo)請求,可通過在指令類中增加一個逆向操作來實作。

  執行個體說明:

/**
 * 包含撤銷指令的接口
 * @author 
*/
public interface Command {
    //執行方法
    void execute();
    //撤銷方法
    void undo();
}
/**
 * 具體指令 -- 建立目錄
 * @author
*/
public class CreateDirCommand implements Command {
    private String dirName;
    MakeDir makeDir;
    public CreateDirCommand(MakeDir makeDir,String dirName) {
        this.makeDir = makeDir;
        this.dirName = dirName;
    }
    @Override
    public void execute() {
        makeDir.createDir(dirName);
    }
    
    @Override
    public void undo() {
        makeDir.deleteDir(dirName);
    }
}
/**
 * 指令接受者
 * 包含兩個指令:建立目錄、删除目錄
 * @author 
*/
public class MakeDir {
    //建立目錄
    public void createDir(String name){
        File dir = new File(name);
        if (dir.exists()) {  
            System.out.println("建立目錄 " + name + " 失敗,目标目錄已經存在");  
        }else{
            //建立目錄  
            if (dir.mkdirs()) {
                System.out.println("建立目錄 " + name + " 成功");  
            } else {  
                System.out.println("建立目錄 " + name + " 失敗");  
            }  
        } 
    }
    //删除目錄
    public void deleteDir(String name){
        File dir = new File(name);
        if(dir.exists()) {  
            if(dir.delete()){
                System.out.println("删除目錄 " + name + " 成功");
            }else{
                System.out.println("删除目錄 " + name + " 失敗");
            }
        }else{
            System.out.println("删除目錄 " + name + " 失敗,目标目錄不存在");
        }
    }
}
/**
 * 請求者
 * @author
*/
public class RequestMakeDir {
    Command createCommand;
    public void setCreateCommand(Command command) {
        this.createCommand = command;
    }
    public void executeCreateCommand(){
        createCommand.execute();
    }    
    public void undoCreateCommand(){
        createCommand.undo();
    }
}      

測試:

/**
 * 測試
 * @author 
*/
public class Client {
    public static void main(String[] args) {
        String dir = "E:\\command2017";
        
        //建立接收者
        MakeDir makeDir = new MakeDir(); 
        //建立具體指令并且指定接收者
        Command create_command = new CreateDirCommand(makeDir,dir);
        //建立請求者
        RequestMakeDir request = new RequestMakeDir();
        //設定指令
        request.setCreateCommand(create_command);
        /***********建立目錄及撤銷**************/
        //建立目錄
        request.executeCreateCommand();
        //撤銷
        request.undoCreateCommand();
    }
}      

結果:

建立目錄 E:\command2017 成功
删除目錄 E:\command2017 成功      

8.多次撤銷

  我們在使用文本編輯器的時候,經常會遇到撤銷的操作,這是就按一下組合鍵(Ctrl + Z),就會回到上次修改,至于代碼要如何實作?

  首先還是要先實作各個指令的undo行為(執行與execute相反順序的操作即可),另外我們還需要一個棧來記錄已經被執行過的操作,以支援撤銷到初始狀态;

  下面來改造一下執行個體:

/**
 * 包含撤銷指令的接口
 * @author 
*/
public interface Command {
    //執行方法
    void execute(String dirName);
    //撤銷方法
    void undo();
}
/**
 * 具體指令 -- 建立目錄
 * @author 
*/
public class CreateDirCommand implements Command {
    private ArrayList<String> dirNameList; //記錄 這裡用list實作
    private MakeDir makeDir;
    public CreateDirCommand(MakeDir makeDir) {
        dirNameList = new ArrayList<String>();
        this.makeDir = makeDir;
    }
    
    @Override
    public void execute(String dirName) {
        makeDir.createDir(dirName);
        dirNameList.add(dirName);
    }
    
    @Override
    public void undo() {
        if(dirNameList.size()>0){
            makeDir.deleteDir(dirNameList.get(dirNameList.size()-1));
            dirNameList.remove(dirNameList.size()-1);
        }else{
             System.out.println("沒有需要撤銷的操作!");
        }
    }
}
/**
 * 指令接受者
 * 包含兩個指令:建立目錄、删除目錄
 * @author 
*/
public class MakeDir {
    //建立目錄
    public void createDir(String name){
        File dir = new File(name);
        if (dir.exists()) {  
            System.out.println("建立目錄 " + name + " 失敗,目标目錄已經存在");  
        }else{
            //建立目錄  
            if (dir.mkdirs()) {
                System.out.println("建立目錄 " + name + " 成功");  
            } else {  
                System.out.println("建立目錄 " + name + " 失敗");  
            }  
        } 
    }
    //删除目錄
    public void deleteDir(String name){
        File dir = new File(name);
        if(dir.exists()) {  
            if(dir.delete()){
                System.out.println("删除目錄 " + name + " 成功");
            }else{
                System.out.println("删除目錄 " + name + " 失敗");
            }
        }else{
            System.out.println("删除目錄 " + name + " 失敗,目标目錄不存在");
        }
    }
}
/**
 * 請求者
 * @author 
*/
public class RequestMakeDir {
    Command createCommand;
    public void setCreateCommand(Command command) {
        this.createCommand = command;
    }
    public void executeCreateCommand(String dirName){
        createCommand.execute(dirName);
    }
    public void undoCreateCommand(){
        createCommand.undo();
    }
}      
/**
 * 測試
 * @author 
*/
public class Client {
    public static void main(String[] args) {
        //建立接收者
        MakeDir makeDir = new MakeDir(); 
        //建立具體指令并且指定接收者
        Command create_command = new CreateDirCommand(makeDir);
        //建立請求者
        RequestMakeDir request = new RequestMakeDir();
        //設定指令
        request.setCreateCommand(create_command);
        /***********建立目錄及撤銷**************/
        //建立目錄
        request.executeCreateCommand("E:\\command\\2017");
        request.executeCreateCommand("E:\\command\\2018");
        request.executeCreateCommand("E:\\command\\2019");
        request.executeCreateCommand("E:\\command\\2020");
        //多次撤銷
        request.undoCreateCommand();
        request.undoCreateCommand();
        request.undoCreateCommand();
        request.undoCreateCommand();
        request.undoCreateCommand();
    }
}      
建立目錄 E:\command\2017 成功
建立目錄 E:\command\2018 成功
建立目錄 E:\command\2019 成功
建立目錄 E:\command\2020 成功
删除目錄 E:\command\2020 成功
删除目錄 E:\command\2019 成功
删除目錄 E:\command\2018 成功
删除目錄 E:\command\2017 成功
沒有需要撤銷的操作!      

上面的例子存在瑕疵:不知道大家有沒有看出來:

  我們沒有儲存指令對象的曆史狀态(執行情況,比如成功、失敗),可以通過引入一個指令集合或其他方式來存儲每一次操作時指令的狀态,這樣實作多次撤銷操作就完美了

9.指令隊列

  有時候我們需要将多個請求排隊,當一個請求發送者發送一個請求時,将不止一個請求接收者産生響應,這些請求接收者将逐個執行業務方法,完成對請求的處理。此時,我們可以通過指令隊列來實作。

  指令隊列的實作方法有多種形式,其中最常用、靈活性最好的一種方式是增加一個CommandQueue類,由該類來負責存儲多個指令對象,而不同的指令對象可以對應不同的請求接收者;

下面執行個體講解:

/**
 * 抽象指令角色類
 * @author 
*/
public interface Command {
     /**
     * 執行方法
     */
    void execute();
}
/**
 * 接收者角色類 - Java工程師
 * @author 
*/
public class JavaCoder {
     /**
     * 真正執行指令相應的操作
     */
    public void work(String task){
        System.out.println("java工程師要完成【"+task+"】");
    }
}
/**
 * 接收者角色類 - H5工程師
 * @author 
*/
public class H5Coder {
    /**
     * 真正執行指令相應的操作
     */
    public void work(String task){
        System.out.println("H5工程師要完成【"+task+"】");
    }
}
/**
 * 具體指令 -- Java工程師執行任務
 * @author 
*/
public class JavaCommand implements Command{
    //持有相應的接收者對象
    private JavaCoder javaCoder;
    //具體任務
    private String task;
    /**
     * 構造方法
     */
    public JavaCommand(JavaCoder javaCoder,String task){
        this.javaCoder = javaCoder;
        this.task = task;
    }
    
    @Override
    public void execute() {
        //通常會轉調接收者對象的相應方法,讓接收者來真正執行功能
        javaCoder.work(this.task);
    }
}
/**
 * 具體指令 -- H5工程師執行任務
 * @author 
*/
public class H5Command implements Command{
    //持有相應的接收者對象
    private H5Coder h5Coder;
    //具體任務
    private String task;
    /**
     * 構造方法
     */
    public H5Command(H5Coder h5Coder,String task){
        this.h5Coder = h5Coder;
        this.task = task;
    }
    
    @Override
    public void execute() {
        //通常會轉調接收者對象的相應方法,讓接收者來真正執行功能
        h5Coder.work(this.task);
    }
}
/**
 * CommandQueue指令隊列類
 * @author 
*/
public class CommandQueue {
    //定義一個ArrayList來存儲指令隊列    
    private ArrayList<Command> commands = new ArrayList<Command>();    
        
    public void addCommand(Command command) {
        commands.add(command);    
    }    
        
    public void removeCommand(Command command) {    
        commands.remove(command);    
    }    
        
    //循環調用每一個指令對象的execute()方法
    public void execute() {    
        for(Command command : commands) {    
            command.execute();    
        }    
    }    
}
/**
 * 請求發送者類Manager針對CommandQueue程式設計
 * @author 
*/
public class Manager {
    private CommandQueue commandQueue; //維持一個CommandQueue對象的引用    
     
    //構造注入    
    public Manager(CommandQueue commandQueue) {
        this. commandQueue = commandQueue;    
    }    
        
    //設值注入    
    public void setCommandQueue(CommandQueue commandQueue) {    
        this.commandQueue = commandQueue;    
    }    
        
    //調用CommandQueue類的execute()方法    
    public void call() {    
        commandQueue.execute();    
    }    
}      
/**
 * 
 * @author 
*/
public class Client {
    public static void main(String[] args) {
        //建立接收者
        JavaCoder javaCoder = new JavaCoder();
        H5Coder h5Coder = new H5Coder();
         //建立指令對象
        JavaCommand javaCommand = new JavaCommand(javaCoder,"登入子產品的背景代碼");
        H5Command h5Command = new H5Command(h5Coder,"登入子產品的前台代碼");
        //建立指令對象隊列
        CommandQueue commandqueue = new CommandQueue();
        commandqueue.addCommand(javaCommand);
        commandqueue.addCommand(h5Command);
        //建立請求者,把指令對象設定進去
        Manager manager = new Manager(commandqueue);
        //執行方法
        manager.call();
    }
}      
java工程師要完成【登入子產品的背景代碼】
H5工程師要完成【登入子產品的前台代碼】      

上面簡單隻是實作,旨在了解指令隊列的結構:

  當一個發送者發送請求後,将有一系列接收者對請求作出響應,指令隊列可以用于設計批處理應用程式,如果請求接收者的接收次序沒有嚴格的先後次序,我們還可以使用多線程技術來并發調用指令對象的execute()方法,進而提高程式的執行效率。

10.請求日志

  概念:将所有的動作都記錄在日志中,并能在系統當機後,重新調用這些動作恢複到之前的狀态。當我們執行指令的時候,将曆時記錄存儲在磁盤中。一旦系統當機,我們就可以将指令對象重新加載,并依次調用這些對象的execute()方法。

  實作方法:利用對象的序列化把對象儲存起來(記錄日志),在需要的時候反序列化(恢複事務)。

請求日志檔案可以實作很多功能,常用功能如下:

    (1)什麼事情都存在意外,如一旦系統發生故障,日志檔案可以為系統提供一種恢複機制,在請求日志檔案中可以記錄使用者對系統的每一步操作,進而讓系統能夠順利恢複到某一個特定的狀态;

    (2)請求日志也可以用于實作批處理,在一個請求日志檔案中可以存儲一系列指令對象,例如一個指令隊列;

    (3)可以将指令隊列中的所有指令對象都存儲在一個日志檔案中,每執行一個指令則從日志檔案中删除一個對應的指令對象,防止因為斷電或者系統重新開機等原因造成請求丢失,而且可以避免重新發送全部請求時造成某些指令的重複執行,隻需讀取請求日志檔案,再繼續執行檔案中剩餘的指令即可。

  模拟場景(未必真實,領會實質即可):

    假如資料庫被DBA誤操作了,資料全删除了,因為兩小時備份了一次資料庫,是以現在隻能恢複到兩小時前的資料;這豈不是很悲劇;要被開除的節奏;還要賠償公司經濟損失;瞬間感覺要死的節奏;

    要是在程式裡加上請求日志,這個算個鳥,分分鐘恢複;

    我們把程式執行的所有sql記錄下來,這裡假設sql都是在這兩小時執行的(比較理想,具體情境具體解決);

  代碼來了:

/**
 * 抽象指令類,由于需要将指令對象寫入檔案,是以它實作了Serializable接口  
 * @author 
*/
public abstract class Command implements Serializable {
    private static final long serialVersionUID = 1L;
    
    protected String name; //指令名稱 
    protected String args; //指令參數  
    protected Operator operator; //接收者對象
    public Command(String name) {  
        this.name = name;
    }        
    public String getName() {  
        return this.name;  
    }       
    public void setName(String name) {  
        this.name = name;  
    }        
    public void setOperator(Operator operator) {  
        this.operator = operator;  
    }      
    //聲明兩個抽象的執行方法execute()  
      public abstract void execute(String args);  
      public abstract void execute();
}
/**
 * 請求接收者 -- 由于Operator類的對象是Command的成員對象,它也将随Command對象一起寫入檔案,是以Operator也需要實作Serializable接口  
 * @author 
*/
public class Operator implements Serializable{
    private static final long serialVersionUID = 1L;

    public void insert(String args) {  
        System.out.println("新增資料:" + args);  
    }  
      
    public void update(String args) {  
        System.out.println("修改資料:" + args);  
    }  
      
    public void delete(String args) {  
        System.out.println("删除資料:" + args);  
    }  
}
/**
 * 具體指令角色類 -- 插入指令
 * @author 
*/
public class InsertCommand extends Command{
    public InsertCommand(String name) {
        super(name);
    }

    public void execute(String args) {  
        this.args = args;  
        operator.insert(args);  
    }  
      
    public void execute() {  
        operator.insert(this.args);  
    }  
}
/**
 * 具體指令角色類 -- 修改指令
 * @author 
*/
public class UpdateCommand extends Command{
    public UpdateCommand(String name) {
        super(name);
    }

    public void execute(String args) {  
        this.args = args;  
        operator.update(args);  
    }  
      
    public void execute() {  
        operator.update(this.args);  
    }  
}
/**
 * 具體指令角色類 -- 删除指令
 * @author 
*/
public class DeleteCommand extends Command{
    public DeleteCommand(String name) {
        super(name);
    }

    public void execute(String args) {  
        this.args = args;  
        operator.delete(args);  
    }  
      
    public void execute() {  
        operator.delete(this.args);  
    }  
}
import com.designpattern.utils.FileUtil;
/**
 * 請求發送者
 * @author 
*/
public class SqlExecuteTool {
     //定義一個集合來存儲每一次操作時的指令對象  
    private ArrayList<Command> commands = new ArrayList<Command>();  
    private Command command;
  
    //注入具體指令對象  
    public void setCommand(Command command) {  
        this.command = command;  
    }  
      
    //執行配置檔案修改指令,同時将指令對象添加到指令集合中  
    public void call(String args) {  
        command.execute(args);  
        commands.add(command);  
    }  
      
    //記錄請求日志,生成日志檔案,将指令集合寫入日志檔案  
    public void save() {  
        FileUtil.writeCommands(commands);  
    }
      
    //從日志檔案中提取指令集合,并循環調用每一個指令對象的execute()方法來實作配置檔案的重新設定  
    public void recover() {  
        ArrayList list = FileUtil.readCommands();  
        for (Object obj : list) {  
            ((Command)obj).execute();  
        }  
    }  
}      
package com.designpattern.Command.extend.RequestLog;
/**
 * 測試
 * @author 
*/
public class Client {
    public static void main(String[] args) {
        SqlExecuteTool tool = new SqlExecuteTool(); //定義請求發送者  
        Operator operator = new Operator(); //定義請求接收者  
          
        Command command;
        
        //執行了很多次SQL
        command = new InsertCommand("增加");  
        command.setOperator(operator);   
        tool.setCommand(command);  
        tool.call("insert xxx");  
          
        command = new InsertCommand("增加");  
        command.setOperator(operator);  
        tool.setCommand(command);  
        tool.call("insert xxx");  
          
        command = new UpdateCommand("修改"); 
        command.setOperator(operator); 
        tool.setCommand(command);  
        tool.call("update xxx");  
          
        command = new DeleteCommand("删除");
        command.setOperator(operator);  
        tool.setCommand(command);          
        tool.call("delete xxx");  
          
        System.out.println("-------------------------------------");
        System.out.println("儲存執行的sql");  
        tool.save();
        System.out.println("-------------------------------------");
        System.out.println("恢複執行的sql");  
        System.out.println("-------------------------------------");
        tool.recover(); 
    }
}      
新增資料:insert xxx
新增資料:insert xxx
修改資料:update xxx
删除資料:delete xxx
-------------------------------------
儲存執行的sql
-------------------------------------
恢複執行的sql
-------------------------------------
新增資料:insert xxx
新增資料:insert xxx
修改資料:update xxx
删除資料:delete xxx      

這裡把這兩小時的sql又執行了一遍,是不是相當于資料全部恢複了?

繼續閱讀