Si lo hace - Lógica de código repetida

15

Mi jefe me dio un proyecto con una lógica particular. Tengo que desarrollar una página web que debe llevar al navegador a través de muchos casos hasta que llegue al producto.

Este es el esquema de ruta de la navegación en el sitio:

IMPORTANT!

En la página de Productos, el navegador puede elegir qué filtro quiere.

  • Si A, él / ella DEBE pasar por B (y luego C, por supuesto) o C y alcanzar los productos.
  • Si B, él / ella DEBE pasar por la C y alcanzar los productos.
  • Si C, él / ella alcanza directamente los productos.

Por supuesto, si comienzo desde A, sigo la ruta más larga y cuando llego a mis productos tengo 3 filtros activos.

Hasta ahora he desarrollado el siguiente código que funciona bien.

if filter_A
  if filter_B
     filter_C()
     .. else ..
  else
     filter_C
    .. else ..
else
   if filter_B
      filter_C()
     .. else ..
   else
     filter_C()
     .. else ..

Estoy aquí para preguntar qué habría hecho un programador más experto en esta situación. No respeté el principio DRY, no me gusta y me gustaría conocer una forma alternativa de desarrollar este tipo de lógica.

Pensé en dividir cada sección de código en funciones, pero ¿es una buena idea en este caso?

    
pregunta Kevin Cittadini 06.03.2015 - 13:18
fuente

5 respuestas

20

No has dicho si los filtros toman algún parámetro. Por ejemplo, filter_A podría ser un filtro de categoría, por lo que no es solo una cuestión de "¿necesito aplicar filter_A ", podría ser "necesito aplicar filter_A y devolver todos los registros con la categoría campo = fooCategory ".

La forma más sencilla de implementar exactamente lo que has descrito (pero asegúrate de leer la segunda mitad de la respuesta a continuación) es similar a las otras respuestas, pero no tendría ningún booleano. cheques en absoluto Definiría interfaces: FilterA, FilterB, FilterC . Entonces puedes tener algo como (soy un programador de Java, así que esta será la sintaxis de Java):

class RequestFilters {
    FilterA filterA;
    FilterB filterB;
    FilterC filterC;
}

Entonces podrías tener algo como esto (usando enum patrón de singleton de Java efectivo ):

enum NoOpFilterA implements FilterA {
    INSTANCE;

    public List<Item> applyFilter(List<Item> input) {
       return input;
    }
}

Pero si realmente quieres filtrar algunos elementos, puedes proporcionar una instancia de una implementación FilterA que realmente haga algo. Tu método de filtración será muy simple

List<Item> filterItems(List<Item> data, RequestFilters filters) {
    List<Item> returnedList = data;
    returnedList = filters.filterA.filter(data);
    returnedList = filters.filterB.filter(data);
    returnedList = filters.filterC.filter(data);
    return returnedList;
}

Pero acabo de empezar.

Sospecho que la llamada applyFilter será en realidad bastante similar para los tres tipos de filtros. Si ese es el caso, ni siquiera lo haría de la manera descrita anteriormente. Puede obtener un código aún más claro si solo tiene una interfaz, y luego haga esto:

class ChainedFilter implements Filter {
     List<Filter> filterList;

     void addFilter(Filter filter) {
          filterList.add(filter);
     }

     List<Item> applyFilter(List<Item> input) {
         List<Item> returnedList = input;
         for(Filter f : filterList) {
             returnedList = f.applyFilter(returnedList);
         }
         return returnedList;
     }
}

Luego, a medida que su usuario navega por las páginas, simplemente agrega una nueva instancia de cualquier filtro que necesite cuando sea apropiado. Esto le permitirá poder aplicar múltiples instancias del mismo filtro con diferentes argumentos en caso de que necesite ese comportamiento en el futuro, y también agregar filtros adicionales en el futuro sin tener que cambiar su diseño .

Además, puedes agregar algo como el NoOpFilter anterior o simplemente no puedes agregar un filtro en particular a la lista, lo que sea más fácil para tu código.

    
respondido por el durron597 06.03.2015 - 15:17
fuente
3

En este caso, es importante separar la lógica del filtrado y el flujo de control de cómo se ejecutan los filtros. La lógica del filtro se debe separar en funciones individuales, que pueden ejecutarse de forma independiente.

ApplyFilterA();
ApplyFilterB();
ApplyFilterC();

En el código de muestra publicado, hay 3 booleans filter_A , filter_B y filter_C . Sin embargo, desde el diagrama, filter_C siempre se ejecuta, por lo que se puede cambiar a un incondicional.

NOTA: supongo que el diagrama de flujo de control es correcto. Existe una discrepancia entre el código de muestra publicado y el diagrama de flujo de control.

Un fragmento de código separado controla qué filtros se ejecutan

ApplyFilters(bool filter_A, bool filter_B)
{
    listOfProducts tmp;
    if (filter_A)
        ApplyFilterA();
    if (filter_B)
        ApplyFilterB();
    ApplyFilterC();
}

Hay una separación distinta entre controlar qué filtros se ejecutan y qué hacen los filtros. Rompe esas dos piezas de lógica.

    
respondido por el CurtisHx 06.03.2015 - 14:12
fuente
2

Supongo que desea el algoritmo más simple y claro.
En este caso, sabiendo que el filtro c siempre se aplica, lo viviría fuera de la lógica if y lo aplicaría al final independientemente. Como se ve en su diagrama de flujo, cada filtro antes de la c, es opcional, porque cada uno de ellos puede aplicarse o no. En este caso, viviría si estuviera separado de cada filtro, sin anidar ni encadenar:

if filter_a
  do_filter_a()

if filter_b
  do_filter_b()

do_filter_c()

si tiene un diagrama de flujo con un número variable de filtros, antes del obligatorio, yo, en cambio, guardaría todos los filtros en una matriz, en el orden en que deberían aparecer. Luego, procese los filtros opcionales en el bucle y aplique el obligatorio al final, fuera del bucle:

optional_filters_array = (a, b, c, d, e, f, g, h, etc)

for current_filter in optional_filters_array
  do_filter(current_filter)

do_required_filter()

o:

optional_filters_array = (a, b, c, d, e, f, g, h, etc)
required_filter = last_filter


for current_filter in optional_filters_array
  do_filter(current_filter)

do_filter(required_filter)

de cource, tendría que definir la subrutina de procesamiento del filtro.

    
respondido por el igoryonya 06.03.2015 - 16:41
fuente
1

Voy a asumir que filterA, filterB, y filterC realmente modifican la lista de productos. De lo contrario, si son solo if-check, entonces filterA y filterB pueden ignorarse ya que todas las rutas conducen en última instancia a filterC. Su descripción del requisito parece implicar que cada filtro reducirá la lista de productos.

Asumiendo que los filtros realmente reducen la lista de productos, aquí hay un poco de pseudo-código ...

class filter
    func check(item) returns boolean
endclass

func applyFilter(filter, productList) returns list
    newList is list
    foreach item in productList
        if filter.check(item) then
            add item to newList
        endif
    endfor 
    return newList
endfunc



filterA, filterB, filterC = subclasses of filter for each condition, chosen by the user
products = list of items to be filtered

if filterA then
    products = applyFilter(filterA, products)
endif

if filterB then
    products = applyFilter(filterB, products)
endif

if filterC then
    products = applyFilter(filterC, products)
endif

# use products...

En sus requisitos, filterC no se aplica automáticamente, pero en el diagrama, sí. Si el requisito es que al menos se aplique filterC sin importar qué, entonces debería llamar applyFilter (filterC, productos) sin verificar si se ha elegido filterC.

filterC = instance of filter, always chosen

...

# if filterC then
products = applyFilter(filterC, products)
# endif
    
respondido por el Kent A. 06.03.2015 - 14:05
fuente
0

Me pregunto si tendría sentido modelar sus filtros para que sean algún tipo de objetos en una gráfica. Al menos eso es lo que pienso cuando veo el diagrama.

Si modelas la dependencia de los filtros como un gráfico de objetos, entonces el código que maneja las posibles rutas de flujo es bastante sencillo sin ninguna lógica peluda. Además, el gráfico (lógica empresarial) puede cambiar, mientras que el código que interpreta el gráfico sigue siendo el mismo.

    
respondido por el enum 06.03.2015 - 14:30
fuente

Lea otras preguntas en las etiquetas