「Undo」をどう設計する?

ドローツールの Undo をどうやって実装するといいんだろう?っていうギモンにリプ頂いたのでまとめ。もっと話広げられれば良かったかもなぁ。
40
くっくっkura 🇯🇵 @PG_kura

ドローツールの Undo, Redo ってどういう設計になってるんだろ。

2011-07-14 17:12:55
くっくっkura 🇯🇵 @PG_kura

@cocoatomo memento でやるなら、現在の状態から元の状態へ戻す操作オブジェクト、もしくはデータ全体のスナップショットを復元するオブジェクトのどちらかを直列化して持っておくようなイメージなんですけど、どっちなのかなぁ、もしくはそれ以外の方法あるのかなぁ、とか。

2011-07-14 17:27:49
tomo🐧@learning @cocoatomo

@PG_kura 差分を持っているんだと思ってます。じゃないと Redo 何していいか分からないですし。

2011-07-14 17:32:41
くっくっkura 🇯🇵 @PG_kura

@cocoatomo 差分にした場合、描画要素同士が互いに参照しあうようなケースではそれらの参照関係の再構築が必要ですよね。(矢印図形が楕円図形の中心を指す、とか。)そーゆーの考えだすと楽しくて帰りたくなるます。

2011-07-14 17:37:27
tomo🐧@learning @cocoatomo

@PG_kura そうですねー。そこは腕の見せ所な気がします。使いやすさを突き詰めて考える作業ですね。帰りますか??www

2011-07-14 17:41:19
pokarim @pokarim

@PG_kura @cocoatomo 照やポインタを生で使わずに、IDをはさむというのはどうでしょうか。さらに参照関係をオブジェクトの外にだしたらすっきりするかも。ほとんどDBですな。適当に言ってますが。

2011-07-14 17:50:57
pokarim @pokarim

@PG_kura @cocoatomo 勘ですが、参照関係の再構築といったあたりからOOPの枠に収まりにくくなっているような気がします。

2011-07-14 17:53:03
くっくっkura 🇯🇵 @PG_kura

@pokarim 以前、その方法を採ってうまくいったことがあります。しかし、ID で参照するとなると参照したり外したり、の手間がワンクッション入るので、もっと型でガチガチにしてみたいなぁって欲が出てきて、何か無いかなあと思うです。(って問題を混同し過ぎな感じはあるんですけどねw)

2011-07-14 17:58:27
pokarim @pokarim

@PG_kura RDBMSで中間テーブル使うと柔軟になるけど面倒になるというのにも似ているかもですね。参照関係はHashMap使ったりすると思いますが、そういった生の参照でない参照関係オブジェクト的なものへのサポートを充実させるべきなんじゃないかなと思ってます。

2011-07-14 18:05:09
みやびあーつ @miyabiarts

自分のソフトウェアだと、オブジェクトの内部状態を変えるようなメンバ関数の中で復元に必要なデータを保持するUndo用オブジェクトを作成し、全てのUndo用オブジェクトを管理するオブジェクトにスタックで追加していくという感じ。

2011-07-14 17:55:54
みやびあーつ @miyabiarts

ただ今の状態だとクラス自体にUndo用のコードを書かないといけないので、機能があまり切り分けられていないように見える。そのため、クラスごとにUndo用のクラスを作ってObserverパターンで切り分けて、メンバ関数経由でUndoを実行すれば良いけど、

2011-07-14 18:01:04
みやびあーつ @miyabiarts

メンバ関数の呼び出しが多くなると、かなりオーバヘッドが発生してしまうので、妥協して今の形になっているところ。

2011-07-14 18:02:28
みやびあーつ @miyabiarts

3Dモデリングツールで、1万頂点を持つモデルを全選択して動かすと、動かすたびにUndo用オブジェクトが 1万個生成される。

2011-07-14 18:08:53
みやびあーつ @miyabiarts

操作したい頂点をリスト化してから実行するとUndo用オブジェクトは1つで済むけど、リスト化して実行しないといけないこと課すため面倒くさくなったり、Undoの単位がメンバ関数単位以上になって、別のところで設計が複雑になってしまう。

2011-07-14 18:16:33
pokarim @pokarim

@PG_kura 離脱了解です。属性が基本で参照関係的なものは直接サポートされてないから、いざ必要になったときに面倒になってしまうのかなと。参照関係が基本になっていれば、(ID->オブジェクト)をかませるのは関数合成みたいなもので済むかなとか妄想してみました。

2011-07-14 18:10:24
pokarim @pokarim

@PG_kura OOPでもメソッドたくさん書けば隠蔽できますが、setter getterたくさん書いて、全体の記述量は減ってないというか下手したら増えてしまう、みたいなことになりかねないですよね。。。

2011-07-14 18:17:34
くっくっkura 🇯🇵 @PG_kura

@pokarim setter, getter による記述量の増加は横に置いておくとして、( ID -> オブジェクト ) をかませるのは必要で、それを関数使ってやるのも良いかと思うんですが、何度もデータプールに対して問い合わせるのもアレだし、一度参照取得できたならそれ使って

2011-07-14 22:49:10
くっくっkura 🇯🇵 @PG_kura

@pokarim ... 型のメリットも最大限活かしたいですし、ってあれこれまさにリアクティブプログラミングの出番!!... とか、考えてましたw

2011-07-14 22:49:56
pokarim @pokarim

@PG_kura インターフェース的には関数として提示されているけど、参照関係の情報がデータプールにあるのか、インスタンスが自分で持っているかの違いを完全に隠蔽したらいいと思うんですよ。

2011-07-14 23:06:52
pokarim @pokarim

@PG_kura undo,redoのようにイベントの履歴が絡んでくる話は、大抵リアクティブプログラミングに持っていけますぜ、旦那!

2011-07-14 23:08:27

■■■■■■■■■■■■■■■■■■■■■■■■■■■
ちなみに、ここで出てくる
"リアクティブプログラミング"っていうのは
コレ。
[[『なぜリアクティブプログラミングは重要か。』
Conceptual Contexture:http://d.hatena.ne.jp/pokarim/20101226]]

以下、トゥギャった後の反応などピックアップ。
■■■■■■■■■■■■■■■■■■■■■■■■■■■

残りを読む(16)

コメント

sesamechang @sesamecake 2011年7月15日
インタプリタかませばおk
0
きゃっつ(Kats)⊿ @grayengineer 2011年7月15日
8bit時代のドローツールは、何か操作するたびに全手順を最初から繰り返してた(当時は描画速度が遅かったから目で見てそれがわかった)なんてこともあったなー
0
BugbearR @BugbearR 2011年7月15日
フルバックアップをときどき取って、そこからの差分バックアップを取るとすれば、差分を最初から全部適用というのはなくなりますね。あとは差分を逆操作にするか、順操作にするかの問題。
0
◇KITIKETAO◇キチケタオ◇ @ktikto 2011年7月15日
これ読んでみて、VOCALOID3 Editorの(un|re)do対応って相当苦労したんだろうな~と思ったり…
0
アニメ大好きハゲおじさん @Crimson_Apple 2011年7月15日
MementoでFA思った僕はまだまだでした。ごめんなさい。
0
孤月 @kogetsu 2011年7月15日
ドローツールじゃないけど、僕がUndoを実装したときは、コマンドパターンつかって、実行用処理と、Undo用の処理を書いて、そのスタックをずっと持ち続けてる感じだった。一個処理が増えるたびにコマンド用のクラス作らないといけなかったから割と大変だったけどそこそこうまくいった。
0
順三朗 @junzabroP 2011年7月17日
通常はCommand/Mementoパターンを適用すれば済む話(楽ではないが)。すると、コマンド化した作業履歴のシリアライズもやりたくなる。やった結果がお絵かきチャット(掲示板)やOpenCanvasなど。
0
ゆんゆん探偵 @yunyundetective 2011年7月18日
動的型付けのスクリプト言語だと、データ型の拡張がイージーなため、「任意のオペレーションに対するUNDO/REDOを行なうデータ」を追加していくのがすごい楽なんだぜ。
0
じん @jin1016 2011年7月18日
操作(コマンド)ごとにクラス作って、Undo/Redoメソッド持って、それをスタックに積んでいく、Redo用にRedoスタックも準備して、UndoされたらRedoスタックに積む、RedoされたらUndoスタックに戻す、Undo後別コマンド実行されたらRedoスタッククリアしてコマンドをUndoスタックに積む、みたいなことやってた。
0
じん @jin1016 2011年7月18日
ポインタ、参照で持つことは出来ないので、各オブジェクトにはIDを与えて、IDで管理もしてた。
0