I tratti sono un altro modo di fare composizione. Pensa a loro come un modo per comporre tutte le parti di una classe in fase di compilazione (o in fase di compilazione JIT), assemblando le implementazioni concrete delle parti necessarie.
Fondamentalmente, vuoi usare i tratti quando ti ritrovi a fare lezioni con diverse combinazioni di caratteristiche. Questa situazione si presenta più spesso per le persone che scrivono librerie flessibili che gli altri possono consumare. Ad esempio, ecco la dichiarazione di una classe di test unit che ho scritto di recente usando ScalaTest :
class TestMyClass
extends WordSpecLike
with Matchers
with MyCustomTrait
with BeforeAndAfterAll
with BeforeAndAfterEach
with ScalaFutures
I framework di unit test hanno moltissime opzioni di configurazione diverse e ogni team ha preferenze diverse su come vogliono fare le cose. Mettendo le opzioni in tratti (che si mescolano usando with
in Scala), ScalaTest può offrire tutte quelle opzioni senza dover creare nomi di classe simili WordSpecLikeWithMatchersAndFutures
, o una tonnellata di flag booleani di runtime come WordSpecLike(enableFutures, enableMatchers, ...)
. Ciò semplifica il rispetto del principio di apertura / chiusura . Puoi aggiungere nuove funzionalità e nuove combinazioni di funzionalità semplicemente aggiungendo un nuovo tratto. Inoltre, semplifica il rispetto del principio di segregazione dell'interfaccia , poiché puoi facilmente inserire funzioni non universalmente necessarie in un tratto.
I tratti sono anche un buon modo per inserire codice comune in diverse classi che non hanno senso condividere una gerarchia di ereditarietà. L'ereditarietà è una relazione molto unita e non dovresti pagare quel costo se puoi aiutarlo. I tratti sono una relazione molto più debole. Nel mio esempio precedente, MyCustomTrait
condividevo facilmente un'implementazione del database finta tra diverse classi di test altrimenti non correlate.
L'iniezione di dipendenza raggiunge molti degli stessi obiettivi, ma in fase di esecuzione in base all'input dell'utente anziché al momento della compilazione in base all'input del programmatore. I tratti sono anche destinati più alle dipendenze che fanno parte semanticamente della stessa classe. Stai un po 'assemblando le parti di una classe piuttosto che effettuare chiamate verso altre classi con altre responsabilità.
I framework di iniezione delle dipendenze raggiungono molti degli stessi obiettivi in fase di compilazione in base all'input del programmatore, ma sono in gran parte una soluzione alternativa per i linguaggi di programmazione senza un adeguato supporto del tratto. I tratti portano queste dipendenze nel regno del controllo del tipo del compilatore, con una sintassi più pulita, con un processo di compilazione più semplice, che fa una distinzione più chiara tra dipendenze in fase di compilazione e di runtime.