Quando utilizzare i tratti, al contrario dell'ereditarietà e della composizione?

7

Ci sono tre modi comuni, AFAIK, per implementare la riusabilità quando si tratta di OOP

  1. Eredità: di solito rappresentare è-una relazione (un'anatra è-un uccello)
  2. Composizione: di solito per rappresentare ha-una relazione (un'auto ha un motore)
  3. Tratti (ad esempio la parola chiave trait in PHP): ... non sono proprio sicuro di questo

Mentre mi sembra che i tratti possano implementare entrambe le relazioni has-a ed is-a, non sono proprio sicuro di quale tipo di modellazione fosse destinato. Per quale tipo di situazioni sono stati progettati i tratti?

    
posta Extrakun 21.03.2016 - 08:27
fonte

1 risposta

4

I tratti sono un altro modo di fare composizione. Pensateli come un modo per comporre tutte le parti di una classe in fase di compilazione (o tempo di compilazione JIT), assemblando le implementazioni concrete delle parti di cui avrete bisogno.

Fondamentalmente, vuoi usare i tratti quando ti trovi a creare lezioni con diverse combinazioni di funzionalità. Questa situazione si presenta più spesso per le persone che scrivono librerie flessibili da utilizzare per gli altri. Ad esempio, ecco la dichiarazione di una classe di test unitario che ho scritto di recente usando ScalaTest :

class TestMyClass
  extends WordSpecLike
  with Matchers
  with MyCustomTrait
  with BeforeAndAfterAll
  with BeforeAndAfterEach
  with ScalaFutures

I quadri di test unitario hanno un ton di diverse opzioni di configurazione, e ogni squadra ha preferenze diverse su come vogliono fare le cose. Inserendo le opzioni in tratti (che sono mescolati usando with in Scala), ScalaTest può offrire tutte queste opzioni senza dover creare nomi di classi come WordSpecLikeWithMatchersAndFutures o una tonnellata di flag booleani di runtime come WordSpecLike(enableFutures, enableMatchers, ...) . In questo modo è facile seguire il principio Aperto / chiuso . Puoi aggiungere nuove funzionalità e nuove combinazioni di funzionalità semplicemente aggiungendo un nuovo tratto. Rende anche più semplice seguire il principio di segregazione dell'interfaccia , perché puoi facilmente inserire funzioni non necessarie universalmente 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 stretta, e non si dovrebbe pagare quel costo se si può aiutare. I tratti sono una relazione molto più liberamente accoppiata. Nel mio esempio precedente, ho utilizzato MyCustomTrait per condividere facilmente un'implementazione di un database fittizio tra diverse classi di test altrimenti non correlate.

L'iniezione di dipendenza raggiunge molti degli stessi obiettivi, ma in fase di esecuzione basata sull'input dell'utente anziché sul momento della compilazione basato sull'input del programmatore. I tratti sono anche intesi più per le dipendenze che fanno parte semanticamente della stessa classe. Stai assemblando le parti di una classe piuttosto che chiamare in altre classi con altre responsabilità.

L'iniezione di dipendenza framework raggiunge molti degli stessi obiettivi in fase di compilazione in base all'input del programmatore, ma è in gran parte una soluzione alternativa per programmare linguaggi senza il supporto del tratto appropriato. I tratti portano queste dipendenze nel regno del controllo di tipo del compilatore, con una sintassi più pulita, con un processo di compilazione più semplice, che distingue più chiaramente le dipendenze in fase di compilazione e di runtime.

    
risposta data 21.03.2016 - 19:35
fonte

Leggi altre domande sui tag