If Else - Ripetuta logica di codice

15

Il mio capo mi ha dato un progetto con una logica particolare. Devo sviluppare una pagina web che deve guidare il navigatore in molti casi fino a quando arriva al prodotto.

Questo è lo schema del percorso della navigazione nel sito:

ATTENZIONE!

Nella pagina Prodotti il navigatore può scegliere il filtro che desidera.

  • Se A, lui DEVE passare attraverso la B (e poi C ovviamente) o C e raggiungere i prodotti.
  • Se B, lui DEVE passare attraverso la C e raggiungere i prodotti.
  • Se C, raggiunge direttamente i prodotti.

Naturalmente se parto da A seguo il percorso più lungo e quando raggiungo i miei prodotti ho 3 filtri attivi.

Fino ad ora ho sviluppato il seguente codice che funziona bene.

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

Sono qui per chiedere cosa avrebbe fatto un programmatore più esperto in questa situazione. Non ho rispettato il principio ASCIUTTO, non mi piace e mi piacerebbe conoscere un modo alternativo per sviluppare questo tipo di logica.

Ho pensato di suddividere ogni sezione di codice nelle funzioni, ma è una buona idea in questo caso?

    
posta Kevin Cittadini 06.03.2015 - 13:18
fonte

5 risposte

20

Non hai detto se i filtri prendono qualsiasi parametro. Ad esempio, filter_A potrebbe essere un filtro di categoria, quindi non è solo una questione di "devo applicare filter_A ", potrebbe essere "Devo applicare filter_A e restituire tutti i record con la categoria campo = fooCategory ".

Il modo più semplice per implementare esattamente quello che hai descritto (ma assicurati di leggere la seconda metà della risposta sotto) è simile alle altre risposte, ma non avrei alcun valore booleano controlli a tutti. Definirei le interfacce: FilterA, FilterB, FilterC . Quindi puoi avere qualcosa di simile (io sono un programmatore Java, quindi questa sarà la sintassi Java-esque):

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

Quindi potresti avere qualcosa di simile a questo (usando il enum modello singleton di Java efficace ):

enum NoOpFilterA implements FilterA {
    INSTANCE;

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

Ma se in effetti desideri che alcuni elementi vengano filtrati, puoi invece fornire un'istanza di un'implementazione FilterA che in realtà fa qualcosa. Il tuo metodo di filtraggio sarà molto semplice

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

Ma ho appena iniziato.

Sospetto che la chiamata di applyFilter sarà in realtà abbastanza simile per tutti e tre i tipi di filtri. Se è così, non lo farei nemmeno come descritto sopra. Puoi ottenere un codice ancora più pulito avendo solo un'interfaccia, quindi facendo questo:

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

Quindi, mentre l'utente naviga tra le pagine, è sufficiente aggiungere una nuova istanza di qualsiasi filtro necessario, se necessario. Ciò ti consentirà di essere in grado di applicare più istanze dello stesso filtro con argomenti diversi nel caso in cui avessi bisogno di tale comportamento in futuro e di aggiungere ulteriori filtri in futuro senza dover modificare il tuo design .

Inoltre, puoi aggiungere qualcosa come NoOpFilter sopra o non puoi semplicemente aggiungere un particolare filtro all'elenco, qualunque cosa sia più facile per il tuo codice.

    
risposta data 06.03.2015 - 15:17
fonte
3

In questo caso, è importante separare la logica del filtro e il flusso di controllo del modo in cui i filtri vengono eseguiti. La logica del filtro deve essere separata in singole funzioni, che possono essere indipendenti l'una dall'altra.

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

Nel codice di esempio pubblicato, ci sono 3 booleans filter_A , filter_B e filter_C . Tuttavia, dal diagramma, viene eseguito sempre filter_C , in modo che possa essere modificato in modo incondizionato.

NOTA: presumo che il diagramma del flusso di controllo sia corretto. C'è una discrepanza tra il codice di esempio pubblicato e il diagramma del flusso di controllo.

Un pezzo separato di codice controlla quali filtri vengono eseguiti

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

Esiste una netta separazione tra il controllo su quali filtri vengono eseguiti e su cosa fanno i filtri. Rompi quei due pezzi di logica.

    
risposta data 06.03.2015 - 14:12
fonte
2

Suppongo che tu voglia l'algoritmo più semplice e chiaro.
In questo caso, sapendo che il filtro c è sempre applicato, vorrei viverlo fuori dalla logica if e applicarlo alla fine a prescindere. Come appare nel tuo diagramma di flusso, ogni filtro prima della c, è opzionale, poiché ognuno di essi può essere applicato o meno. In questo caso, vorrei vivere se separato da ciascun filtro, senza nidificazione e concatenamento:

if filter_a
  do_filter_a()

if filter_b
  do_filter_b()

do_filter_c()

se hai un diagramma di flusso con un numero variabile di filtri, prima di quello obbligatorio, vorrei, invece, salvare tutti i filtri su un array, in un ordine in cui dovrebbero apparire. Quindi elaborare i filtri opzionali nel ciclo e applicare quello obbligatorio alla fine, al di fuori del ciclo:

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)

di cource, dovresti definire la subroutine di elaborazione del filtro.

    
risposta data 06.03.2015 - 16:41
fonte
1

Ho intenzione di supporre che filterA, filterB e filterC modificino effettivamente l'elenco dei prodotti. Altrimenti, se sono solo if-checks, quindi filterA e filterB possono essere ignorati poiché tutti i percorsi portano in definitiva al filterC. La tua descrizione del requisito sembra implicare che ciascun filtro ridurrà l'elenco dei prodotti.

Quindi, supponendo che i filtri riducano effettivamente l'elenco dei prodotti, ecco un po 'di pseudo-codice ...

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...

Nei tuoi requisiti, filterC non viene applicato automaticamente, ma nel diagramma lo è. Se il requisito è che almeno filterC dovrebbe essere applicato, non importa cosa, allora chiamerai applyFilter (filterC, products) senza controllare se filterC è scelto.

filterC = instance of filter, always chosen

...

# if filterC then
products = applyFilter(filterC, products)
# endif
    
risposta data 06.03.2015 - 14:05
fonte
0

Mi chiedo se la modellazione dei filtri per essere una sorta di oggetti in un grafico avrebbe senso. Almeno questo è quello che penso quando vedo il diagramma.

Se modellate la dipendenza dei filtri come un grafo di oggetti, allora il codice che gestisce i possibili percorsi di flusso è praticamente diretto senza alcuna logica pelosa. Inoltre, il grafico (business logic) può cambiare, mentre il codice che interpreta il grafico rimane lo stesso.

    
risposta data 06.03.2015 - 14:30
fonte