¿Cómo debo manejar una situación en la que necesito almacenar varios tipos no relacionados pero proporcionar tipos específicos a pedido?

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

  •  09-06-2019
  •  | 
  •  

Pregunta

Estoy trabajando en un editor de archivos que utiliza una importante herramienta de prueba interna que utilizamos.La herramienta en sí es grande, complicada y refactorizarla o reescribirla requeriría más recursos de los que podemos dedicarle en el futuro previsible, por lo que tengo las manos atadas cuando se trata de modificaciones importantes.Debo usar un lenguaje .NET.

Los archivos son versiones serializadas XML de cuatro clases que utiliza la herramienta (llamémoslas A, B, C y D).Las clases forman una estructura de árbol cuando todo está bien.Nuestro editor funciona cargando un conjunto de archivos, deseriarizándolos, resolviendo las relaciones entre ellos y realizando un seguimiento de cualquier estado incorrecto que pueda encontrar.La idea es que dejemos de editar manualmente estos archivos, lo que introduce toneladas de errores.

Para un tipo particular de error, me gustaría mantener una colección de todos los archivos que tienen el problema.Las cuatro clases pueden tener el problema y me gustaría reducir la duplicación de código tanto como sea posible.Un requisito importante es que el usuario debe poder obtener los artículos en conjuntos;por ejemplo, necesitan obtener todos los objetos A con un error, y decirles que iteren sobre toda la colección y elijan lo que quieren es inaceptable en comparación con un GetAs() método.Entonces, mi primer pensamiento fue hacer un elemento genérico que relacionara el objeto deserializado y algunos metadatos para indicar el error:

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

Luego, tendría una clase de colección que podría contener todos los elementos de error, con métodos auxiliares para extraer los elementos de una clase específica cuando el usuario los necesite.Aquí es donde empieza el problema.

Ninguna de las clases hereda de un ancestro común (aparte de Object).Probablemente esto fue un error del diseño inicial, pero pasé algunos días pensando en ello y las clases realmente no tienen mucho en común aparte de una propiedad GUID que identifica de forma única cada elemento para que pueda ver por qué el diseñador original no los relacionó por herencia.Esto significa que la colección de errores unificada necesitaría almacenar ErrorItem<Object> objetos, ya que no tengo una clase base o interfaz para restringir lo que entra.Sin embargo, esto hace que la idea de esta colección unificada me resulte un poco vaga:

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

Sin embargo, esto tiene consecuencias en la interfaz pública.Lo que realmente quiero es devolver el apropiado ErrorItem tipo genérico como este:

public ErrorItem<A>[] GetA()

Esto es imposible porque sólo puedo almacenar ErrorItem<Object>!He repasado algunas soluciones en mi cabeza;en su mayoría incluyen la creación de un nuevo ErrorItem del tipo apropiado sobre la marcha, pero se siente un poco feo.Otro pensamiento ha sido usar un Dictionary para mantener los elementos organizados por tipo, pero todavía no parece correcto.

¿Existe algún tipo de patrón que pueda ayudarme aquí?Sé que la forma más sencilla de resolver esto es agregar una clase base de la que derivan A, B, C y D, pero estoy tratando de tener el menor impacto posible en la herramienta original.¿El costo de alguna solución alternativa es lo suficientemente alto como para que deba presionar para cambiar la herramienta inicial?

¿Fue útil?

Solución

Si A, B, C y D no tienen nada en común, agregar una clase base realmente no le brindará nada.Será simplemente una clase vacía y, de hecho, será lo mismo que el objeto.

Simplemente crearía una clase ErrorItem sin los genéricos, convertiría a Item en un objeto y realizaría algunas conversiones cuando quisiera usar los objetos a los que se hace referencia.Si desea utilizar alguna de las propiedades o métodos de la clase A, B, C o D que no sea Guid, habría tenido que convertirlos de todos modos.

Otros consejos

¿Es esto lo que estás buscando?

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);
        }
    }
}

Si desea almacenar en caché el ItemsOfA puedes hacerlo fácilmente:

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

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

La respuesta que voy a seguir hasta ahora es una combinación de las respuestas de fryguybob y Mendelt Siebenga.

Agregar una clase base simplemente contaminaría el espacio de nombres e introduciría un problema similar, como señaló Mendelt Siebenga.Tendría más control sobre qué elementos pueden incluirse en la colección, pero aún necesitaría almacenar ErrorItem<BaseClass> y todavía hago algunas conversiones, por lo que tendría un problema ligeramente diferente con la misma causa raíz.Por eso seleccioné la publicación como respuesta:señala que voy a tener que hacer algunos moldes sin importar qué, y KISS dictaría que la clase base adicional y los genéricos son demasiado.

Me gusta la respuesta de fryguybob no por la solución en sí sino por recordarme yield return, lo que facilitará la escritura de una versión sin caché (iba a usar LINQ).Creo que una versión en caché es un poco más inteligente, aunque los parámetros de rendimiento esperados no harán que la versión sin caché sea notablemente más lenta.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top