Entrambe le risposte attuali sembrano colpire solo parzialmente, e si concentrano su esempi che offuscano l'idea centrale. Anche questo non è (solo) un principio OOP ma un principio di progettazione software in generale.
La cosa che "varia" in questa frase è il codice. Christophe è sul punto di dire che di solito è qualcosa che può variare, cioè spesso anticipare questo. L'obiettivo è di proteggersi dai futuri cambiamenti nel codice. Questo è strettamente correlato a programmazione su un'interfaccia . Tuttavia, Christophe non è corretto per limitare questo a "dettagli di implementazione". In effetti, il valore di questo consiglio è spesso dovuto a cambiamenti nei requisiti .
Questo è solo indirettamente correlato allo stato di incapsulamento, che è ciò a cui credo che David Arno stia pensando. Questo consiglio non sempre (ma spesso lo fa) suggerisce lo stato di incapsulamento, e questo consiglio vale anche per gli oggetti immutabili. In effetti, la semplice denominazione delle costanti è una forma (molto basilare) di incapsulare ciò che varia.
CandiedOrange confonde esplicitamente "ciò che varia" con "dettagli". Questo è solo parzialmente corretto. Sono d'accordo che qualsiasi codice che varia è "dettagli" in un certo senso, ma un "dettaglio" non può variare (a meno che non si definiscano "dettagli" per rendere questo tautologico). Ci possono essere motivi per incapsulare dettagli non variabili, ma questo motto non è uno. In parole povere, se fossi molto fiducioso che "cane", "gatto" e "papero" sarebbero gli unici tipi con cui avresti mai avuto bisogno di gestirli, allora questo motto non suggerisce che il refactoring di CandiedOrange sia eseguito.
Lanciare l'esempio di CandiedOrange in un contesto diverso, supponiamo di avere un linguaggio procedurale come C. Se ho del codice che contiene:
if (pet.type() == dog) {
pet.bark();
} else if (pet.type() == cat) {
pet.meow();
} else if (pet.type() == duck) {
pet.quack()
}
Posso ragionevolmente aspettarmi che questo pezzo di codice cambi in futuro. Posso "incapsulare" semplicemente definendo una nuova procedura:
void speak(pet) {
if (pet.type() == dog) {
pet.bark();
} else if (pet.type() == cat) {
pet.meow();
} else if (pet.type() == duck) {
pet.quack()
}
}
e usando questa nuova procedura invece del blocco di codice (cioè un refactoring di "metodo di estrazione"). A questo punto, aggiungere un tipo "cow" o qualsiasi altra cosa richiede solo l'aggiornamento della procedura speak
. Ovviamente, in un linguaggio OO si può invece sfruttare l'invio dinamico come accennato dalla risposta di CandiedOrange. Questo accadrà naturalmente se accedi a pet
tramite un'interfaccia. Eliminare la logica condizionale tramite la spedizione dinamica è una preoccupazione ortogonale che faceva parte del motivo per cui ho realizzato questa versione procedurale. Voglio anche sottolineare che questo non richiede funzionalità particolari per OOP. Anche in un linguaggio OO, incapsulare ciò che varia non significa necessariamente creare una nuova classe o interfaccia.
Come esempio più archetipico (che è più vicino ma non abbastanza OO), diciamo che vogliamo rimuovere i duplicati da una lista. Diciamo che lo implementiamo iterando sulla lista tenendo traccia degli articoli che abbiamo visto finora in un altro elenco e rimuovendo tutti gli elementi che abbiamo visto. È ragionevole presumere che potremmo voler cambiare il modo in cui teniamo traccia degli articoli visti, almeno per motivi di prestazioni. Il motto per incapsulare ciò che varia suggerisce che dovremmo costruire un tipo di dati astratto per rappresentare l'insieme di elementi visti. Il nostro algoritmo è ora definito contro questo tipo di dati Set astratto, e se decidiamo di passare a un albero di ricerca binario, il nostro algoritmo non ha bisogno di cambiare o preoccuparsi. In un linguaggio OO, possiamo usare una classe o un'interfaccia per catturare questo tipo di dati astratti. In una lingua come SML / O'Caml potresti invece acquisire il tipo di dati astratti Set come modulo.
Per un esempio orientato ai requisiti, supponiamo di dover convalidare alcuni campi per quanto riguarda alcune logiche di business. Anche se ora potresti avere dei requisiti specifici, sospetti strongmente che si evolveranno. È possibile incapsulare la logica corrente nella propria procedura / funzione / regola / classe.
Sebbene si tratti di una preoccupazione ortogonale che non fa parte di "incapsulare ciò che varia", è spesso naturale estrapolare, cioè parametrizzare, la logica ormai incapsulata. Questo in genere porta a un codice più flessibile e consente di modificare la logica sostituendo un'implementazione alternativa anziché modificando la logica incapsulata.