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 withw 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 MyCustomTraitz ł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.