Vantaggi del modello di strategia

15

Perché è utile usare il modello di strategia se puoi semplicemente scrivere il tuo codice in caso di / se casi?

Ad esempio: ho una classe TaxPayer e uno dei suoi metodi calcola le tasse utilizzando diversi algoritmi. Quindi, perché non può avere se / allora i casi e capire quale algoritmo usare in quel metodo, invece di usare il modello di strategia? Inoltre, perché non puoi semplicemente implementare un metodo separato per ogni algoritmo nella classe TaxPayer?

Inoltre, cosa significa che l'algoritmo cambia in fase di runtime?

    
posta Armon Safai 15.11.2015 - 10:34
fonte

6 risposte

20

Per prima cosa, i grossi blocchi di blocchi if/else sono non facilmente testabili . Ogni nuovo "ramo" aggiunge un altro percorso di esecuzione e quindi aumenta la complessità ciclomatica . Se vuoi testare accuratamente il tuo codice, dovresti coprire tutti i percorsi di esecuzione e ogni condizione richiederebbe di scrivere almeno un altro test (supponendo che tu scriva test piccoli e focalizzati). D'altra parte, le classi che implementano strategie in genere espongono solo 1 metodo pubblico, che è facile da testare.

Quindi, con nidificato if/else ti ritroverai con molti test per una singola parte del tuo codice, mentre con Strategia avrai pochi test per ciascuna delle strategie più semplici. Con quest'ultimo, è facile avere una copertura migliore, perché è più difficile perdere i percorsi di esecuzione.

Come per estensibilità , immagina di scrivere un framework, in cui gli utenti dovrebbero essere in grado di iniettare il proprio comportamento. Ad esempio, si desidera creare una sorta di quadro di calcolo delle imposte e si desidera supportare i sistemi fiscali di diversi paesi. Invece di implementarli tutti, vuoi solo offrire agli utenti del framework la possibilità di fornire un'implementazione su come calcolare alcune tasse particolari.

Ecco il modello di strategia:

  • definisci un'interfaccia, ad es. TaxCalculation e il tuo framework accetta istanze di questo tipo per calcolare le tasse
  • Un utente del framework crea una classe che implementa questa interfaccia e la passa al tuo framework, fornendo così un modo per eseguire parte dei calcoli

Non puoi fare lo stesso con if/else , perché ciò richiederebbe la modifica del codice del framework, nel qual caso non sarebbe più un framework. Poiché i framework sono spesso distribuiti in forma compilata, questa potrebbe essere l'unica opzione.

Tuttavia, anche se scrivi semplicemente del codice normale, la strategia è utile perché rende più chiari i tuoi intenti. Dice "questa logica è collegabile e condizionale", cioè possono esserci più implementazioni che possono variare a seconda delle azioni dell'utente, della configurazione o persino della piattaforma.

L'uso del modello di strategia può migliorare la leggibilità perché, mentre una classe che implementa una determinata strategia in genere dovrebbe avere un nome descrittivo, ad es. USAIncomeTaxCalculator , if/else blocchi sono "senza nome", nei casi migliori appena commentati, e i commenti possono mentire. Inoltre, secondo il mio gusto personale, non è possibile leggere più di quel 3% di blocchi di% di fila in una riga, e diventa piuttosto brutto con i blocchi nidificati.

Anche il principio Aperto / Chiuso è molto pertinente, perché, come descritto nell'esempio sopra, Strategia ti permette di estendere una logica in alcune parti del tuo codice ("apri per estensione") senza riscrivere quelle parti ("chiuso per modifica").

    
risposta data 15.11.2015 - 12:45
fonte
5

Why is it beneficial to use the strategy pattern if you can just write your code in if/then cases?

A volte dovresti usare solo se / allora. È un codice semplice che è facile da leggere.

I due problemi chiave con codice if / then semplice è che possono violare il principio aperto aperto . Se mai dovessi entrare e aggiungere o modificare una condizione, stai modificando questo codice. Se ti aspetti di avere più condizioni, aggiungere una nuova strategia è più semplice / più pulito / meno probabile-to-break-roba.

L'altro problema è l'accoppiamento. Usando if / then, tutte le implementazioni sono legate a tale implementazione, rendendole più difficili da cambiare in futuro. Usando la strategia, l'unico accoppiamento è all'interfaccia della strategia.

    
risposta data 15.11.2015 - 16:46
fonte
4

La strategia è utile quando le if/then condizioni sono basate sui tipi , come spiegato nel link

I condizionali di controllo del tipo di solito non hanno una elevata complessità ciclomatica, quindi non direi che la strategia migliora le cose necessariamente lì.

Il motivo principale della strategia è spiegato nel libro GoF p.316 che ha introdotto il modello:

Use the Strategy pattern when

...

  • a class defines many behaviors, and these appear as multiple conditional statements in its operations. Instead of many conditionals, move related conditional branches into their own Strategy class.

Come accennato in altre risposte, se applicato appropriatamente il modello di strategia consente di aggiungere nuove estensioni (strategie concrete) senza necessariamente richiedere modifiche del resto del codice. Questo è il cosiddetto principio Open-Closed o Principio delle varianti protette . Naturalmente, devi ancora codificare la nuova strategia concreta e il codice client deve riconoscere le strategie come plug-in (non è banale).

Con if/then condizionali, è necessario modificare il codice della classe contenente la logica condizionale. Come accennato in altre risposte, a volte questo è OK quando non si desidera aggiungere la complessità per supportare l'aggiunta di nuove funzionalità (plug-in) senza ricompilare.

    
risposta data 16.11.2015 - 17:30
fonte
3

[...] if you can just write your code in if/then cases?

Questo è esattamente il più grande vantaggio del modello strategico. Non avendo condizioni.

Vuoi che le tue classi / metodi / funzioni siano il più semplici e brevi possibile. Il codice breve è molto facile da testare e molto facile da leggere.

Condizioni ( if / elseif / else ) rendono le tue classi / metodi / funzioni a lungo, perché solitamente il codice in cui una decisione valuta a true è diverso dalla parte in cui la decisione valuta a false .

Un altro grande vantaggio del modello strategico è che è riutilizzabile nell'intero progetto.

Quando si utilizza il modello di progettazione della strategia, è molto probabile avere una sorta di contenitore IoC, dal quale si ottiene l'implementazione desiderata di un'interfaccia, forse con un metodo getById(int id) , in cui id potrebbe essere un membro dell'enumerazione.

Questo significa che la creazione dell'implementazione è solo in un punto del tuo codice.

Se desideri aggiungere ulteriori implementazioni, aggiungi la nuova implementazione al metodo getById e questa modifica si riflette ovunque nel codice in cui la chiami.

Con if / elseif / else questo è impossibile da fare. Aggiungendo una nuova implementazione, devi aggiungere un nuovo blocco elseif e farlo ovunque dove sono state utilizzate le implementazioni, oppure potresti finire con un codice che non è valido, perché hai dimenticato di aggiungere l'implementazione alla sua struttura.

Also, what does it mean for the algorithm to change at runtime?

Nel mio esempio, id potrebbe essere una variabile popolata in base a un input dell'utente. Se l'utente fa clic su un pulsante A, quindi id = 2 , se fa clic su un pulsante B, quindi id = 8 .

A causa del diverso valore id , dal contenitore IoC si ottiene un'implementazione diversa di un'interfaccia e il codice esegue diverse operazioni.

    
risposta data 15.11.2015 - 13:31
fonte
2

Why is it beneficial to use the strategy pattern if you can just write your code in if/then cases?

Il modello di strategia ti consente di separare i tuoi algoritmi (i dettagli) dalla tua logica aziendale (criterio di alto livello). Queste due cose non sono solo confuse da leggere quando sono miste, ma hanno anche ragioni molto diverse per cambiare.

C'è anche un importante fattore di scalabilità del lavoro di squadra qui. Immagina un grande team di programmazione in cui molte persone stanno lavorando a questo pacchetto di contabilità. Se gli algoritmi fiscali sono tutti nella classe o modulo TaxPayer , è probabile che i conflitti di unione diventino probabili. I conflitti di unione sono lunghi e soggetti a errori da risolvere. Questa volta drena la produttività dalla squadra e gli errori introdotti dalla cattiva fusione danno credibilità ai clienti.

Also, what does it mean for the algorithm to change at runtime?

Un algoritmo che cambia in runtime è uno il cui comportamento è determinato dalla configurazione o dal contesto. Un approccio if / then in atto non lo abilita in quanto comporta il ricaricamento delle classi utilizzate attivamente esistenti. Con il modello di strategia gli oggetti strategici che implementano ciascun algoritmo potrebbero essere costruiti in base all'uso. Di conseguenza, le modifiche a questi algoritmi (correzioni di bug o miglioramenti) potrebbero essere apportate e ricaricate in fase di runtime. Questo approccio potrebbe essere utilizzato per abilitare la disponibilità continua e le versioni a zero downtime.

    
risposta data 16.11.2015 - 17:48
fonte
1

Non c'è niente di sbagliato in if/else di per sé. In molti casi, if/else è il modo più semplice e più leggibile di esprimere la logica. Quindi l'approccio che descrivi è perfettamente valido in molti casi. (È anche perfettamente testabile, quindi non è un problema.)

Ma ci sono alcuni casi particolari in cui un modello strategico può migliorare la manutenibilità del codice generale. Ad esempio:

  • Se gli algoritmi di calcolo delle tasse particolari possono cambiare indipendentemente l'uno dall'altro e dalla logica principale. In questo caso sarebbe bello averli separati in classi distinte, poiché le modifiche saranno localizzate.
  • Se è possibile aggiungere nuovi algoritmi in futuro, senza modificare la logica di base.
  • Se la causa della differenza tra i due algoritmi influisce anche su altre parti del codice. Supponi di selezionare tra i due algoritmi in base alla fascia di reddito del contribuente. Se questa fascia di reddito ti permette anche di selezionare diversi rami nel codice, allora è più pulito istanziare una strategia corrispondente alla fascia di reddito una volta, e chiamare quando è necessario, piuttosto che avere più se / altrimenti-rami sparsi sul codice.

Affinché il modello strategico abbia senso, l'interfaccia tra la logica di base e gli algoritmi di calcolo delle tasse dovrebbe essere più stabile rispetto ai singoli componenti. Se è altrettanto probabile che una modifica dei requisiti causerà la modifica dell'interfaccia, allora il modello di strategia potrebbe essere effettivamente una responsabilità.

Tutto si riduce a se gli "algoritmi di calcolo delle tasse" possono essere separati in modo pulito dalla logica di base che la invoca. Un modello di strategia ha un overhead rispetto a un if/else , quindi dovrai decidere caso per caso se l'investimento vale la pena.

    
risposta data 16.05.2017 - 13:19
fonte