Cechy to kolejny sposób na komponowanie. Pomyśl o nich jako o sposobie komponowania wszystkich części klasy w czasie kompilacji (lub w czasie kompilacji JIT), składając konkretne implementacje potrzebnych części.
Zasadniczo chcesz korzystać z cech, gdy tworzysz zajęcia z różnymi kombinacjami funkcji. Sytuacja ta pojawia się najczęściej u osób piszących elastyczne biblioteki, z których mogą korzystać inni. Na przykład, oto deklaracja klasy testu jednostkowego, którą ostatnio napisałem za pomocą ScalaTest :
class TestMyClass
extends WordSpecLike
with Matchers
with MyCustomTrait
with BeforeAndAfterAll
with BeforeAndAfterEach
with ScalaFutures
Struktury testów jednostkowych mają mnóstwo różnych opcji konfiguracji, a każdy zespół ma inne preferencje co do tego, jak chcą to robić. Umieszczając opcje w cechach (które są mieszane przy użyciu with
w Scali), ScalaTest może zaoferować wszystkie te opcje bez konieczności tworzenia nazw klas, takich jak WordSpecLikeWithMatchersAndFutures
, lub ton flagi logicznej środowiska wykonawczego, takich jak WordSpecLike(enableFutures, enableMatchers, ...)
. Ułatwia to przestrzeganie zasady otwartej / zamkniętej . Możesz dodać nowe funkcje i nowe kombinacje funkcji, po prostu dodając nową cechę. Ułatwia także przestrzeganie zasady segregacji interfejsów , ponieważ łatwo można umieścić w funkcji cechy, które nie są powszechnie potrzebne.
Cechy są również dobrym sposobem na umieszczenie wspólnego kodu w kilku klasach, które nie mają sensu współdzielić hierarchii dziedziczenia. Dziedziczenie jest związkiem bardzo ściśle powiązanym i nie powinieneś ponosić tych kosztów, jeśli możesz mu pomóc. Cechy są relacją znacznie bardziej luźno powiązaną. W powyższym przykładzie MyCustomTrait
z łatwością korzystałem z udawanej implementacji bazy danych między kilkoma niezwiązanymi ze sobą klasami testów.
Wstrzykiwanie zależności osiąga wiele takich samych celów, ale w czasie wykonywania na podstawie danych wejściowych użytkownika zamiast w czasie kompilacji na podstawie danych programisty. Cechy są również przeznaczone bardziej dla zależności, które są semantycznie częścią tej samej klasy. Gromadzisz części jednej klasy, a nie dzwonisz do innych klas z innymi obowiązkami.
Ramy wstrzykiwania zależności osiągają wiele takich samych celów w czasie kompilacji w oparciu o dane programistyczne, ale w dużej mierze są obejściem dla języków programowania bez odpowiedniego wsparcia cech. Cechy wprowadzają te zależności do dziedziny sprawdzania typu kompilatora, z czystszą składnią, z prostszym procesem kompilacji, który czyni wyraźniejsze rozróżnienie między zależnościami czasu kompilacji i środowiska wykonawczego.