命令模式 undo,redo参考:http://blog.csdn.net/dengjili/article/details/79533521
序列化与反序列化参考:http://blog.csdn.net/dengjili/article/details/79541333
描述来自于headfirst
某些应用需要将所有的动作都记录在日志中,并能在系统死机后,重新调用这些动作恢复到之前的状态,通过新增store、load两个方法,就能支持这一点。当我们执行命令的时候,调用store方法,将历史记录在磁盘中,一旦系统死机,就可以将命令load重载,并一次调用这些对象的execute方法。许多调用大型数据结构的动作的应用无法在每次改变时快速地存储。通过记录日志,可以将上次检查点之后的所有操作记录下来,如果出现问题,就从检查点开始重新执行操作,检查点是能确保之前的操作全部执行成功的一个时间点。
具体的例子
当我们操作数据时候,我们需要向数据库执行A、B、C、D命令,然而,在执行完A、B、C命令后,系统宕机,然后我们找到A、B、C原始命令(序列化),通过执行undo操作,回退到之前正确的状态
具体的例子
对数据库执行,添加,查询,删除,更新操作,在执行删除操作时候,系统报出异常(模拟宕机),然后通过load回滚到之前正常的状态
reveiver:数据库操作功能
package headfirst.hd.command.db.reveiver;
import java.io.Serializable;
public class DbOp implements Serializable {
private static final long serialVersionUID = 1L;
public void add() {
System.out.println("对数据【添加】操作");
}
public void delete() {
System.out.println("对数据【删除】操作");
//模拟宕机
throw new RuntimeException("当前操作【删除】,系统宕机。。。");
}
public void update() {
System.out.println("对数据【修改】操作");
}
public void query() {
System.out.println("对数据【查询】操作");
}
public void addCancel() {
System.out.println("【回退之前】对数据【添加】操作");
}
public void deleteCancel() {
System.out.println("【回退之前】对数据【删除】操作");
}
public void updateCancel() {
System.out.println("【回退之前】对数据【修改】操作");
}
//查询不需要回退操作,空方法,这里只是显示提示
public void queryCancel() {
System.out.println("【回退之前】对数据【查询】操作");
}
}
command接口
package headfirst.hd.command.db.interfaces;
import java.io.Serializable;
public interface Command extends Serializable{
//执行命令的正常操作
void execute();
//回退操作
void undo();
//序列化操作,对应execute方法
void store();
}
concretecommand具体功能
package headfirst.hd.command.db.concretecommand;
import headfirst.hd.command.db.interfaces.Command;
import headfirst.hd.command.db.reveiver.DbOp;
import headfirst.hd.command.db.utils.DBUtils;
public class DbAdd implements Command{
private static final long serialVersionUID = 1L;
private DbOp dbOp;
public DbAdd(DbOp dbOp) {
super();
this.dbOp = dbOp;
}
@Override
public void execute() {
dbOp.add();
System.out.println("命令执行完成,开始序列化本次命令");
store();
}
@Override
public void undo() {
dbOp.addCancel();
}
@Override
public void store() {
DBUtils.store(this);
}
}
package headfirst.hd.command.db.concretecommand;
import headfirst.hd.command.db.interfaces.Command;
import headfirst.hd.command.db.reveiver.DbOp;
import headfirst.hd.command.db.utils.DBUtils;
public class DbDelete implements Command{
private static final long serialVersionUID = 1L;
private DbOp dbOp;
public DbDelete(DbOp dbOp) {
super();
this.dbOp = dbOp;
}
@Override
public void execute() {
dbOp.delete();
System.out.println("命令执行完成,开始序列化本次命令");
store();
}
@Override
public void undo() {
dbOp.deleteCancel();
}
@Override
public void store() {
DBUtils.store(this);
}
}
package headfirst.hd.command.db.concretecommand;
import headfirst.hd.command.db.interfaces.Command;
import headfirst.hd.command.db.reveiver.DbOp;
import headfirst.hd.command.db.utils.DBUtils;
public class DbQuery implements Command{
private static final long serialVersionUID = 1L;
private DbOp dbOp;
public DbQuery(DbOp dbOp) {
super();
this.dbOp = dbOp;
}
@Override
public void execute() {
dbOp.query();
System.out.println("命令执行完成,开始序列化本次命令");
store();
}
@Override
public void undo() {
dbOp.queryCancel();
}
@Override
public void store() {
DBUtils.store(this);
}
}
package headfirst.hd.command.db.concretecommand;
import headfirst.hd.command.db.interfaces.Command;
import headfirst.hd.command.db.reveiver.DbOp;
import headfirst.hd.command.db.utils.DBUtils;
public class DbUpdate implements Command{
private static final long serialVersionUID = 1L;
private DbOp dbOp;
public DbUpdate(DbOp dbOp) {
super();
this.dbOp = dbOp;
}
@Override
public void execute() {
dbOp.update();
System.out.println("命令执行完成,开始序列化本次命令");
store();
}
@Override
public void undo() {
dbOp.updateCancel();
}
@Override
public void store() {
DBUtils.store(this);
}
}
invoker控制器:DbControl
package headfirst.hd.command.db.invoker;
import java.util.ArrayList;
import java.util.List;
import headfirst.hd.command.db.interfaces.Command;
import headfirst.hd.command.db.utils.DBUtils;
public class DbControl {
private List<Command> commands = new ArrayList<Command>();
public void setCommand(Command command) {
commands.add(command);
}
public void startExecutes() {
for (Command command : commands) {
command.execute();
}
}
//额外方法,这个方法应该不放在这个类中,这里简化操作,放在这里
public void loadComands() {
for (Command command : DBUtils.load()) {
command.undo();
}
}
}
工具类
package headfirst.hd.command.db.utils;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
//单件模式
public class SerializableHelper {
public static String FILE_NAME = "a.txt";
private static class SingletonHolder {
private static SerializableHelper instance = new SerializableHelper();
private static ObjectOutputStream oos;
static {
try {
FileOutputStream fos = new FileOutputStream(FILE_NAME);
oos = new ObjectOutputStream(fos);
} catch (IOException e) {
//这里简单处理
e.printStackTrace();
}
}
}
private SerializableHelper() {
}
public static SerializableHelper getInstance() {
return SingletonHolder.instance;
}
public static ObjectOutputStream getOOS() {
return SingletonHolder.oos;
}
//暂不处理
public static void release() {
}
//必须获取最新的,不然一开始加载的就是空文件
/* public static ObjectInputStream getOIS() {
return SingletonHolder.ois;
}*/
}
package headfirst.hd.command.db.utils;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
import headfirst.hd.command.db.interfaces.Command;
public class DBUtils {
public static void store(Command command) {
ObjectOutputStream oos = SerializableHelper.getOOS();
try {
oos.writeObject(command);
} catch (IOException e) {
// 这里不能抛异常,子类异常不能超过父类异常,简单处理
e.printStackTrace();
}
}
public static List<Command> load() {
List<Command> commands = new ArrayList<Command>();
FileInputStream fis = null;
try {
fis = new FileInputStream(SerializableHelper.FILE_NAME);
ObjectInputStream ois = new ObjectInputStream(fis);
while (true) {
Object object = ois.readObject();
//if (object instanceof Command),未考察区别,大多数人用的下面方法
if (Command.class.isAssignableFrom(object.getClass())) {
Command command = (Command) object;
commands.add(command);
}
}
} catch (Exception e) {
//请百度:ObjectInputStream判断文件是否序列化完毕的思路,由于是模拟系统宕机,不可能是文件末标志结束符,这里用异常方式
//读取到文件末,会抛异常,这里返回null,作为判断文件结束条件
} finally {
try {
if (fis != null) {
fis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return commands;
}
}
模拟数据库操作,报错
package headfirst.hd.command.db;
import headfirst.hd.command.db.concretecommand.DbAdd;
import headfirst.hd.command.db.concretecommand.DbDelete;
import headfirst.hd.command.db.concretecommand.DbQuery;
import headfirst.hd.command.db.concretecommand.DbUpdate;
import headfirst.hd.command.db.invoker.DbControl;
import headfirst.hd.command.db.reveiver.DbOp;
public class DriveTest {
public static void main(String[] args) {
//invoker
DbControl dbControl = new DbControl();
//reveiver
DbOp dbOp = new DbOp();
//concretecommand
DbAdd dbAdd = new DbAdd(dbOp);
DbQuery dbQuery = new DbQuery(dbOp);
DbDelete dbDelete = new DbDelete(dbOp);
DbUpdate dbUpdate = new DbUpdate(dbOp);
//绑定
dbControl.setCommand(dbAdd);
dbControl.setCommand(dbQuery);
dbControl.setCommand(dbDelete); //报错
dbControl.setCommand(dbUpdate);
dbControl.startExecutes();
}
}
测试结果
模拟恢复
package headfirst.hd.command.db;
import headfirst.hd.command.db.invoker.DbControl;
public class LoadTest {
public static void main(String[] args) {
//invoker
DbControl dbControl = new DbControl();
//加载异常时候的命令,回退
dbControl.loadComands();
}
}
测试结果
预期结果
满足