end0tknr's kipple - 新web写経開発

http://d.hatena.ne.jp/end0tknr/ から移転しました

perlやjavascriptでCommandパターンによるundoを実装したい

と思い、ググッてみたら、次のurlが参考になりそう。

泥船 - PerlでCommandパターン
http://d.hatena.ne.jp/hachi_ukkari/20090221/1235199125
あしあと日記 - Undo,Redoの実装って何十回もやってる気がする
http://d.hatena.ne.jp/Youchan/20081110/1226282911
あしあと日記 - Undo,Redoの実装つづき
http://d.hatena.ne.jp/Youchan/20081111/1226388917
Bug Catharsis - Undo,Redoの実装って何回かしかやってない気がする...
http://d.hatena.ne.jp/zecl/20091002/p1

WEB+DB PRESS vol.6にもperlによるデザインパターンが記載されており、その中でcommandパターンも簡単に紹介されています。

interfaceのないperljavascriptで、Commandパターンによるundoを実装するなら、クラス構成は次のようになるかな? 実際に実行すると、いろいろと変更点が出てくると思いますが

これまでMVCでは描いていましたが、controlerにある各action methodを子クラス化にすることで実現できるかな?

例 2013/11/29追記

http://alexanderbrevig.github.io/CommandManager.js/examples/

var CommandManager = (function() {
    function CommandManager() {}
    
    CommandManager.executed = [];
    CommandManager.unexecuted = [];
    
    CommandManager.execute = function execute(cmd) {
        cmd.execute();
        CommandManager.executed.push(cmd);
    };
    
    CommandManager.undo = function undo() {
        var cmd1 = CommandManager.executed.pop();
        if (cmd1 !== undefined){
            if (cmd1.unexecute !== undefined){
                cmd1.unexecute();
            }
            CommandManager.unexecuted.push(cmd1);
        }
    };
    
    CommandManager.redo = function redo() {
        var cmd2 = CommandManager.unexecuted.pop();
        
        if (cmd2 === undefined){
            cmd2 = CommandManager.executed.pop();
            CommandManager.executed.push(cmd2); 
            CommandManager.executed.push(cmd2); 
        }
        
        if (cmd2 !== undefined){
            cmd2.execute();
            CommandManager.executed.push(cmd2); 
        }
    };
    
    return CommandManager;
})();
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset=utf-8 />
  <title>CommandManager.js</title>
  <link href="http://twitter.github.com/bootstrap/assets/css/bootstrap.css" rel="stylesheet">
  <style>
    body {
      padding-top: 60px; /* 60px to make the container go all the way to the bottom of the topbar */
    }
  </style>
</head>
<body>
  <div class="navbar navbar-inverse navbar-fixed-top">
    <div class="navbar-inner">
      <div class="container">
        <a class="brand" href="#">CommandManager.js</a>
      </div>
    </div>
  </div>
  <div class="container">
    <button id="addNumbers" class="btn">Add numbers</button>
    <button id="addText" class="btn">Add text</button>
    <button id="redo" class="btn">Redo</button>
    <button id="undo" class="btn">Undo</button>
    <div style="margin:1em 0;">
      <pre id="text"></pre>
    </div>
  </div>

  <script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
  <script src="../CommandManager.js"></script>
  <script type="text/javascript">
  $(function(){
    $("#addNumbers").click(function(){
        CommandManager.execute({
          execute: function(){
            $("#text").append("\n0123456789");
          },
          unexecute: function(){
            //t.split("\n").slice(0,2)
            var arr = $("#text").html().split("\n");
            var html = arr.slice(0,arr.length-1).join("\n");
            $("#text").html(html);
          }
        });
    });
    $("#addText").click(function(){
        CommandManager.execute({
          execute: function(){
            $("#text").append("\ntext from command");
          },
          unexecute: function(){
            var arr = $("#text").html().split("\n");
            var html = arr.slice(0,arr.length-1).join("\n");
            $("#text").html(html);
          }
        });
    });

    $("#redo").click(function(){
      CommandManager.redo();
    });

    $("#undo").click(function(){
      CommandManager.undo();
    });
  });
  </script>
</body>
</html>

例(その2)

wysiwygエディタのCKEditorのpluginにundoがあるので、こちらも参考になるかもしれません。pluginのsrcは読んでおらず、command patternでもないかもしれませんが
http://ckeditor.com/addon/undo