¿La herencia múltiple viola el principio de responsabilidad única?

18

Si tiene una clase que hereda de dos clases distintas, ¿no significa que su subclase automáticamente hace (al menos) 2 cosas, una de cada superclase?

Creo que no hay diferencia si tiene herencia de interfaz múltiple.

Editar: Para ser claros, creo que si la subclasificación de varias clases viola SRP, la implementación de múltiples interfaces (sin marcador o interfaz básica (por ejemplo, Comparable)) también viola SRP.

m3th0dman
fuente
Para ser claros, ¿también crees que implementar múltiples interfaces viola el SRP?
Creo que si subclasificar varias clases viola SRP, entonces la implementación de múltiples interfaces (sin marcador) también viola SRP.
m3th0dman
Votaría esta pregunta, pero has incrustado tu opinión en la pregunta, con la que no estoy de acuerdo.
Nicole
1
@NickC: No he dicho mi opinión (deberías haber leído el comentario anterior); Editaré la pregunta para que quede más clara.
m3th0dman
1
@NickC No tengo una opinión, por eso pregunté. Solo dije que los dos están relacionados (herencia de interfaz y herencia de clase); si viola la herencia de clase, entonces también viola la herencia de la interfaz, si no lo hace para una, entonces tampoco viola para la otra. Y no me importa el voto
positivo

Respuestas:

17

En un sentido muy limitado, la respuesta es "Sí": suponiendo que sus clases o interfaces base están diseñadas para un solo propósito, heredar ambas crea una clase con múltiples responsabilidades. Sin embargo, si es "algo malo" o no depende de la naturaleza de las clases o interfaces que está heredando.

Puede dividir sus clases e interfaces en dos grupos principales: los que abordan la complejidad esencial de su sistema y los que abordan su complejidad accidental. Si hereda de más de una clase de "complejidad esencial", es malo; si hereda de una clase "esencial" y una o más clases "accidentales", está bien.

Por ejemplo, en un sistema de facturación, podría tener clases para representar facturas y ciclos de facturación (abordan la complejidad esencial) y clases para objetos persistentes (abordan la complejidad accidental). Si heredas así

class BillingCycleInvoice : public BillingCycle, public Invoice {
};

es malo: BillingCycleInvoicetiene una responsabilidad mixta en relación con la complejidad esencial del sistema.

Por otro lado, si heredas así

class PersistentInvoice : public Invoice, public PersistentObject {
};

su clase está bien: técnicamente, atiende dos inquietudes a la vez, pero como solo una de ellas es esencial, puede descartar heredar la accidental como el "costo de hacer negocios".

dasblinkenlight
fuente
3
Su segundo ejemplo es muy similar a lo que sería una preocupación transversal en la programación orientada a aspectos. Esto significa que sus objetos a veces necesitan hacer cosas (como iniciar sesión, pruebas de control de acceso o persistencia) que no son su foco principal, y que puede hacerlo con herencia múltiple. Ese es un uso apropiado de la herramienta.
Lo más simple es Si implementar una nueva interfaz requiere completar la funcionalidad de la clase, entonces no lo es. Pero si agrega nueva responsabilidad Implemente la interfaz con otra clase y luego inyecte, ya que la dependencia nunca violará SRP
Ramankingdom
9

El SRP es una guía para evitar las clases divinas, que son malas, pero también puede tomarse de manera demasiado literal y un proyecto comienza con toneladas de clases que realmente no pueden hacer nada sin un puñado combinado. La herencia múltiple / las interfaces múltiples pueden ser una señal de que una clase se está volviendo demasiado grande, pero también podría ser una señal de que su enfoque es demasiado granular para la tarea en cuestión y su solución está sobredimensionada, o simplemente podría ser una solución perfecta. caso de uso válido para herencia múltiple y sus preocupaciones son infundadas.

El diseño de software es tanto arte como ciencia, es un acto de equilibrio de muchos intereses en competencia. Tenemos reglas para ayudar a evitar cosas que sabemos que están demasiado lejos en una dirección que causan problemas, pero esas reglas pueden llevarnos fácilmente a un lugar que es tan malo o peor que lo que estábamos tratando de evitar en primer lugar.

Ryathal
fuente
4

Algo. Centrémonos en el principio principal de SRP:

Una clase solo debe tener una razón para cambiar.

La herencia múltiple no fuerza esto, pero tiende a conducir a ello. Si la clase anula o implementa sus clases base, eso tiende a ser más razones para que cambie. De esta manera, la herencia múltiple de interfaz tiende a ser más problemática que la herencia de clase. Si se heredan dos clases pero no se anulan, entonces las razones para cambiar existen en las clases base, no en la nueva clase.

Los lugares donde la herencia múltiple no viola SRP es cuando todos menos uno de los tipos base no son algo que la clase está implementando, sino que actúan como un rasgo que la clase tiene. Considere IDisposableen C #. Las cosas que son desechables no tienen dos responsabilidades, la interfaz solo actúa como una vista de clases que comparten el rasgo pero tienen responsabilidades decididamente diferentes.

Telastyn
fuente
2

No en mi opinión Para mí, una responsabilidad única es "modelar un usuario de mi aplicación". Es posible que necesite entregar esos datos a través de un servicio web y almacenarlos en una base de datos relacional. Podría proporcionar esa funcionalidad heredando un par de clases mixtas.

Eso no significa que la clase tenga tres responsabilidades. Significa que parte de la responsabilidad de modelar un usuario requiere soporte de serialización y persistencia.

Kevin Cline
fuente
2

De ningún modo. En Java, puede tener una interfaz que represente algún tipo de objeto de dominio, un Foo, por ejemplo. Es posible que deba compararse con otros objetos Foo, por lo que implementa Comparable (con la lógica de comparación externalizada en una clase Comparator); puede ser que este objeto también necesite ser serializable, de modo que pueda transportarse a través de la red; y puede necesitar ser clonable. Por lo tanto, la clase implementa tres interfaces pero no tiene ninguna lógica interna para admitirlas más allá de la que admite Foo.

Dicho esto, es una tontería llevar el SRP a los extremos. Si una clase define más de un método, ¿está violando el SRP? Todos los objetos Java tienen un método toString () y un método igual (Object), lo que significa que CADA objeto en Java es responsable de la igualdad y la autorrepresentación. ¿Es eso algo malo?

Matthew Flynn
fuente
1

Supongo que depende de cómo se defina la responsabilidad. En el ejemplo clásico de iostream, no viola el SRP. El IOstream es responsable de administrar un flujo bidireccional.

Después de quedarse sin responsabilidades primitivas, pasa a responsabilidades de alto nivel más genéricas, después de todo, ¿cómo define la responsabilidad de su punto de entrada principal? Su responsabilidad debe ser "ser el punto de entrada principal", no "todo lo que hace mi aplicación", de lo contrario es imposible no violar el SRP y producir una aplicación.

metal de piedra
fuente