Agile contraddice il principio Aperto / Chiuso?

5

Il principio O / C impone che il codice di produzione non debba essere modificato se è richiesto un aumento del comportamento del sistema. Altrimenti diventa difficile affidarsi ai test.

Seguendolo come un dogma termineremo con alberi o aggregazioni di eredità profonda.

D'altra parte non abbiamo mai provato questo approccio, quindi la prima affermazione è più di una supposizione. Ignoriamo completamente il principio O / C, cosa che ci porta molto dolore. Ma l'aumento spesso si accompagna a cambiamenti di interfaccia, così il design viene "shaked" dall'iterazione all'iterazione e immagino a malapena come sia possibile aderire al principio O / C.

Non dovrebbe esistere qualche nozione di completezza della funzionalità solo dopo aver raggiunto quali modifiche al codice dovrebbero essere scoraggiate?

    
posta Pavel Voronin 16.12.2014 - 10:56
fonte

2 risposte

17

Il principio di apertura / chiusura è difficile, e penso che sia spesso frainteso. In senso stretto, ovviamente è impossibile aggiungere un comportamento a un'applicazione senza modificare il suo codice, ma ciò che intendiamo con open / closed è che dovrebbe essere possibile estendere la funzionalità di una classe data senza cambiando quella classe . Lascia che provi a inventare un semplice esempio forzato. Supponiamo che tu abbia una classe Order, che ha un metodo di prezzo che somma i prezzi di tutti gli articoli nell'ordine:

class Order
  def price
    items.inject(0) { |total, item| total += item.price }
  end
end

Ora, dì che hai un nuovo requisito che alcuni ordini saranno tassabili (per i clienti nello stesso stato, ad es.) e altri non lo saranno. Indipendentemente dal fatto che tu voglia aderire a O / C, dovrai cambiare la classe dell'Ordine. È inevitabile. Ma come lo fai? L'approccio ovvio è un condizionale:

class Order
  def price
    base_price = items.inject(0) { |total, item| total += item.price }
    if taxable?
      base_price * 1.1
    else
      base_price
    end
  end
end

Tuttavia, la cosa da capire qui è che l' next momento in cui qualcuno richiede una modifica al modo in cui vengono calcolati i prezzi degli ordini, dovrai ancora fare un'altra modifica a questo classe. E mentre questi cambiamenti si impilano l'uno sull'altro, le cose sfuggono di mano. Quindi, quello che apre / chiude dice è che dovresti rifattorizzare la funzionalità esistente in modo tale da rendere possibile la modifica senza alterare l'Ordine.

Ci sono un milione di modi per farlo, ma per il gusto di argomentare diciamo che vuoi impiegare una sorta di modello di strategia: il tuo Ordine dipende da una delle molte sottoclassi di PriceCalculator per calcolare il prezzo, e il calcolatore viene iniettato quando tu instanzia un Ordine. Ora, quando inevitabilmente ottieni la richiesta di aggiungere una tassa di spedizione ad alcuni ordini (ma non ad altri), puoi scambiare il prezzo con un prezzoCalculatorWithShipping (o qualsiasi altra cosa) e non dover toccare Ordine.

Quindi, per riassumere, direi aperto / chiuso non è scoraggiare le modifiche al codice, è solo cercare di assicurarmi che tali cambiamenti avvengano nel minor numero possibile di luoghi e che ci siano meno motivi per apportare modifiche estese a quelli esistenti classi. Direi che a medio termine questo rende le applicazioni molto più facili da cambiare e quindi è completamente compatibile con la filosofia agile.

Per un'esposizione davvero eccezionale di queste idee mi raccomando questo discorso di Sandi Metz: link .

    
risposta data 16.12.2014 - 11:24
fonte
1

"Il principio O / C impone che il codice di produzione non debba essere modificato se è richiesto un aumento del comportamento del sistema."

La mia comprensione di O / C non era che un programmatore non dovrebbe modificare una classe nel tempo (non ci sono rischi fondamentali per testare come possiamo e dovremmo sempre aggiornare i nostri test insieme alle eventuali modifiche alla funzionalità), ma che le classi non dovrebbero esponi abbastanza del loro funzionamento interno che altro codice può effettivamente modificare il loro comportamento. Cioè, se creo una classe Foo con un set completo di test unitari, non dovrebbe essere possibile creare una classe DerivedFoo in modo tale che se la uso in un test Foo, quel test fallirà.

    
risposta data 16.12.2014 - 12:36
fonte

Leggi altre domande sui tag