Procesando un flujo. ¿Se deben violar las capas?

7

Situación teórica:
Un billón de foobars se almacenan en un archivo de texto (sin bases de datos de fantasía). Cada foobar debe tener ejecutada alguna lógica de negocios. Un conjunto de 1 trillón no cabrá en la memoria, por lo que la capa de datos no puede devolver un gran conjunto a la capa empresarial. En su lugar, deben transmitirse en 1 foobar a la vez y hacer que la lógica de negocios se ejecute en 1 foobar a la vez. La secuencia debe estar cerrada cuando haya terminado.

Para que el flujo se cierre, la capa de negocios debe cerrar el flujo (un detalle de la operación de datos), violando así la separación de preocupaciones.

¿Es posible procesar datos de manera incremental sin violar las capas?

    
pregunta Lord Tydus 05.10.2012 - 03:01

4 respuestas

5

Buena pregunta.

Aquí hay un código C # -ish que te permite tenerlo de ambas maneras. El truco aquí es la evaluación perezosa (rendimiento).

// Lazy producer that will auto-close the file.
// The OS and the disk do the caching for you.
public static IEnumerable<FooBar> GetFoobars(string fileName, long maxNumber = 1000000000000)
{
    using(File file = open(fileName)) // pseudocode
    {
        FooBar nextFooBar;
        do
        {
            nextFoobar = new Foobar(file.ReadByte()); // or something like that.
            yield return nextFoobar;
        }
        while(nextFooBar != null || /* reach max */); // pseudocode
    }
}

// Consumer ... stupid example. Oh well ...
public static int Consume()
{
    int result = (from foo in GetFoobars("foo.txt").AsParallel()
                 where foo.Depth % 10 == 0).Count();
    return result;
}

La función GetFoobars(...) sabe qué archivo abrir, cuántas cosas leer y cómo cerrar el flujo. Solo tira de los resultados a medida que los necesite. Las preocupaciones están separadas. ¿Cuál parece ser el problema? Probablemente podrías hacerlo todo de una sola vez ... tal vez no, pero cerca.

P.S. Recomiendo ir a través de las conferencias de vídeo SICP.

    
respondido por el Job 05.10.2012 - 07:37
2
  

¿Es posible procesar datos de manera incremental sin violar las capas?

Sí, lo es. Tienes que definir la unidad de trabajo que resolverá tu confusión. Creo que procesar conjunto de registros (100 o 1000 dependen del tiempo de procesamiento de un registro promedio) o un registro a la vez tendrá resultados más confiables y manejables.

Además, me aseguraría de que el proceso de manipulación de datos (procesamiento empresarial) esté en una transacción , todos los procesadores de errores se registren y se deshagan para su investigación. Siempre puede tener algunas situaciones excepcionales que deben tenerse en cuenta.

    
respondido por el EL Yusubov 05.10.2012 - 04:00
1

Defina una interfaz abstracta que presente un foobar (o cualquier otro conjunto de foobars que se requiera), dejándolo a la implementación de la abstracción para determinar cómo se adquieren y / o modifican los foobars.

Naturalmente, debe definir los elementos de esta interfaz en torno a las condiciones reales. Si la única forma de contar los foobars es transmitir más allá de los trillones, no incluya "obtener el número de foobars" en su interfaz. Pero probablemente su interfaz contendrá algunos elementos como "start" "stop" y "abort" además de "get"

    
respondido por el ddyer 05.10.2012 - 05:46
1

La forma en que lo veo es esta:

  • Tiene una capa de datos que proporciona funcionalidad para leer foobars individuales, uno a la vez.

  • Esta funcionalidad tiene una API. Esa API tiene un contrato. Cualquier persona que desee utilizar la API debe cumplir con el contrato. El contrato podría especificar, por ejemplo, que debe llamar a NextFoobar() hasta que obtenga un valor nulo o algo así, y luego debe dejar de llamarlo, pero por supuesto podría especificar algo más complejo y simplemente sería el contrato a utilizar. / p>

  • A la capa empresarial no le importa cómo implementó la API, solo necesita usar esta API para hacer lo que debe hacer. El uso de la API incluye que debe cumplir con el contrato.

Ahora, si observa la respuesta basada en C # publicada por @Job, sigue todos estos principios:

  • La capa de datos proporciona un IEnumerable<Foobar> , un objeto que permite que cualquiera pueda recuperar foobars de uno en uno, sin tener que preocuparse de dónde vienen los foobars.

  • La API en IEnumerable<T> está bien definida. Puede usarlo de la manera más difícil (llame a GetEnumerator() , y luego en el enumerador siga llamando a MoveNext() y Current hasta que MoveNext() devuelva falso, y luego llame a Dispose() ) o use una función de idioma o método que utiliza la API automáticamente (por ejemplo, foreach o, en su caso, .Count() ).

  • La capa empresarial utiliza correctamente esta API. hace necesita llamar a Dispose() (lo que hace indirectamente al llamar a .Count() ) pero no necesita preocuparse por lo que Dispose() realmente hace, solo lo llama para cumplir con el contrato.

(Por cierto, si enumeras todos foobars, la llamada a Dispose() no cierra realmente el archivo, sino la última llamada a MoveNext() ... pero Dispose() sigue siendo parte de la API para que pueda (y cierre) el archivo si decide dejar de enumerar en el medio. La belleza de la separación de preocupaciones es que la capa empresarial no necesita preocuparse por nada de eso.)

    
respondido por el Timwi 07.10.2012 - 08:18

Lea otras preguntas en las etiquetas