Как связать объекты команд с правильным получателем?

9

Я пытался использовать Command Pattern для реализации Undo и Redo в моем проекте

public abstract class Command
{
    protected Form Receiver { set; get; }
    protected HtmlElement Element { set; get; }
    abstract public void ReDo();
    abstract public void UnDo();
    public Command(Form receiver)
    {
        this.Receiver = receiver;
    }
}
class AddElementCmd : Command
{        
    public AddElementCmd(HtmlElement elem, Form receiver)
        : base(receiver)
    {
        Element = elem;
    }
    public override void ReDo()
    {
        ((FormEdit)Receiver).AddElement(Element,false);
    }
    public override void UnDo()
    {
        ((FormEdit)Receiver).DelElement(Element, false);
    }
}
class DelElementCmd : Command
{
    public DelElementCmd(HtmlElement elem, Form receiver)
        : base(receiver)
    {
        Element = elem;
    }
    public override void ReDo()
    {
        ((FormEdit)Receiver).DelElement(Element, false);
    }
    public override void UnDo()
    {
        ((FormEdit)Receiver).AddElement(Element, false);
    }
}

Реализация AddElementкоманды в FormEdit.

public void AddElement(HtmlElement elem, bool isNew = true)
{
    IHTMLElement2 dom = elem.DomElement as IHTMLElement2;
    if (isNew)
    {
        Command cmd = new AddElementCmd(elem, this);
        Undo.Push(cmd);
        Redo.Clear();
    }    
    // some codes here....
    if (showAlltoolStripButton.Checked)
    {
        dom.runtimeStyle.visibility = "hidden";
    }
    else if (showSelectionToolStripButton.Checked)
    {
        dom.runtimeStyle.visibility = "visible";
    }
 }
...

Undoи Redoстеки хранятся в FormMainклассе и передаются в виде редактора.

public Stack<Command> Undo = new Stack<Command>();
public Stack<Command> Redo = new Stack<Command>();

....
FormEdit editor = new FormEdit ();
editor.Browser = webBrowser1;
editor.addedElements = addedElements;
editor.restoreElements = restoreElements;
editor.Undo = Undo;
editor.Redo = Redo;

Когда в новом FormEditпользователь нажимает кнопку «Вернуть» или «Отменить», соответствующая функция в FormEditвыполняется, но, как я уже проверял, этот получатель команды является формой, в которой команда была создана впервые и теперь может быть удалена. Я ожидаю, что программа выдаст ошибку, но кажется, что Commandобъект хранит ссылку на старую форму, и это приводит к неправильному поведению.

Поэтому я считаю, что должен найти согласованный приемник для команд, либо основной формы, либо элемента управления webBrowser, который имеет то же время жизни, что и сами команды. Но все же у меня должен быть доступ к некоторым элементам управления, связанным с командами.

Где лучшее место для реализации командных функций в качестве приемника Commandобъектов? Или любой другой способ связать новую форму с командой, извлеченной из стека.

Ahmad
источник
Я думаю, что это решение за вами. Мы не можем вам помочь, потому что мы не знаем спецификации или функциональных требований вашего приложения.
Эйфорическое
8
Я считаю, что объекты Command должны содержать только сериализуемые данные (т. Е. Не иметь ссылок на другие объекты), поскольку их общее использование включает отправку их сериализованных форм через сети, сохранение их в файл для последующего использования или воспроизведение их на другом приемнике (если вы хотите ваши изменения будут отображаться на моем экране в режиме реального времени, например). Это может означать, что вы хотите передать Receiver каждому методу команды, или, возможно, дать Receiver executeCommand () / undoCommand () методы, которые позволяют ему проходить самостоятельно, или возможно использовать объекты команды, которые содержат только имена / аргументы метода вместо кода ,
Ixrec
@Ixrec Спасибо за ваш совет, тогда вы имеете в виду, что я должен быть в состоянии установить Receiverкаждый объект команды, я собираюсь сделать это.
Ахмад
Попробуйте вместо этого использовать шаблон памяти.
П. Роу

Ответы:

1

Командная модель должна применяться к модели , а не пользовательский интерфейс. В вашем случае сделайте это

protected HtmlDocument Receiver { set; get; }
protected HtmlElement Element { set; get; }

Чтобы обновить пользовательский интерфейс, используйте шаблон Observer , чтобы все открытые формы и их элементы управления могли реагировать на изменения в базовой модели.

Ваш код станет более четким и разделенным, потому что Command может позаботиться только об изменении документа, а наблюдатели в пользовательском интерфейсе должны только обновлять элементы управления, независимо от того, что именно изменилось.

Когда форма закрывается, она отменяет регистрацию в качестве наблюдателя, и ссылки на нее не сохраняются.

Если новая форма открывается после изменения в документе, она будет уведомлена после отмены, даже если ее не было, когда было сделано первоначальное изменение.

апалала
источник