L'ereditarietà multipla viola il singolo principio di responsabilità?


18

Se hai una classe che eredita da due classi distinte, ciò non significa che la tua sottoclasse faccia automaticamente (almeno) 2 cose, una per ogni superclasse?

Credo che non ci sia differenza se si dispone di ereditarietà di più interfacce.

Modifica: per essere chiari, credo che se la sottoclasse di più classi viola SRP, l'implementazione di più interfacce (non marcatore o interfaccia di base (es. Comparable)) viola anche SRP.


Giusto per essere chiari, credi anche che l'implementazione di più interfacce viola l'SRP?

Credo che se la sottoclasse di più classi viola SRP, l'implementazione di più interfacce (non marker) viola anche SRP.
m3th0dman,

Voterei questa domanda, ma hai incorporato la tua opinione nella domanda, con la quale non sono d'accordo.
Nicole,

1
@NickC: non ho detto la mia opinione (avresti dovuto leggere il commento sopra); Modificherò la domanda per renderla più chiara.
m3th0dman

1
@NickC Non ho un'opinione, ecco perché l'ho chiesto. Ho solo detto che i due sono correlati (eredità dell'interfaccia ed eredità di classe); se viola per l'eredità di classe, allora viola anche per l'eredità di interfaccia, se non lo fa per uno, non viola nemmeno per l'altro. E non mi interessa il voto
positivo

Risposte:


17

In senso molto stretto, la risposta è "Sì": supponendo che le classi o le interfacce di base siano progettate per un unico scopo, ereditarle entrambe crea una classe con responsabilità multiple. Tuttavia, che si tratti o meno di "una cosa negativa" dipende dalla natura delle classi o delle interfacce che si stanno ereditando.

Puoi suddividere le classi e le interfacce in due gruppi principali: quelli che affrontano la complessità essenziale del tuo sistema e quelli che affrontano la sua complessità accidentale. Se ereditato da più di una classe di "complessità essenziale", è male; se erediti da una "essenziale" e una o più classi "accidentali", va bene.

Ad esempio, in un sistema di fatturazione potresti avere classi per rappresentare fatture e cicli di fatturazione (affrontano la complessità essenziale) e classi per oggetti persistenti (affrontano la complessità accidentale). Se erediti in questo modo

class BillingCycleInvoice : public BillingCycle, public Invoice {
};

è un male: hai BillingCycleInvoiceuna responsabilità mista in relazione alla complessità essenziale del sistema.

D'altra parte, se erediti in questo modo

class PersistentInvoice : public Invoice, public PersistentObject {
};

la tua classe è OK: tecnicamente, serve due preoccupazioni contemporaneamente, ma poiché solo una di esse è essenziale, puoi cancellare l'eredità accidentale come "costo per fare affari".


3
Il tuo secondo esempio è molto simile a quello che sarebbe una preoccupazione trasversale nella programmazione orientata all'aspetto. Questo sta dicendo che i tuoi oggetti a volte hanno bisogno di fare cose (come la registrazione, i test di controllo degli accessi o la persistenza) che non sono il loro focus principale e che puoi farlo con eredità multipla. Questo è un uso appropriato dello strumento.

Il più semplice è che se l'implementazione di una nuova interfaccia richiede il completamento della funzionalità della classe, la sua no. Ma se si aggiunge una nuova responsabilità Implementare l'interfaccia con qualche altra classe quindi iniettare come dipendenza non violerà mai SRP
Ramankingdom

9

L'SRP è una linea guida per evitare le divinità, che sono cattive, ma può anche essere presa troppo alla lettera e un progetto inizia a palloncino con tonnellate di classi che non possono davvero fare nulla senza una manciata combinata. Eredità multipla / interfacce multiple possono essere un segno che una classe sta diventando troppo grande, ma potrebbe anche essere un segno che l'attenzione è troppo granulare per l'attività a portata di mano e la soluzione è troppo ingegnerizzata, o potrebbe semplicemente essere perfettamente caso d'uso valido per eredità multipla e le preoccupazioni non sono fondate.

La progettazione del software è tanto arte quanto la scienza, è un atto di bilanciamento di molti interessi in competizione. Abbiamo delle regole per aiutare a evitare che le cose che sappiamo siano troppo lontane in una direzione che causano problemi, ma quelle regole possono facilmente portarci in un posto altrettanto brutto o peggiore di quello che stavamo cercando di evitare in primo luogo.


4

Un po '. Concentriamoci sul principio principale di SRP:

Una classe dovrebbe avere solo un motivo per cambiare.

L'eredità multipla non forza questo, ma tende a condurlo. Se la classe sovrascrive o implementa le sue classi di base, ciò tende ad essere più motivi per cambiare. In questo modo, l'ereditarietà multipla dell'interfaccia tende ad essere più problematica dell'ereditarietà di classe. Se due classi vengono ereditate ma non sovrascritte, le ragioni per cambiare esistono nelle classi di base e non nella nuova classe.

I luoghi in cui l'ereditarietà multipla non viola SRP è quando tutti i tipi di base tranne uno non sono qualcosa che la classe sta implementando, ma fungono da tratto che la classe sembra avere. Considerare IDisposablein C #. Le cose usa e getta non hanno due responsabilità, l'interfaccia funge solo da vista in classi che condividono il tratto ma hanno responsabilità decisamente diverse.


2

Non secondo me. Per me, una sola responsabilità è "modellare un utente della mia applicazione". Potrebbe essere necessario fornire tali dati tramite un servizio Web e archiviarli in un database relazionale. Potrei fornire quella funzionalità ereditando un paio di classi di mix-in.

Ciò non significa che la classe abbia tre responsabilità. Significa che parte della responsabilità di modellare un utente richiede il supporto della serializzazione e della persistenza.


2

Affatto. In Java, puoi avere un'interfaccia che rappresenta una sorta di oggetto dominio - un Foo, per esempio. Potrebbe essere necessario confrontarlo con altri oggetti Foo e quindi implementa Comparable (con la logica di confronto esternalizzata in una classe Comparator); può darsi che anche questo oggetto debba essere serializzabile, in modo che possa essere trasportato attraverso la rete; e potrebbe essere necessario essere clonabile. Pertanto la classe implementa tre interfacce ma non ha alcuna logica interna per supportarle oltre a quella supportata da Foo.

Detto questo, è sciocco portare l'SRP agli estremi. Se una classe definisce più di un metodo, sta violando l'SRP? Tutti gli oggetti Java hanno un metodo toString () e un metodo equals (Object), il che significa che OGNI oggetto in Java è responsabile sia dell'uguaglianza che dell'autorappresentazione. È una brutta cosa?


1

Immagino che dipenda da come definisci la responsabilità. Nel classico esempio di iostream non viola l'SRP. L'IOstream è responsabile della gestione di un flusso bidirezionale.

Dopo aver esaurito le responsabilità primitive, si passa a responsabilità di livello più generico, dopo tutto come si definisce la responsabilità del punto di ingresso principale? La sua responsabilità deve essere "essere il punto di accesso principale" e non "tutto ciò che fa la mia app", altrimenti è impossibile non violare l'SRP e produrre un'applicazione.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.