Jeśli piszesz kod, który używa wielu pięknych, niezmiennych struktur danych, klasy przypadków wydają się być darem niebios, oferując za darmo wszystkie poniższe elementy za pomocą tylko jednego słowa kluczowego:
- Domyślnie wszystko niezmienne
- Gettery definiowane automatycznie
- Przyzwoita implementacja metody toString ()
- Zgodne równa się () i hashCode ()
- Obiekt towarzyszący z metodą unapply () do dopasowania
Ale jakie są wady definiowania niezmiennej struktury danych jako klasy przypadku?
Jakie ograniczenia nakłada na klasę lub jej klientów?
Czy są sytuacje, w których wolisz zajęcia bez przypadków?
scala
case-class
Graham Lea
źródło
źródło
Odpowiedzi:
Jedna duża wada: klasy przypadków nie mogą rozszerzać klasy przypadku. To jest ograniczenie.
Inne pominięte korzyści, wymienione dla kompletności: zgodna serializacja / deserializacja, brak potrzeby używania słowa kluczowego „new” do tworzenia.
Preferuję klasy bez wielkości liter dla obiektów ze zmiennym stanem, stanem prywatnym lub bez stanu (np. Większość pojedynczych komponentów). Klasy przypadków dla prawie wszystkiego innego.
źródło
Najpierw dobre bity:
Domyślnie wszystko niezmienne
Tak, a nawet można go zastąpić (używając
var
), jeśli tego potrzebujeszGettery definiowane automatycznie
Możliwe w dowolnej klasie, poprzedzając parametry przedrostkiem
val
Godna
toString()
realizacjaTak, bardzo przydatne, ale w razie potrzeby możliwe do wykonania ręcznie na dowolnej klasie
Zgodny
equals()
ihashCode()
W połączeniu z łatwym dopasowywaniem wzorców jest to główny powód, dla którego ludzie używają klas przypadków
Obiekt towarzyszący z
unapply()
metodą dopasowaniaMożliwe również ręczne na dowolnej klasie przy użyciu ekstraktorów
Ta lista powinna również zawierać niezwykle potężną metodę kopiowania, jedną z najlepszych rzeczy, jakie można znaleźć w Scali 2.8
Z drugiej strony jest tylko kilka rzeczywistych ograniczeń z klasami przypadków:
Nie można zdefiniować
apply
w obiekcie towarzyszącym przy użyciu tego samego podpisu, co metoda wygenerowana przez kompilatorJednak w praktyce rzadko stanowi to problem. Zmiana zachowania wygenerowanej metody Apply gwarantuje, że zaskoczy użytkowników i powinna być zdecydowanie odradzana, jedynym uzasadnieniem takiego działania jest walidacja parametrów wejściowych - zadanie najlepiej wykonać w głównej treści konstruktora (która również udostępnia walidację podczas używania
copy
)Nie możesz podklasy
To prawda, chociaż nadal istnieje możliwość, aby klasa przypadku sama była potomkiem. Jednym z powszechnych wzorców jest zbudowanie hierarchii klas cech, używając klas przypadków jako węzłów liści drzewa.
Warto również zwrócić uwagę na
sealed
modyfikator. Każda podklasa cechy z tym modyfikatorem musi być zadeklarowana w tym samym pliku. Podczas dopasowywania wzorców do wystąpień cechy kompilator może następnie ostrzec Cię, jeśli nie sprawdziłeś wszystkich możliwych konkretnych podklas. W połączeniu z klasami przypadków może to zapewnić bardzo wysoki poziom zaufania do kodu, jeśli kompiluje się bez ostrzeżenia.Jako podklasa Product, klasy przypadków nie mogą mieć więcej niż 22 parametry
Nie ma prawdziwego obejścia, z wyjątkiem zaprzestania nadużywania klas z tak wieloma parametrami :)
Również...
Innym zauważanym czasem ograniczeniem jest to, że Scala (obecnie) nie obsługuje leniwych parametrów (takich jak
lazy val
s, ale jako parametry). Obejściem tego problemu jest użycie parametru by-name i przypisanie go do leniwej wartości w konstruktorze. Niestety, parametry według nazwy nie mieszają się z dopasowywaniem wzorców, co zapobiega używaniu tej techniki z klasami przypadków, ponieważ przerywa działanie ekstraktora generowanego przez kompilator.Jest to istotne, jeśli chcesz zaimplementować wysoce funkcjonalne leniwe struktury danych i miejmy nadzieję, zostanie rozwiązane przez dodanie leniwych parametrów do przyszłej wersji Scali.
źródło
Myślę, że ma tu zastosowanie zasada TDD: nie projektuj nadmiernie. Kiedy deklarujesz coś jako a
case class
, deklarujesz wiele funkcji. Zmniejszy to elastyczność, jaką masz przy zmianie klasy w przyszłości.Na przykład a
case class
maequals
metodę nad parametrami konstruktora. Możesz nie przejmować się tym, kiedy po raz pierwszy piszesz swoją klasę, ale później możesz zdecydować, że chcesz, aby równość ignorowała niektóre z tych parametrów lub zrobiła coś nieco innego. Jednak kod klienta może zostać napisany w średnim czasie, który zależy odcase class
równości.źródło
Martin Odersky daje nam dobry punkt wyjścia w swoim kursie Zasady programowania funkcjonalnego w Scali (Wykład 4.6 - Dopasowywanie wzorców), z którego moglibyśmy skorzystać, gdy musimy wybrać między klasą a klasą przypadku. Rozdział 7 książki Scala By Example zawiera ten sam przykład.
Ponadto dodanie nowej klasy Prod nie pociąga za sobą żadnych zmian w istniejącym kodzie:
Natomiast dodanie nowej metody wymaga modyfikacji wszystkich istniejących klas.
Ten sam problem rozwiązany z klasami przypadków.
Dodanie nowej metody jest zmianą lokalną.
Dodanie nowej klasy Prod wymaga potencjalnie zmiany całego dopasowania wzorców.
Transkrypcja z wykładu wideo 4.6 Dopasowywanie wzorców
Pamiętaj: musimy traktować to jako punkt wyjścia, a nie jako jedyne kryteria.
źródło
Cytuję z tego
Scala cookbook
przezAlvin Alexander
Rozdział 6:objects
.To jedna z wielu rzeczy, które wydały mi się interesujące w tej książce.
Aby zapewnić wiele konstruktorów dla klasy case, ważne jest, aby wiedzieć, co faktycznie robi deklaracja klasy case.
Jeśli spojrzysz na kod, który kompilator Scala generuje dla przykładu klasy sprawy, zobaczysz, że tworzy on dwa pliki wyjściowe, Person $ .class i Person.class. Jeśli zdemontujesz Person $ .class za pomocą polecenia javap, zobaczysz, że zawiera ona metodę stosującą, a także wiele innych:
Możesz również zdemontować Person.class, aby zobaczyć, co zawiera. W przypadku takiej prostej klasy zawiera 20 dodatkowych metod; ten ukryty nadmiar jest jednym z powodów, dla których niektórzy programiści nie lubią klas przypadków.
źródło