C # diseño para un objeto, donde algunas propiedades son caros: excusa para que sea mutable?

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

  •  13-09-2019
  •  | 
  •  

Pregunta

Sí, lo sé, otra pregunta acerca de los objetos mutables. Ver este para el fondo general y esta para el análogo más cercano a mi pregunta 614.875 /. (Aunque tiene connotaciones específicas alguna C ++ que no se aplican aquí)

Vamos a suponer que el siguiente pseudo código representa el mejor Interfaz de diseño. Es decir, que es la expresión más clara de la semántica de negocio (tal y como están en la actualidad) en tipo OO. Naturalmente, el UglyData y las cosas que está encargado de hacer con él están sujetos a cambio incremental.

public class FriendlyWrapper
{
    public FriendlyWrapper(UglyDatum u)
    {
        Foo = u.asdf[0].f[0].o.o;
        Bar = u.barbarbar.ToDooDad();
        Baz = u.uglyNameForBaz;
        // etc
    }

    public Widget Foo { get; private set; }
    public DooDad Bar { get; private set; }
    public DooDad Baz { get; private set; }
    // etc
    public WhizBang Expensive1 { get; private set; }
    public WhizBang Expensive2 { get; private set; }

    public void Calculate()
    {
        Expensive1 = Calc(Foo, Bar);
        Expensive2 = Calc(Foo, Baz);
    }

    private WhizBang Calc(Widget a, DooDad b) { /* stuff */ }

    public override void ToString()
    {
        return string.Format("{0}{1}{2}{3}{4}", Foo, Bar, Baz, Expensive1 ?? "", Expensive2 ?? "");                             
    }
}

// Consumer 1 is happy to work with just the basic wrapped properties
public string Summarize()
{
    var myStuff = from u in data
                  where IsWhatIWant(u)
                  select new FriendlyWrapper(u);

    var sb = new StringBuilder();
    foreach (var s in myStuff)
    {
        sb.AppendLine(s.ToString());
    }
    return sb.ToString();
}

// Consumer 2's job is to take the performance hit up front.  His callers might do things 
// with expensive properties (eg bind one to a UI element) that should not take noticeable time. 
public IEnumerable<FriendlyWrapper> FetchAllData(Predicate<UglyDatum> pred)
{
    var myStuff = from u in data
                  where pred(u)
                  select new FriendlyWrapper(u);

    foreach (var s in myStuff)
    {
        s.Calculate();  // as written, this doesn't do what you intend...
    }

    return myStuff;
}

¿Cuál es la mejor ruta aquí? Opciones puedo ver:

  1. objeto mutable con un método explícito Calcular (), como el anterior
  2. objeto mutable, donde costosos cálculos se realizan en los captadores (y probablemente en caché)
  3. Dividir en dos objetos en los que uno hereda (o quizás compone?) De la otra
  4. Algún tipo de mecanismo de bloqueo + estática, como en la pregunta C ++ vinculado anteriormente

Me estoy inclinando hacia # 2 yo. Pero cada ruta tiene peligros potenciales.

Si elige # 1 o # 2, entonces ¿cómo poner en práctica bucle de Consumer2 sobre mutables de una manera clara, correcta?

Si elige # 1 o # 3, ¿cómo manejar situaciones futuras en la que sólo desea calcular algunas propiedades pero no en otros? Dispuesto para crear métodos de ayuda N / clases derivadas?

Si elige # 4, creo que estás loco, pero no dude en explicar

¿Fue útil?

Solución

En su caso, ya que estás usando LINQ, que sólo vamos a la construcción de estos objetos en los casos en que desea que el cálculo.

Si ese es su patrón de uso estándar, me acabo de poner el cálculo cara directamente en el constructor. El uso de inicialización perezosa es siempre más lento a menos que planea tener algunos casos en los que no calculan. Haciendo el cálculo de los captadores no guardará nada (al menos en este caso específico).

En cuanto a la mutabilidad - objetos mutables con sintaxis de referencia e identidad (es decir: las clases en C #) son muy bien - que es más un problema cuando se trata con los tipos de valor mutable (es decir: estructuras). Hay muchas, muchas clases mutables en .NET BCL - y no causan problemas. El problema suele ser más de uno cuando se inicia tratar con tipos de valor. tipos de valores mutables conducen a un comportamiento muy inesperado.

En general, me vuelvo a esta pregunta al revés - cómo y dónde se va a utilizar este objeto? ¿Cómo puede hacer este objeto el mas potente (si se ha determinado que sea problemático) sin afectar a la usabilidad? Su 1), 3) y 4) opciones serían todos usabilidad hacer sufrir, así que me gustaría evitarlos. En este caso, haciendo 2) no ayudará. Yo sólo lo pondría en el constructor, por lo que su objeto está siempre en un estado válido (que es muy bueno para la usabilidad y facilidad de mantenimiento).

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