Entrambe le risposte attuali sembrano colpire solo parzialmente il segno e si concentrano su esempi che offuscano l'idea di base. Anche questo non è (esclusivamente) un principio OOP ma un principio di progettazione del 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 lo prevedi . L'obiettivo è proteggersi da future modifiche al codice. Questo è strettamente correlato alla programmazione contro un'interfaccia . Tuttavia, Christophe non è corretto nel limitare i "dettagli di implementazione". In effetti, il valore di questo consiglio è spesso dovuto a cambiamenti nei requisiti .
Questo è solo indirettamente correlato allo stato incapsulante, che è ciò a cui penso David Arno stia pensando. Questo consiglio non sempre (ma spesso) suggerisce uno stato incapsulante, e questo consiglio si applica anche agli oggetti immutabili. In effetti, semplicemente nominare le costanti è una forma (molto basilare) di incapsulare ciò che varia.
CandiedOrange fonde esplicitamente "ciò che varia" con "dettagli". Questo è solo parzialmente corretto. Concordo sul fatto che qualsiasi codice che varia è in qualche modo "dettagli", 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 detto non è uno. In parole povere, se eri sicuro che "cane", "gatto" e "anatra" sarebbero stati gli unici tipi con cui avresti mai avuto a che fare, allora questo detto non suggerisce il refactoring eseguito da CandiedOrange.
Trasmettendo l'esempio di CandiedOrange in un contesto diverso, supponiamo di avere un linguaggio procedurale come C. Se ho un 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 cambierà in futuro. Posso "incapsularlo" 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 utilizzando questa nuova procedura invece del blocco di codice (ovvero un refactoring "metodo di estrazione"). A questo punto l'aggiunta di un tipo "mucca" o qualsiasi altra cosa richiede solo l'aggiornamento della speak
procedura. Naturalmente, in una lingua OO puoi invece sfruttare l'invio dinamico come indicato dalla risposta di CandiedOrange. Questo accadrà naturalmente se accedi pet
tramite un'interfaccia. L'eliminazione della logica condizionale tramite invio dinamico è una preoccupazione ortogonale che faceva parte del motivo per cui ho realizzato questa interpretazione procedurale. Voglio anche sottolineare che ciò non richiede funzionalità particolari di OOP. Anche in un linguaggio OO, incapsulare ciò che varia non significa necessariamente che è necessario creare una nuova classe o interfaccia.
Come esempio più archetipico (che è più vicino ma non del tutto OO), diciamo che vogliamo rimuovere i duplicati da un elenco. Diciamo che lo implementiamo ripetendo l'elenco tenendo traccia degli elementi che abbiamo visto finora in un altro elenco e rimuovendo tutti gli elementi che abbiamo visto. È ragionevole supporre che potremmo voler cambiare il modo in cui tenere traccia degli oggetti visti può, almeno, per motivi di prestazioni. Il dettato per incapsulare ciò che varia suggerisce che dovremmo costruire un tipo di dati astratto per rappresentare l'insieme di oggetti visti. Il nostro algoritmo è ora definito rispetto a questo tipo di dati Set astratto e se decidiamo di passare a un albero di ricerca binario, il nostro algoritmo non deve cambiare o preoccuparsi. In un linguaggio OO, potremmo utilizzare una classe o un'interfaccia per acquisire questo tipo di dati astratto. In una lingua come SML / O '
Per un esempio basato sui requisiti, supponiamo che sia necessario convalidare un campo per quanto riguarda alcune logiche aziendali. Anche se ora potresti avere requisiti specifici, sospetti fortemente che si evolveranno. È possibile incapsulare la logica corrente nella propria procedura / funzione / regola / classe.
Sebbene questa sia una preoccupazione ortogonale che non fa parte di "incapsulare ciò che varia", è spesso naturale sottrarre, cioè parametrizzare, la logica ora incapsulata. Questo in genere porta a un codice più flessibile e consente di modificare la logica sostituendo in un'implementazione alternativa anziché modificare la logica incapsulata.