Como devo lidar com uma situação em que preciso armazenar vários tipos não relacionados, mas fornecer tipos específicos sob demanda?

StackOverflow https://stackoverflow.com/questions/54219

  •  09-06-2019
  •  | 
  •  

Pergunta

Estou trabalhando em um editor para arquivos que são usados ​​por uma importante ferramenta de testes internos que usamos.A ferramenta em si é grande, complicada e refatorar ou reescrever exigiria mais recursos do que somos capazes de dedicar a ela no futuro próximo, por isso estou de mãos atadas quando se trata de grandes modificações.Devo usar uma linguagem .NET.

Os arquivos são versões XML serializadas de quatro classes usadas pela ferramenta (vamos chamá-las de A, B, C e D).As classes formam uma estrutura em árvore quando tudo está bem.Nosso editor funciona carregando um conjunto de arquivos, desserializando-os, elaborando os relacionamentos entre eles e monitorando quaisquer estados incorretos que puder encontrar.A ideia é deixarmos de editar manualmente esses arquivos, o que introduz muitos erros.

Para um tipo específico de erro, gostaria de manter uma coleção de todos os arquivos que apresentam o problema.Todas as quatro classes podem ter o problema e eu gostaria de reduzir ao máximo a duplicação de código.Um requisito importante é que o usuário seja capaz de obter os itens em conjuntos;por exemplo, eles precisam obter todos os objetos A com um erro, e dizer-lhes para iterar em toda a coleção e escolher o que desejam é inaceitável em comparação com um GetAs() método.Então, meu primeiro pensamento foi fazer um item genérico que relacionasse o objeto desserializado e alguns metadados para indicar o erro:

public class ErrorItem<T>
{
    public T Item { get; set; }
    public Metadata Metadata { get; set; }
}

Então, eu teria uma classe de coleção que poderia conter todos os itens de erro, com métodos auxiliares para extrair os itens de uma classe específica quando o usuário precisar deles.É aqui que o problema começa.

Nenhuma das classes herda de um ancestral comum (exceto Object).Provavelmente isso foi um erro do design inicial, mas passei alguns dias pensando sobre isso e as classes realmente não têm muito em comum além de uma propriedade GUID que identifica exclusivamente cada item para que eu possa ver por que o designer original não os relacionou por herança.Isso significa que a coleção unificada de erros precisaria armazenar ErrorItem<Object> objetos, já que não tenho uma classe base ou interface para restringir o que entra.No entanto, isso torna a ideia desta coleção unificada um pouco vaga para mim:

Public Class ErrorCollection
{
    public ErrorItem<Object> AllItems { get; set; }
}

No entanto, isso tem consequências na interface pública.O que eu realmente quero é devolver o apropriado ErrorItem tipo genérico como este:

public ErrorItem<A>[] GetA()

Isso é impossível porque só posso armazenar ErrorItem<Object>!Repassei algumas soluções alternativas em minha cabeça;principalmente eles incluem a criação de um novo ErrorItem do tipo apropriado na hora, mas parece meio feio.Outro pensamento tem sido usar um Dictionary para manter os itens organizados por tipo, mas ainda não parece certo.

Existe algum tipo de padrão que pode me ajudar aqui?Eu sei que a maneira mais fácil de resolver isso é adicionar uma classe base da qual A, B, C e D derivam, mas estou tentando causar o menor impacto possível na ferramenta original.O custo de qualquer solução alternativa é grande o suficiente para que eu deva forçar a alteração da ferramenta inicial?

Foi útil?

Solução

Se A, B, C e D não têm nada em comum, adicionar uma classe base não trará nada.Será apenas uma classe vazia e, na verdade, será igual a um objeto.

Eu apenas criaria uma classe ErrorItem sem os genéricos, tornaria Item um objeto e faria alguma conversão quando quiser usar os objetos referenciados.Se você quiser usar qualquer uma das propriedades ou métodos da classe A, B, C ou D diferente do Guid, você terá que lançá-los de qualquer maneira.

Outras dicas

É isso que você está procurando?

private List<ErrorItem<object>> _allObjects = new List<ErrorItem<object>>();

public IEnumerable<ErrorItem<A>> ItemsOfA
{
    get
    {
        foreach (ErrorItem<object> obj in _allObjects)
        {
            if (obj.Item is A)
                yield return new ErrorItem<A>((A)obj.Item, obj.MetaData);
        }
    }
}

Se você quiser armazenar em cache o ItemsOfA você pode fazer isso facilmente:

private List<ErrorItem<A>> _itemsOfA = null;

public IEnumerable<ErrorItem<A>> ItemsOfACached
{
    if (_itemsOfA == null)
        _itemsOfA = new List<ErrorItem<A>>(ItemsOfA);
    return _itemsOfA;
}

A resposta que estou apresentando até agora é uma combinação das respostas de Fryguybob e Mendelt Siebenga.

Adicionar uma classe base apenas poluiria o namespace e introduziria um problema semelhante, como apontou Mendelt Siebenga.Eu teria mais controle sobre quais itens podem entrar na coleção, mas ainda precisaria armazenar ErrorItem<BaseClass> e ainda fazer algumas conversões, então eu teria um problema um pouco diferente com a mesma causa raiz.É por isso que selecionei a postagem como resposta:ele ressalta que terei que fazer algumas conversões de qualquer maneira, e o KISS ditaria que a classe base extra e os genéricos são demais.

Gosto da resposta de Fryguybob não pela solução em si, mas por me lembrar yield return, o que tornará mais fácil escrever uma versão sem cache (eu usaria LINQ).Acho que uma versão em cache é um pouco mais sábia, embora os parâmetros de desempenho esperados não tornem a versão sem cache visivelmente mais lenta.

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