문제

저는 몇 달 전에 시작한 Java에서 소규모 UML 편집자 프로젝트를 진행하고 있습니다. 몇 주 후, UML 클래스 다이어그램 편집기를위한 작업 사본을 받았습니다.

그러나 이제는 시퀀스, 상태, 클래스 등의 다른 유형의 다이어그램을 지원하기 위해 완전히 재 설계하고 있습니다. 이것은 그래프 구성 프레임 워크를 구현하여 수행됩니다 (Cay Horstmann은 바이올렛 UML 편집자).

내 친구 중 한 명이 프로젝트에 DO/UNDO 기능을 추가하는 것을 잊어 버릴 때까지 재 설계가 순조롭게 진행되었습니다.

객체 지향 디자인 코스를 기억하면서 나는 즉시 Memento와 명령 패턴을 생각했습니다.

여기에 거래가 있습니다. 초록 클래스 인 AbstractDiagram은 두 개의 ArrayList를 포함합니다. 하나는 노드 (내 프로젝트의 요소라고 함)와 가장자리를 저장하는 데 있어서는 노드를 저장하는 것 (프로젝트에서 링크라고 함)을 포함합니다. 다이어그램은 아마도 실행 취소/재조정 할 수있는 명령 스택을 유지할 것입니다. 꽤 표준.

이러한 명령을 효율적으로 어떻게 실행할 수 있습니까? 예를 들어, 노드를 움직이고 싶다고 가정합니다 (노드는 Inode라는 인터페이스 유형이되며 IT에서 파생 된 콘크리트 노드가 있습니다 (ClassNode, InterfaceNode, NotEnode 등).

위치 정보는 노드에서 속성으로 유지되므로 노드 자체에 속성을 모으게함으로써 상태가 변경됩니다. 디스플레이가 새로 고침되면 노드가 이동했습니다. 이것은 패턴의 기념품 부분이며, 대상이 상태 자체라는 차이가 있습니다.

또한 원래 노드의 클론을 유지하면 (이동하기 전에) 이전 버전으로 돌아갈 수 있습니다. 노드에 포함 된 정보 (클래스 또는 인터페이스 이름, 노트 노드의 텍스트, 속성 이름 등)에 동일한 기술이 적용됩니다.

문제는 다이어그램에서 노드를 실행 취소/레디 작업시 클론으로 어떻게 교체합니까? 다이어그램 (노드 목록에 있음)에서 참조 된 원래 객체를 복제하면 클론은 다이어그램에서 참조되지 않으며, 지적하는 유일한 것은 명령 자체입니다! Shoud I Diagram에 ID에 따라 노드를 찾기위한 메커니즘을 포함하여 (예를 들어) 다이어그램에서 클론으로 노드를 교체 할 수 있습니까? 그렇게하는 것이 기념품과 명령 패턴에 달려 있습니까? 링크는 어떻습니까? 그것들도 움직일 수 있어야하지만 링크에 대한 명령을 만들고 싶지는 않으며 (그리고 노드에 대한 것만) 명령의 유형에 따라 올바른 목록 (노드 또는 링크)을 수정할 수 있어야합니다. 언급하고 있습니다.

어떻게 진행 하시겠습니까? 요컨대, 객체의 상태를 명령/메모 토 패턴으로 표현하는 데 어려움을 겪고있어 효율적으로 복구하고 원래 객체를 다이어그램 목록에서 복원 할 수 있고 객체 유형 (노드 또는 링크)에 따라 복원 할 수 있습니다.

정말 감사합니다!

기 illa.

추신 : 내가 명확하지 않다면, 말해 주시면 내 메시지를 명확히하겠습니다 (언제나처럼!).

편집하다

이 질문을 게시하기 전에 구현을 시작한 실제 솔루션은 다음과 같습니다.

먼저 다음으로 정의 된 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;
    }
}

또한 MopeemoveCommand가 있습니다 (객체/노드를 이동하거나 제거). 인스턴스의 ID를 사용하는 경우 다이어그램을 실제 노드 나 링크로 전달하여 다이어그램에서 자체적으로 제거 할 수 있도록 다이어그램을 전달할 필요가 없습니다 (이는 생각이 좋지 않습니다).

AbstractDiagram 다이어그램; 중독 된 obj; 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;
        }
    }
}

마지막으로, 노드 또는 링크의 정보 (클래스 이름 등)의 정보를 수정하는 데 사용되는 수정 관리 명령이 있습니다. 이것은 미래에 MoveCommand와 병합 될 수 있습니다. 이 수업은 지금 비어 있습니다. 수정 된 객체가 노드 또는 모서리인지 (인스턴스의 인스턴스 또는 ID의 특수한)인지 결정하기 위해 메커니즘으로 ID를 수행 할 것입니다.

이것이 좋은 솔루션입니까?

도움이 되었습니까?

해결책

문제를 더 작은 문제로 분해해야한다고 생각합니다.

첫 번째 문제 : Q : Memento/Command 패턴으로 앱의 단계를 표현하는 방법은 무엇입니까? 우선, 나는 당신의 앱이 어떻게 작동하는지 정확히 전혀 모르지만 당신은 내가 이것으로 어디로 가는지 알기를 바랍니다. 다음 속성과 함께 다이어그램에 클래스 노드를 배치하고 싶다고 말합니다.

{ width:100, height:50, position:(10,25), content:"Am I certain?", edge-connections:null}

그것은 명령 개체로 마무리 될 것입니다. DiagramController로 이동한다고 말합니다. 그런 다음 다이어그램 컨트롤러의 책임은 해당 명령 (스택에 푸시가 내 베팅이 될 것임)을 기록하고 명령을 DiagramBuilder로 전달하는 것입니다. DiagramBuilder는 실제로 디스플레이 업데이트를 담당합니다.

DiagramController
{
  public DiagramController(diagramBuilder:DiagramBuilder)
  {
    this._diagramBuilder = diagramBuilder;
    this._commandStack = new Stack();
  }

  public void Add(node:ConditionalNode)
  {
    this._commandStack.push(node);
    this._diagramBuilder.Draw(node);
  }

  public void Undo()
  {
    var node = this._commandStack.pop();
    this._diagramBuilderUndraw(node);
  }
}

그런 일은 그것을해야하며 물론 분류 할 수있는 많은 세부 사항이있을 것입니다. 그건 그렇고, 노드가 더 자세한 속성을 가질수록 더 자세한 undraw가 필요할 것입니다.

ID를 사용하여 스택의 명령을 그린 요소에 연결하는 것이 좋습니다. 다음과 같이 보일 수 있습니다.

DiagramController
{
  public DiagramController(diagramBuilder:DiagramBuilder)
  {
    this._diagramBuilder = diagramBuilder;
    this._commandStack = new Stack();
  }

  public void Add(node:ConditionalNode)
  {
    string graphicalRefId = this._diagramBuilder.Draw(node);
    var nodePair = new KeyValuePair<string, ConditionalNode> (graphicalRefId, node);
    this._commandStack.push(nodePair);
  }

  public void Undo()
  {
    var nodePair = this._commandStack.pop();
    this._diagramBuilderUndraw(nodePair.Key);
  }
} 

이 시점에서 당신은 절대적으로 아닙니다 있어야합니다 ID가 있기 때문에 객체이지만 Redo 기능도 구현하기로 결정하면 도움이됩니다. 노드에 대한 ID를 생성하는 좋은 방법은 해시 코드가 동일하게 발생하는 방식으로 노드를 복제하지 않아야한다는 사실을 제외하고는 해시 코드 메소드를 구현하는 것입니다.

문제의 다음 부분은 DiagramBuilder 내에 있습니다. 왜냐하면이 명령을 어떻게 처리하는지 알아 내려고 노력하고 있기 때문입니다. 이를 위해 내가 말할 수있는 것은 실제로 추가 할 수있는 각 유형의 구성 요소에 대한 역 동작을 만들 수 있도록하는 것입니다. Delinking을 처리하려면 Edge-Connection 속성 (코드의 링크)을 살펴보고 각 에지 연결이 특정 노드에서 분리 될 것을 알릴 수 있습니다. 나는 단절시 그들이 적절하게 스스로를 다시 그리을 수 있다고 가정합니다.

요약하면, 스택에서 노드에 대한 참조를 유지하지 않고 그 시점에서 주어진 노드의 상태를 나타내는 일종의 토큰을 사용하는 것이 좋습니다. 이렇게하면 동일한 객체를 참조하지 않고도 여러 장소에서 실행 취소 스택에서 동일한 노드를 나타낼 수 있습니다.

Q가있는 경우 게시하십시오. 이것은 복잡한 문제입니다.

다른 팁

내 겸손한 견해로는, 당신은 그것을 실제보다 더 복잡한 방식으로 생각하고 있습니다. 이전 상태로 되돌리기 위해 전체 노드의 클론은 전혀 필요하지 않습니다. 오히려 각각 **명령 클래스는 -

  1. 작동중인 노드에 대한 참조
  2. memento 객체 (노드가 되돌릴만큼 충분한 상태 변수가 있음)
  3. execute () 메소드
  4. undo () 메소드.

명령 클래스는 노드를 참조하기 때문에 다이어그램에서 객체를 참조하기 위해 ID 메커니즘이 필요하지 않습니다.

귀하의 질문의 예에서, 우리는 노드를 새로운 위치로 옮기려고합니다. 이를 위해서는 NodePositionChangeCommand 클래스가 있습니다.

public class NodePositionChangeCommand {
    // This command will act upon this node
    private Node node;

    // Old state is stored here
    private NodePositionMemento previousNodePosition;

    NodePositionChangeCommand(Node node) {
        this.node = node;
    }

    public void execute(NodePositionMemento newPosition) {
        // Save current state in memento object previousNodePosition

        // Act upon this.node
    }

    public void undo() {
        // Update this.node object with values from this.previousNodePosition
    }
}

링크는 어떻습니까? 그것들도 움직일 수 있어야하지만 링크에 대한 명령을 만들고 싶지는 않습니다.

나는 GOF 책 (Memento Pattern 토론에서)에서 노드의 위치 변화와의 링크가 어떤 종류의 제약 솔버에 의해 처리된다는 것을 읽었습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top