Obie obecne odpowiedzi wydają się tylko częściowo trafiać w sedno i koncentrują się na przykładach, które przesłaniają podstawową ideę. Nie jest to również (wyłącznie) zasada OOP, ale ogólnie zasada projektowania oprogramowania.
Tym, co „różni się” w tym wyrażeniu, jest kod. Christophe ma rację mówiąc, że zwykle jest to coś, co może się różnić, to znaczy, że często tego oczekujesz . Celem jest ochrona się przed przyszłymi zmianami w kodzie. Jest to ściśle związane z programowaniem w interfejsie . Jednak Christophe błędnie ogranicza to do „szczegółów implementacji”. W rzeczywistości wartość tej porady często wynika ze zmian wymagań .
Jest to tylko pośrednio związane z stanem enkapsulacji, o czym, jak sądzę, myśli David Arno. Ta rada nie zawsze (ale często) sugeruje stan kapsułkowania, a ta rada dotyczy również obiektów niezmiennych. W rzeczywistości zwykłe nazywanie stałych jest (bardzo podstawową) formą enkapsulacji tego, co różni.
CandiedOrange wyraźnie łączy „to, co się zmienia” z „szczegółami”. Jest to tylko częściowo poprawne. Zgadzam się, że każdy kod, który się zmienia, jest w pewnym sensie „szczegółami”, ale „szczegół” może się nie różnić (chyba że zdefiniujesz „szczegóły”, aby uczynić to tautologicznym). Mogą istnieć powody, by ujmować nie zmieniające się szczegóły, ale to powiedzenie nie jest jedno. Z grubsza mówiąc, jeśli jesteś bardzo pewny, że „pies”, „kot” i „kaczka” będą jedynymi typami, z którymi kiedykolwiek będziesz musiał sobie poradzić, to to powiedzenie nie sugeruje refaktoryzacji, jaką wykonuje CandiedOrange.
Rzucając przykład CandiedOrange w innym kontekście, załóżmy, że mamy język proceduralny, taki jak C. Jeśli mam jakiś kod, który zawiera:
if (pet.type() == dog) {
pet.bark();
} else if (pet.type() == cat) {
pet.meow();
} else if (pet.type() == duck) {
pet.quack()
}
Mogę się spodziewać, że ten fragment kodu zmieni się w przyszłości. Mogę to „zamknąć” po prostu, definiując nową procedurę:
void speak(pet) {
if (pet.type() == dog) {
pet.bark();
} else if (pet.type() == cat) {
pet.meow();
} else if (pet.type() == duck) {
pet.quack()
}
}
i stosując tę nową procedurę zamiast bloku kodu (tj. refaktoryzację metodą „wyodrębniania”). W tym momencie dodanie typu „krowa” lub cokolwiek innego wymaga jedynie zaktualizowania speak
procedury. Oczywiście w języku OO możesz zamiast tego skorzystać z dynamicznej wysyłki, o czym wspomina odpowiedź CandiedOrange. Stanie się to naturalnie, jeśli uzyskasz dostęp pet
przez interfejs. Eliminacja logiki warunkowej za pomocą dynamicznej wysyłki jest kwestią ortogonalną, która była częścią tego, dlaczego dokonałem tego proceduralnego wykonania. Chcę również podkreślić, że nie wymaga to cech charakterystycznych dla OOP. Nawet w języku OO hermetyzacja tego, co się różni, niekoniecznie oznacza, że należy utworzyć nową klasę lub interfejs.
Jako bardziej archetypowy przykład (który jest bliższy, ale nie do końca OO), powiedzmy, że chcemy usunąć duplikaty z listy. Załóżmy, że wdrażamy to, iterując listę, śledząc przedmioty, które widzieliśmy do tej pory na innej liście, i usuwając wszystkie, które widzieliśmy. Rozsądnie jest założyć, że możemy chcieć zmienić sposób śledzenia obserwowanych przedmiotów, przynajmniej z powodów związanych z wydajnością. Wymóg zawarcia tego, co się różni, sugeruje, że powinniśmy zbudować abstrakcyjny typ danych, który reprezentowałby zestaw widocznych elementów. Nasz algorytm jest teraz zdefiniowany w oparciu o ten abstrakcyjny zestaw danych, a jeśli zdecydujemy się przejść na drzewo wyszukiwania binarnego, nasz algorytm nie musi się zmieniać ani się tym przejmować. W języku OO możemy użyć klasy lub interfejsu do przechwycenia tego abstrakcyjnego typu danych. W języku takim jak SML / O ”
W przykładzie opartym na wymaganiach powiedz, że musisz zweryfikować niektóre pola w odniesieniu do logiki biznesowej. Chociaż możesz mieć teraz określone wymagania, mocno podejrzewasz, że będą ewoluować. Możesz zawrzeć aktualną logikę we własnej procedurze / funkcji / regule / klasie.
Chociaż jest to problem ortogonalny, który nie jest częścią „enkapsulacji tego, co się różni”, często naturalne jest wyodrębnienie, które jest sparametryzowane przez teraz enkapsulowaną logikę. Zwykle prowadzi to do bardziej elastycznego kodu i pozwala na zmianę logiki poprzez zastąpienie jej alternatywną implementacją zamiast modyfikowania logiki zamkniętej.