請求傳送者與接收者解耦——命令模式(五)
6 請求日誌
請求日誌就是將請求的歷史記錄儲存下來,通常以日誌檔案(Log File)的形式永久儲存在計算機中。很多系統都提供了日誌檔案,例如Windows日誌檔案、Oracle日誌檔案等,日誌檔案可以記錄使用者對系統的一些操作(例如對資料的更改)。請求日誌檔案可以實現很多功能,常用功能如下:
(1) “天有不測風雲”,一旦系統發生故障,日誌檔案可以為系統提供一種恢復機制,在請求日誌檔案中可以記錄使用者對系統的每一步操作,從而讓系統能夠順利恢復到某一個特定的狀態;
(2) 請求日誌也可以用於實現批處理,在一個請求日誌檔案中可以儲存一系列命令物件,例如一個命令佇列;
(3) 可以將命令佇列中的所有命令物件都儲存在一個日誌檔案中,每執行一個命令則從日誌檔案中刪除一個對應的命令物件,防止因為斷電或者系統重啟等原因造成請求丟失,而且可以避免重新傳送全部請求時造成某些命令的重複執行,只需讀取請求日誌檔案,再繼續執行檔案中剩餘的命令即可。
在實現請求日誌時,我們可以將命令物件通過序列化寫到日誌檔案中,此時命令類必須實現java.io.Serializable介面。下面我們通過一個簡單例項來說明日誌檔案的用途以及如何實現請求日誌:
Sunny軟體公司開發了一個網站配置檔案管理工具,可以通過一個視覺化介面對網站配置檔案進行增刪改等操作,該工具使用命令模式進行設計,結構如圖6所示: 圖6 網站配置檔案管理工具結構圖 現在Sunny軟體公司開發人員希望將對配置檔案的操作請求記錄在日誌檔案中,如果網站重新部署,只需要執行儲存在日誌檔案中的命令物件即可修改配置檔案。 |
本例項完整程式碼如下所示:
import java.io.*;
import java.util.*;
//抽象命令類,由於需要將命令物件寫入檔案,因此它實現了Serializable介面
abstract class Command implements Serializable {
protected String name; //命令名稱
protected String args; //命令引數
protected ConfigOperator configOperator; //維持對接收者物件的引用
public Command(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public void setConfigOperator(ConfigOperator configOperator) {
this.configOperator = configOperator;
}
//宣告兩個抽象的執行方法execute()
public abstract void execute(String args);
public abstract void execute();
}
//增加命令類:具體命令
class InsertCommand extends Command {
public InsertCommand(String name) {
super(name);
}
public void execute(String args) {
this.args = args;
configOperator.insert(args);
}
public void execute() {
configOperator.insert(this.args);
}
}
//修改命令類:具體命令
class ModifyCommand extends Command {
public ModifyCommand(String name) {
super(name);
}
public void execute(String args) {
this.args = args;
configOperator.modify(args);
}
public void execute() {
configOperator.modify(this.args);
}
}
//省略了刪除命令類DeleteCommand
//配置檔案操作類:請求接收者。由於ConfigOperator類的物件是Command的成員物件,它也將隨Command物件一起寫入檔案,因此ConfigOperator也需要實現Serializable介面
class ConfigOperator implements Serializable {
public void insert(String args) {
System.out.println("增加新節點:" + args);
}
public void modify(String args) {
System.out.println("修改節點:" + args);
}
public void delete(String args) {
System.out.println("刪除節點:" + args);
}
}
//配置檔案設定視窗類:請求傳送者
class ConfigSettingWindow {
//定義一個集合來儲存每一次操作時的命令物件
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;
list = FileUtil.readCommands();
for (Object obj : list) {
((Command)obj).execute();
}
}
}
//工具類:檔案操作類
class FileUtil {
//將命令集合寫入日誌檔案
public static void writeCommands(ArrayList commands) {
try {
FileOutputStream file = new FileOutputStream("config.log");
//建立物件輸出流用於將物件寫入到檔案中
ObjectOutputStream objout = new ObjectOutputStream(new BufferedOutputStream(file));
//將物件寫入檔案
objout.writeObject(commands);
objout.close();
}
catch(Exception e) {
System.out.println("命令儲存失敗!");
e.printStackTrace();
}
}
//從日誌檔案中提取命令集合
public static ArrayList readCommands() {
try {
FileInputStream file = new FileInputStream("config.log");
//建立物件輸入流用於從檔案中讀取物件
ObjectInputStream objin = new ObjectInputStream(new BufferedInputStream(file));
//將檔案中的物件讀出並轉換為ArrayList型別
ArrayList commands = (ArrayList)objin.readObject();
objin.close();
return commands;
}
catch(Exception e) {
System.out.println("命令讀取失敗!");
e.printStackTrace();
return null;
}
}
}
編寫如下客戶端測試程式碼:
class Client {
public static void main(String args[]) {
ConfigSettingWindow csw = new ConfigSettingWindow(); //定義請求傳送者
Command command; //定義命令物件
ConfigOperator co = new ConfigOperator(); //定義請求接收者
//四次對配置檔案的更改
command = new InsertCommand("增加");
command.setConfigOperator(co);
csw.setCommand(command);
csw.call("網站首頁");
command = new InsertCommand("增加");
command.setConfigOperator(co);
csw.setCommand(command);
csw.call("埠號");
command = new ModifyCommand("修改");
command.setConfigOperator(co);
csw.setCommand(command);
csw.call("網站首頁");
command = new ModifyCommand("修改");
command.setConfigOperator(co);
csw.setCommand(command);
csw.call("埠號");
System.out.println("----------------------------");
System.out.println("儲存配置");
csw.save();
System.out.println("----------------------------");
System.out.println("恢復配置");
System.out.println("----------------------------");
csw.recover();
}
}
編譯並執行程式,輸出結果如下:
增加新節點:網站首頁 增加新節點:埠號 修改節點:網站首頁 修改節點:埠號 ---------------------------- 儲存配置 ---------------------------- 恢復配置 ---------------------------- 增加新節點:網站首頁 增加新節點:埠號 修改節點:網站首頁 修改節點:埠號 |
【作者:劉偉 http://blog.csdn.net/lovelion】
相關文章
- Java設計模式-16、命令模式-請求傳送者與接收者解耦Java設計模式解耦
- 命令模式-接收者與執行者解耦和模式解耦
- Postman傳送Post請求Postman
- java傳送http請求JavaHTTP
- Java傳送Post請求Java
- 傳送GET請求 示例
- SpringMVC中如何傳送GET請求、POST請求、PUT請求、DELETE請求。SpringMVCdelete
- 如何傳送請求以及AJAX
- python傳送HTTP POST請求PythonHTTP
- 使用Feign傳送HTTP請求HTTP
- Vue 使用 Axios 傳送請求的請求體問題VueiOS
- Postman傳送請求引數是Map格式的請求Postman
- Vue中封裝axios傳送請求Vue封裝iOS
- java傳送GET和post請求Java
- linux用curl傳送post請求Linux
- Python爬蟲(二)——傳送請求Python爬蟲
- nodejs使用request傳送http請求NodeJSHTTP
- axios傳送兩次請求原因及解決方法iOS
- Linux基礎命令---accept/reject 允許拒絕傳送列印請求Linux
- scrapy-redis原始碼解讀之傳送POST請求Redis原始碼
- vue中使用axios傳送ajax請求VueiOS
- react-fetch資料傳送請求React
- 首頁 使用axios 傳送ajax請求iOS
- 使用requests庫來傳送HTTP請求HTTP
- httprequest- post- get -傳送請求HTTP
- 使用Postman傳送POST請求的指南Postman
- java傳送get請求帶引數Java
- shell指令碼:批次傳送curl請求指令碼
- file_get_contents傳送post請求
- 以Raw的方式傳送POST請求
- jQuery裡如何使用ajax傳送請求jQuery
- 傳送 options 請求 後端返回 405 的解決過程後端
- 為何要在componentDidMount裡面傳送請求?
- postman(二):使用postman傳送get or post請求Postman
- 什麼時候會傳送options請求
- Go HTTP GET 請求可以傳送 body 嗎GoHTTP
- html頁面中如何傳送ajax請求HTML
- curl 傳送 POST 請求的四種方式
- 如何在 Go 中傳送表單請求Go