请求发送者与接收者解耦——命令模式(五)

  • Post author:
  • Post category:其他




6

请求日志


请求日志就是将请求的历史记录保存下来,通常以


日志文件

(Log File)



的形式永久存储在计算机中。很多系统都提供了日志文件,例如

Windows

日志文件、

Oracle

日志文件等,日志文件可以记录用户对系统的一些操作(例如对数据的更改)。请求日志文件可以实现很多功能,常用功能如下:



(1)

“天有不测风云”,一旦系统发生故障,日志文件可以为系统提供一种恢复机制,在请求日志文件中可以记录用户对系统的每一步操作,从而让系统能够顺利恢复到某一个特定的状态;



(2)

请求日志也可以用于实现批处理,在一个请求日志文件中可以存储一系列命令对象,例如一个命令队列;



(3)

可以将命令队列中的所有命令对象都存储在一个日志文件中,每执行一个命令则从日志文件中删除一个对应的命令对象,防止因为断电或者系统重启等原因造成请求丢失,而且可以避免重新发送全部请求时造成某些命令的重复执行,只需读取请求日志文件,再继续执行文件中剩余的命令即可。


在实现请求日志时,我们可以


将命令对象通过序列化写到日志文件中


,此时命令类必须实现

java.io.Serializable

接口。下面我们通过一个简单实例来说明日志文件的用途以及如何实现请求日志:



Sunny

软件公司开发了一个网站配置文件管理工具,可以通过一个可视化界面对网站配置文件进行增删改等操作,该工具使用命令模式进行设计,结构如图

6

所示:







6

网站配置文件管理工具结构图


现在

Sunny

软件公司开发人员希望将对配置文件的操作请求记录在日志文件中,如果网站重新部署,只需要执行保存在日志文件中的命令对象即可修改配置文件。


本实例完整代码如下所示:


  1. import

    java.io.*;

  2. import

    java.util.*;

  3. //抽象命令类,由于需要将命令对象写入文件,因此它实现了Serializable接口

  4. abstract


    class

    Command

    implements

    Serializable {

  5. protected

    String name;

    //命令名称

  6. protected

    String args;

    //命令参数

  7. protected

    ConfigOperator configOperator;

    //维持对接收者对象的引用

  8. public

    Command(String name) {

  9. this

    .name = name;
  10. }

  11. public

    String getName() {

  12. return


    this

    .name;
  13. }

  14. public


    void

    setName(String name) {

  15. this

    .name = name;
  16. }

  17. public


    void

    setConfigOperator(ConfigOperator configOperator) {

  18. this

    .configOperator = configOperator;
  19. }

  20. //声明两个抽象的执行方法execute()

  21. public


    abstract


    void

    execute(String args);

  22. public


    abstract


    void

    execute();
  23. }

  24. //增加命令类:具体命令

  25. class

    InsertCommand

    extends

    Command {

  26. public

    InsertCommand(String name) {

  27. super

    (name);
  28. }

  29. public


    void

    execute(String args) {

  30. this

    .args = args;
  31. configOperator.insert(args);
  32. }

  33. public


    void

    execute() {
  34. configOperator.insert(

    this

    .args);
  35. }
  36. }

  37. //修改命令类:具体命令

  38. class

    ModifyCommand

    extends

    Command {

  39. public

    ModifyCommand(String name) {

  40. super

    (name);
  41. }

  42. public


    void

    execute(String args) {

  43. this

    .args = args;
  44. configOperator.modify(args);
  45. }

  46. public


    void

    execute() {
  47. configOperator.modify(

    this

    .args);
  48. }
  49. }

  50. //省略了删除命令类DeleteCommand

  51. //配置文件操作类:请求接收者。由于ConfigOperator类的对象是Command的成员对象,它也将随Command对象一起写入文件,因此ConfigOperator也需要实现Serializable接口

  52. class

    ConfigOperator

    implements

    Serializable {

  53. public


    void

    insert(String args) {
  54. System.out.println(

    “增加新节点:”

    + args);
  55. }

  56. public


    void

    modify(String args) {
  57. System.out.println(

    “修改节点:”

    + args);
  58. }

  59. public


    void

    delete(String args) {
  60. System.out.println(

    “删除节点:”

    + args);
  61. }
  62. }

  63. //配置文件设置窗口类:请求发送者

  64. class

    ConfigSettingWindow {

  65. //定义一个集合来存储每一次操作时的命令对象

  66. private

    ArrayList<Command> commands =

    new

    ArrayList<Command>();

  67. private

    Command command;

  68. //注入具体命令对象

  69. public


    void

    setCommand(Command command) {

  70. this

    .command = command;
  71. }

  72. //执行配置文件修改命令,同时将命令对象添加到命令集合中

  73. public


    void

    call(String args) {
  74. command.execute(args);
  75. commands.add(command);
  76. }

  77. //记录请求日志,生成日志文件,将命令集合写入日志文件

  78. public


    void

    save() {
  79. FileUtil.writeCommands(commands);
  80. }

  81. //从日志文件中提取命令集合,并循环调用每一个命令对象的execute()方法来实现配置文件的重新设置

  82. public


    void

    recover() {
  83. ArrayList list;
  84. list = FileUtil.readCommands();

  85. for

    (Object obj : list) {
  86. ((Command)obj).execute();
  87. }
  88. }
  89. }

  90. //工具类:文件操作类

  91. class

    FileUtil {

  92. //将命令集合写入日志文件

  93. public


    static


    void

    writeCommands(ArrayList commands) {

  94. try

    {
  95. FileOutputStream file =

    new

    FileOutputStream(

    “config.log”

    );

  96. //创建对象输出流用于将对象写入到文件中
  97. ObjectOutputStream objout =

    new

    ObjectOutputStream(

    new

    BufferedOutputStream(file));

  98. //将对象写入文件
  99. objout.writeObject(commands);
  100. objout.close();
  101. }

  102. catch

    (Exception e) {
  103. System.out.println(

    “命令保存失败!”

    );
  104. e.printStackTrace();
  105. }
  106. }

  107. //从日志文件中提取命令集合

  108. public


    static

    ArrayList readCommands() {

  109. try

    {
  110. FileInputStream file =

    new

    FileInputStream(

    “config.log”

    );

  111. //创建对象输入流用于从文件中读取对象
  112. ObjectInputStream objin =

    new

    ObjectInputStream(

    new

    BufferedInputStream(file));

  113. //将文件中的对象读出并转换为ArrayList类型
  114. ArrayList commands = (ArrayList)objin.readObject();
  115. objin.close();

  116. return

    commands;
  117. }

  118. catch

    (Exception e) {
  119. System.out.println(

    “命令读取失败!”

    );
  120. e.printStackTrace();

  121. return


    null

    ;
  122. }
  123. }
  124. }




编写如下客户端测试代码:


  1. class

    Client {

  2. public


    static


    void

    main(String args[]) {
  3. ConfigSettingWindow csw =

    new

    ConfigSettingWindow();

    //定义请求发送者
  4. Command command;

    //定义命令对象
  5. ConfigOperator co =

    new

    ConfigOperator();

    //定义请求接收者

  6. //四次对配置文件的更改
  7. command =

    new

    InsertCommand(

    “增加”

    );
  8. command.setConfigOperator(co);
  9. csw.setCommand(command);
  10. csw.call(

    “网站首页”

    );
  11. command =

    new

    InsertCommand(

    “增加”

    );
  12. command.setConfigOperator(co);
  13. csw.setCommand(command);
  14. csw.call(

    “端口号”

    );
  15. command =

    new

    ModifyCommand(

    “修改”

    );
  16. command.setConfigOperator(co);
  17. csw.setCommand(command);
  18. csw.call(

    “网站首页”

    );
  19. command =

    new

    ModifyCommand(

    “修改”

    );
  20. command.setConfigOperator(co);
  21. csw.setCommand(command);
  22. csw.call(

    “端口号”

    );
  23. System.out.println(

    “—————————-”

    );
  24. System.out.println(

    “保存配置”

    );
  25. csw.save();
  26. System.out.println(

    “—————————-”

    );
  27. System.out.println(

    “恢复配置”

    );
  28. System.out.println(

    “—————————-”

    );
  29. csw.recover();
  30. }
  31. }




编译并运行程序,输出结果如下:


增加新节点:网站首页


增加新节点:端口号


修改节点:网站首页


修改节点:端口号


—————————-


保存配置


—————————-


恢复配置


—————————-



增加新节点:网站首页



增加新节点:端口号



修改节点:网站首页



修改节点:端口号