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 .