私は、数か月前に開始したJavaの小さなUMLエディタープロジェクトに取り組んでいます。数週間後、UMLクラス図エディターの作業用コピーを入手しました。
しかし今、私はそれを完全に再設計して、シーケンス、状態、クラスなどの他のタイプの図をサポートしています。これは、グラフ構築フレームワークを実装することによって行われます(私は、この主題に関するCayHorstmannの作業に大きく影響を受けています。バイオレットUMLエディター)。
友人の1人が、プロジェクトにDo / Undo機能を追加するのを忘れたと言うまで、再設計は順調に進んでいました。これは、私の意見では非常に重要です。
オブジェクト指向のデザインコースを思い出して、すぐにMementoとCommandパターンについて考えました。
これが取引です。2つのArrayListを含む抽象クラスAbstractDiagramがあります。1つはノード(プロジェクトではElementsと呼ばれます)を格納するためのもので、もう1つはEdges(プロジェクトではLinksと呼ばれる)を格納するためのものです。この図は、おそらく、元に戻す/やり直すことができるコマンドのスタックを保持します。かなり標準的です。
これらのコマンドを効率的に実行するにはどうすればよいですか?たとえば、ノードを移動したいとします(ノードはINodeという名前のインターフェイスタイプになり、そこから派生した具象ノード(ClassNode、InterfaceNode、NoteNodeなど)があります)。
位置情報はノード内の属性として保持されるため、ノード自体でその属性を変更することにより、状態が変更されます。表示が更新されると、ノードが移動します。これはパターンのMemento部分です(私は思います)が、オブジェクトが状態そのものであるという違いがあります。
さらに、元のノードのクローンを(移動する前に)保持すると、古いバージョンに戻すことができます。同じ手法が、ノードに含まれる情報(クラス名またはインターフェース名、ノートノードのテキスト、属性名など)にも適用されます。
問題は、図で、元に/やり直し操作時にノードをそのクローンに置き換えるにはどうすればよいですか?ダイアグラムによって参照されている(ノードリストにある)元のオブジェクトのクローンを作成した場合、そのクローンはダイアグラム内で参照されておらず、ポイントしているのはコマンド自体だけです。シャウド私は、IDに従ってノードを見つけるためのメカニズムを図に含めているので(たとえば)、図でノードをそのクローンに置き換えることができますか(またはその逆)?それを行うのはMementoとCommandパターン次第ですか?リンクはどうですか?それらも移動可能である必要がありますが、リンク専用(およびノード専用)のコマンドを作成したくないので、コマンドのオブジェクトのタイプに応じて適切なリスト(ノードまたはリンク)を変更できる必要がありますを参照しています。
どのように進めますか?つまり、オブジェクトのタイプ(ノードまたはリンク)によっては、オブジェクトの状態をコマンド/ mementoパターンで表現して、オブジェクトを効率的に復元し、元のオブジェクトをダイアグラムリストに復元できるようにするのに問題があります。
どうもありがとう!
ギヨーム。
PS:はっきりしない場合は、教えてください。メッセージを明確にします(いつものように!)。
編集
これが私の実際の解決策であり、この質問を投稿する前に実装を開始しました。
まず、次のように定義されたAbstractCommandクラスがあります。
public abstract class AbstractCommand {
public boolean blnComplete;
public void setComplete(boolean complete) {
this.blnComplete = complete;
}
public boolean isComplete() {
return this.blnComplete;
}
public abstract void execute();
public abstract void unexecute();
}
次に、各タイプのコマンドは、AbstractCommandの具体的な派生を使用して実装されます。
だから私はオブジェクトを移動するコマンドを持っています:
public class MoveCommand extends AbstractCommand {
Moveable movingObject;
Point2D startPos;
Point2D endPos;
public MoveCommand(Point2D start) {
this.startPos = start;
}
public void execute() {
if(this.movingObject != null && this.endPos != null)
this.movingObject.moveTo(this.endPos);
}
public void unexecute() {
if(this.movingObject != null && this.startPos != null)
this.movingObject.moveTo(this.startPos);
}
public void setStart(Point2D start) {
this.startPos = start;
}
public void setEnd(Point2D end) {
this.endPos = end;
}
}
MoveRemoveCommandもあります(オブジェクト/ノードを移動または削除するため)。instanceofメソッドのIDを使用する場合、ダイアグラムを実際のノードまたはリンクに渡して、ダイアグラムからそれ自体を削除できるようにする必要はありません(これは悪い考えだと思います)。
AbstractDiagram図; 追加可能なオブジェクト; AddRemoveTypeタイプ;
@SuppressWarnings("unused")
private AddRemoveCommand() {}
public AddRemoveCommand(AbstractDiagram diagram, Addable obj, AddRemoveType type) {
this.diagram = diagram;
this.obj = obj;
this.type = type;
}
public void execute() {
if(obj != null && diagram != null) {
switch(type) {
case ADD:
this.obj.addToDiagram(diagram);
break;
case REMOVE:
this.obj.removeFromDiagram(diagram);
break;
}
}
}
public void unexecute() {
if(obj != null && diagram != null) {
switch(type) {
case ADD:
this.obj.removeFromDiagram(diagram);
break;
case REMOVE:
this.obj.addToDiagram(diagram);
break;
}
}
}
最後に、ノードまたはリンクの情報(クラス名など)を変更するために使用されるModificationCommandがあります。これは、将来、MoveCommandとマージされる可能性があります。このクラスは今のところ空です。私はおそらく、変更されたオブジェクトがノードであるかエッジであるかを判断するメカニズムを使用してIDを実行します(IDのinstanceofまたは特別な表記を介して)。
これは良い解決策ですか?