Pergunta

Eu estou escrevendo um estruturais ferramenta de modelagem para um civil, enginering aplicação.Eu tenho uma enorme classe de modelo que representa todo o edifício, que incluem conjuntos de nós, linha de elementos, cargas, etc.que também são classes personalizadas.

Eu já codificado um desfazer o motor, que guarda um profundo cópia depois de cada modificação para o modelo.Agora eu comecei a pensar se eu poderia ter codificado de forma diferente.Em vez de guardar o profundo cópias, eu talvez pudesse salvar uma lista de cada modificador de ação com um correspondente inversa modificador.Para que eu pudesse aplicar a inversa de modificadores para o modelo atual para desfazer, ou os modificadores para refazer.

Posso imaginar como você gostaria de realizar comandos simples que alterar as propriedades do objeto, etc.Mas como sobre os comandos complexos?Como inserir um novo nó de objetos para o modelo e adicionando alguns objetos de linha que manter as referências para os novos nós.

Como seria um ir sobre como implementar isso?

Foi útil?

Solução

A maioria dos exemplos que eu vi usar uma variante do Comando-Padrão para este.Cada usuário-ação é reversível obtém a sua própria instância de comando com todas as informações para executar a ação e revertê-lo.Em seguida, você pode manter uma lista de todos os comandos que foram executados e você pode revertê-las uma por uma.

Outras dicas

Eu penso que a lembrança e o comando não é prático quando você está lidando com um modelo de tamanho e escopo que o OP implica.Eles funcionam, mas seria muito trabalho para manter e ampliar.

Para este tipo de problema, eu acho que você precisa para construir em suporte para o seu modelo de dados para suporte do diferencial de pontos de verificação para cada objeto envolvidos no modelo.Eu já fiz isso uma vez e funcionou muito liso.A maior coisa que você tem a fazer é evitar o uso direto de ponteiros ou referências no modelo.

Cada referência para outro objeto, usa alguns identificador (como um número inteiro).Sempre que o objeto for necessário, você pesquisa a definição atual do objeto a partir de uma tabela.A tabela contém uma lista ligada para cada objeto que contém todas as versões anteriores, juntamente com informações sobre a checkpoint eles estavam ativas para.

A implementação de desfazer/refazer é simples:Faça a sua acção e estabelecer um novo ponto de verificação;anulação de todos os objetos de versões para o ponto de verificação anterior.

Leva um pouco de disciplina no código, mas tem muitas vantagens:você não precisa de um profundo cópias desde que você estão fazendo o diferencial de armazenamento do modelo de estado;você pode direcionar a quantidade de memória que você quer usar (muito importante para coisas como modelos CAD) pelo número de repetições ou de memória utilizada;muito escalável e de baixa manutenção para as funções que operam sobre o modelo, já que eles não precisam fazer nada para implementar desfazer/refazer.

Se você está falando GoF, o Memento padrão especificamente endereços de desfazer.

Como outros têm dito, o comando padrão é um método muito poderoso de implementação de Desfazer/Refazer.Mas não há vantagem importante, eu gostaria de mencionar para o padrão de comando.

Quando a implementação de desfazer/refazer usando o comando padrão, você pode evitar grandes quantidades de código duplicado pela abstração (um grau) as operações realizadas sobre os dados e utilizar as operações de desfazer/refazer sistema.Por exemplo, em um editor de texto, cortar e colar são complementares comandos (além do gerenciamento da área de transferência).Em outras palavras, a operação de desfazer para um corte é colar e desfazer a operação para um colar é cortado.Isso se aplica a muito mais simples operações como escrever e apagar texto.

A chave aqui é que você pode utilizar a sua desfazer/refazer sistema como o principal sistema de comando de seu editor.Em vez de escrever o sistema como "criar desfazer objeto, modificar o documento" você pode "criar desfazer objeto, executar operação de refazer em desfazer objeto para modificar o documento".

Agora, sem dúvida, muitas pessoas estão pensando para si mesmos "Bem, duh, não parte do ponto de que o comando padrão?" Sim, mas já vi muitos sistemas de comando que temos dois conjuntos de comandos, um para imediata de operações e outro conjunto para desfazer/refazer.Eu não estou dizendo que não haverá comandos que são específicos para imediata de operações e de desfazer/refazer, mas reduzir a duplicação vai tornar o código mais fácil de manter.

Você pode querer referir-se a Paint.NET código para sua desfazer - eles têm um bom sistema para desfazer.É provavelmente um pouco mais simples do que o que você precisa, mas pode dar-lhe algumas ideias e diretrizes.

-Adam

Este pode ser um caso onde CSLA é aplicável.Ele foi projetado para fornecer complexo desfazer suporte para objetos em aplicações Windows Forms.

Eu tenho implementado complexo desfazer sistemas com êxito usando o padrão Memento - muito fácil, e tem a vantagem de, naturalmente, fornecer uma Refazer o quadro também.Um mais sutil vantagem é que a agregação de acções pode estar contido dentro de um único Desfazer também.

Em poucas palavras, você tem duas pilhas de memento objetos.Um para Desfazer, e o outro para Refazer.Cada operação que cria uma nova lembrança, que, idealmente, serão algumas chamadas para alterar o estado do modelo, documento (ou qualquer outra coisa).Esta é adicionado à pilha de desfazer.Quando você fizer uma cópia de segurança de operação, além de executar a ação para Anular o Memento objeto para alterar o modelo de volta, você também pop o objeto fora a pilha de Desfazer e empurre-a para a direita para a Refazer pilha.

Como o método para alterar o estado do documento é implementado depende completamente de sua implementação.Se você pode simplesmente fazer uma chamada de API (por exemplo,ChangeColour(r,g,b)) e, em seguida, precedê-lo com uma consulta para obter e guardar o estado correspondente.Mas o padrão também vai apoiar a tomada de profunda cópias, memória instantâneos, temp criação do arquivo, etc. - é tudo para você como ele é, é simplesmente um método virtual de implementação.

Para fazer a agregação de acções (por exemplo,usuário Shift-Seleciona uma carga de objetos para fazer uma operação, como por exemplo, excluir, renomear, alterar o atributo), o código cria uma nova pilha de Desfazer como uma simples lembrança, e passa-o para a operação real para adicionar o indivíduo operações.Assim, seus métodos de ação não precisa (um) global pilha de se preocupar e (b) pode ser codificado o mesmo se eles são executados isoladamente ou como parte de um agregado de operação.

Muitos desfazer sistemas estão na memória apenas, mas você pode manter a pilha de desfazer, se você quiser, eu acho.

Acabado de ler sobre o padrão de comando em minha desenvolvimento ágil livro - talvez seja o que tem potencial?

Você pode ter todos os comandos implementar a interface de comando (que tem um método Execute ()).Se você quiser desfazer, você pode adicionar um método de cópia de segurança.

mais informações aqui

Eu estou com Mendelt Siebenga sobre o fato de que você deve usar o Comando Padrão.O padrão utilizado foi o Memento Padrão, que pode e vai se tornar muito desperdício ao longo do tempo.

Uma vez que você está trabalhando em um intensivo de memória de aplicativo, você deve ser capaz de especificar o quanto de memória o desfazer do motor é permitido levar até, como muitos níveis de desfazer estão salvos ou alguns de armazenamento para o qual devem ser persistentes.Você não deve fazer isso, você logo irá se deparar com erros resultantes de uma máquina de memória.

Eu aconselho você a verificar se há um quadro que já criou um modelo para desfazer a linguagem de programação / quadro de sua escolha.É bom para inventar coisas novas, mas é melhor tomar algo já escrito, depurado e testado em cenários reais.Ajudaria se você adicionou o que você está escrevendo isso, então as pessoas podem recomendar os quadros que eles conhecem.

Codeplex projeto:

É uma framework simples para adicionar Desfazer/Refazer a funcionalidade de seus aplicativos, com base no clássico de design de Comando padrão.Ele suporta mesclagem de ações, transações aninhadas, atraso de execução (execução em nível superior de confirmação de transação) e a possível não-linear desfazer a história (onde você pode ter uma escolha de várias ações para refazer).

A maioria dos exemplos que eu li fazê-lo usando o comando ou memento padrão.Mas você pode fazê-lo sem padrões de design também com um simples deque-estrutura.

Uma maneira inteligente de lidar desfazer, o que tornaria o software também adequado para multi-usuário de colaboração, é a implementação de um transformação operacional da estrutura de dados.

Este conceito não é muito popular, mas bem definida e útil.Se a definição parece muito abstrato para você, este projeto é um exemplo bem sucedido de como uma transformação operacional para objetos JSON é definido e implementado em Javascript

Para referência, aqui está uma implementação simples do padrão de Comando para Desfazer/Refazer em C#: Simples de desfazer/refazer sistema para C#.

Nós reutilizado o arquivo carregar e guardar código de serialização para "objetos" de uma forma conveniente para salvar e restaurar o estado de um objeto.Nós empurrar os objetos serializados sobre a pilha de desfazer – juntamente com algumas informações sobre o que a operação foi realizada e dicas sobre o desfazer-ing, que a operação se não houver número suficiente de informações obtidas a partir de dados serializados.Desfazer e Refazer muitas vezes é apenas a substituição de um objeto com outro (em teoria).

Tem havido muitos e muitos bugs devido à ponteiros (C++) para objetos que nunca foram corrigidos, como você realizar algumas ímpar desfazer refazer sequências (esses lugares não são atualizados para a segurança de desfazer consciente "identificadores").Bugs nesta área, muitas vezes ...ummm...interessantes.

Algumas operações podem ser casos especiais para velocidade/utilização de recursos - como o dimensionamento coisas, mover as coisas ao redor.

Multi-seleção fornece algumas interessantes complicações bem.Luckly nós já tivemos um agrupamento conceito no código.Kristopher Johnson comentário sobre a sub-itens é muito próximo do que fazemos.

Eu tive que fazer isso ao escrever um solver para um peg-salto jogo de quebra-cabeça.Eu fiz cada movimento de um objeto de Comando que realizou o suficiente de informações que poderia ser feito ou desfeito.No meu caso isso foi tão simples como armazenar a posição inicial e a direção de cada movimento.Eu, então, armazenados todos esses objetos em uma pilha de modo que o programa poderia facilmente desfazer como muitos movimentos como necessária ao retrocesso.

Você pode tentar pronto-a-implementação de Desfazer/Refazer padrão em PostSharp. https://www.postsharp.net/model/undo-redo

Ele permite que você adicione desfazer/refazer funcionalidade para o seu aplicativo sem implementar o padrão de si mesmo.Ele usa de gravação padrão para rastrear as alterações em seu modelo e trabalha com INotifyPropertyChanged padrão que também é implementada no PostSharp.

Você está equipado com controles de INTERFACE de usuário e você pode decidir qual é o nome e a granularidade de cada operação será.

Certa vez, trabalhei em uma aplicação em que todas as alterações feitas por um comando para a aplicação do modelo (i.e.CDocument...nós estavam utilizando MFC) foram persistentes no final do comando de atualização de campos em um banco de dados interno mantida no modelo.Então, nós não temos que escrever separado de desfazer/refazer código para cada ação.A pilha de desfazer simplesmente lembrei do chaves primárias, nomes de campos e valores antigos, cada vez que um registro foi alterado (no final de cada comando).

A primeira seção de Padrões de Projeto (GoF, 1994) tem um caso de uso para a implementação de desfazer/refazer como um padrão de design.

Você pode fazer a sua idéia inicial alto desempenho.

Utilização estruturas de dados persistentes, e ficar com a manutenção de um lista de referências ao antigo estado cerca de.(Mas que realmente só funciona se as operações de todos os dados em seu estado de classe são imutáveis, e todas as operações de retorno de uma nova versão---mas a nova versão não precisa ser uma cópia profunda, apenas substituir as peças alteradas "copy-on-write'.)

Eu encontrei o Comando padrão a ser muito útil aqui.Em vez de implementar várias inversa comandos, estou usando uma reversão com um atraso de execução em uma segunda instância do meu API.

Esta abordagem parece razoável se você quiser baixo esforço de implementação e de fácil manutenção (e pode pagar o extra de memória para a 2ª instância).

Veja aqui um exemplo:https://github.com/thilo20/Undo/

Eu não sei se isso vai ser de alguma utilidade para você, mas quando eu tinha que fazer algo semelhante em um dos meus projetos, eu acabei de baixar UndoEngine de http://www.undomadeeasy.com - um maravilhoso mecanismo e eu realmente não me importo muito sobre o que estava sob o capô, ele só trabalhou.

Na minha opinião, o de DESFAZER/REFAZER poderia ser implementado em 2 formas amplamente.1.Nível de comando (chamado de nível de comando Undo/Redo) 2.Nível do documento (chamado global de Desfazer/Refazer)

Nível de comando:Como muitas respostas apontam, esta é obtida com eficiência usando o Memento padrão.Se o comando também oferece suporte diário a ação, um refazer é facilmente suportada.

Limitação:Uma vez que o escopo do comando está fora, o de desfazer/refazer é impossível, o que leva a nível de documento(global) de desfazer/refazer

Eu acho que o seu caso seria se encaixam no global desfazer/refazer, pois é adequado para um modelo que envolve um monte de espaço de memória.Além disso, esta é adequado para seletivamente desfazer/refazer também.Existem dois tipos primitivos

  1. Toda a memória de desfazer/refazer
  2. A nível de objeto Desfazer Refazer

Em "Toda a memória de Desfazer/Refazer", toda a memória é tratada como uma conexão de dados (como uma árvore, ou uma lista ou gráfico) e a memória é gerenciada pela aplicação, em vez de SO.Tão novo e excluir operadores em C++ são sobrecarregadas para conter mais estruturas específicas para implementar eficazmente as operações como um.Se nenhum nó é modificado, b.holding e limpeza de dados, etc., O funcionamento é basicamente copiar a memória inteira(supondo-se que a alocação de memória já está otimizada e gerenciadas pelo aplicativo usando algoritmos avançados) e armazená-lo em uma pilha.Se a cópia de memória é solicitada, a estrutura de árvore é copiado com base na necessidade de se ter um rasos ou profundos cópia.Uma cópia profunda é feita apenas para a variável em que é modificado.Uma vez que cada variável é alocada por meio de alocação personalizados, o aplicativo tem a palavra final quando excluí-lo, se necessário.As coisas se tornam muito interessante, se tivermos a partição de Desfazer/Refazer quando acontece que precisamos através de programação-seletivamente Desfazer/Refazer um conjunto de operação.Neste caso, apenas as novas variáveis ou excluído variáveis ou variáveis modificadas são dadas um sinalizador para que Desfazer/Refazer apenas desfaz/refazer aqueles memória As coisas tornam-se ainda mais interessante se precisarmos fazer um parcial de Desfazer/Refazer dentro de um objeto.Quando tal é o caso, uma nova idéia de "Visitante" padrão é usado.Ele é chamado de "Nível de Objeto de Desfazer/refazer"

  1. Nível de objeto de Desfazer/Refazer:Quando a notificação de desfazer/refazer é chamado, cada objeto implementa uma transmissão de operação em que, o gerador recebe a partir do objeto, os dados antigos/novos dados que está programado.Os dados que não está a ser perturbado é deixada intacta.Cada objeto é um streamer como argumento e dentro de Desfazer/Refazer chamada, fluxos/unstreams os dados do objeto.

1 e 2 poderia ter métodos, tais como 1.BeforeUndo() 2.AfterUndo() 3.BeforeRedo() 4.AfterRedo().Estes métodos têm de ser publicados na base de Desfazer/refazer Comando ( não contextual de comando), de modo que todos os objetos que implementam esses métodos também para obter uma acção específica.

Uma boa estratégia é criar um híbrido de 1 e 2.A beleza é que esses métodos(1&2) - se usar o comando padrões

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top