A herança múltipla viola o princípio de responsabilidade única?

18

Se você tem uma classe que herda de duas classes distintas, isso não significa que sua subclasse faz automaticamente (pelo menos) duas coisas, uma de cada superclasse?

Eu acredito que não há diferença se você tem herança de interface múltipla.

Edit: Para ser claro, acredito que se a subclasse de várias classes viola o SRP, a implementação de várias interfaces (sem marcador ou interface básica (por exemplo, comparável)) também viola o SRP.

m3th0dman
fonte
Só para esclarecer, você também acredita que implementar várias interfaces viola o SRP?
Acredito que se a subclasse de várias classes viola o SRP, a implementação de várias interfaces (sem marcador) também viola o SRP.
M3th0dman
Gostaria de aprovar esta pergunta, mas você incorporou sua opinião na pergunta, com a qual não concordo.
7779 Nicole
1
@ NickC: Eu não disse minha opinião (você deveria ter lido o comentário acima); Vou editar a pergunta para torná-la mais clara.
M3th0dman
1
@ NickC Eu não tenho uma opinião, é por isso que perguntei. Eu apenas disse que os dois estão relacionados (herança de interface e herança de classe); se viola a herança de classe, também viola a herança de interface; se não violar uma, também não violar a outra. E eu não me importo com o voto-se ...
m3th0dman

Respostas:

17

Em um sentido muito restrito, a resposta é "Sim": supondo que suas classes ou interfaces base sejam projetadas para um único propósito, herdar as duas cria uma classe com múltiplas responsabilidades. No entanto, se é ou não uma "coisa ruim" depende da natureza das classes ou interfaces que você está herdando.

Você pode particionar suas classes e interfaces em dois grupos principais - os que abordam a complexidade essencial do seu sistema e os que abordam a complexidade acidental. Se herdar de mais de uma classe de "complexidade essencial", isso é ruim; se você herdar de uma classe "essencial" e uma ou mais classes "acidentais", tudo bem.

Por exemplo, em um sistema de faturamento, você poderia ter classes para representar faturas e ciclos de faturamento (eles abordam a complexidade essencial) e classes para objetos persistentes (eles abordam a complexidade acidental). Se você herdar assim

class BillingCycleInvoice : public BillingCycle, public Invoice {
};

é ruim: você BillingCycleInvoicetem uma responsabilidade mista no que se refere à complexidade essencial do sistema.

Por outro lado, se você herdar assim

class PersistentInvoice : public Invoice, public PersistentObject {
};

sua turma é boa: tecnicamente, atende duas preocupações ao mesmo tempo, mas como apenas uma delas é essencial, você pode considerar a herança da acidental como o "custo de fazer negócios".

dasblinkenlight
fonte
3
Seu segundo exemplo é muito semelhante ao que seria uma preocupação transversal na programação orientada a aspectos. Isso significa que, às vezes, seus objetos precisam fazer coisas (como log, testes de controle de acesso ou persistência) que não sejam o foco principal e que você pode fazer isso com herança múltipla. Esse é um uso apropriado da ferramenta.
O mais simples é: se a implementação de uma nova interface exigir a conclusão da funcionalidade da classe, não será. Mas se a sua adição nova responsabilidade implementar a interface com alguma outra classe, em seguida, injetar a dependência nunca violar SRP
Ramankingdom
9

O SRP é uma diretriz para evitar as classes divinas, que são ruins, mas também pode ser interpretado de maneira muito literal e um projeto inicia um balão com toneladas de classes que realmente não podem fazer nada sem que um punhado seja combinado. Herança múltipla / interfaces múltiplas podem ser um sinal de que uma classe está ficando muito grande, mas também pode ser um sinal de que seu foco é granular demais para a tarefa em questão e que sua solução está com excesso de engenharia ou simplesmente pode ser uma solução perfeita. caso de uso válido para herança múltipla e suas preocupações são infundadas.

O design de software é tanto arte quanto ciência, é um ato de equilíbrio de muitos interesses concorrentes. Temos regras para ajudar a evitar coisas que sabemos que estão longe em uma direção que causam problemas, mas essas regras podem facilmente nos levar a um lugar tão ruim ou pior quanto o que estávamos tentando evitar em primeiro lugar.

Ryathal
fonte
4

Um pouco. Vamos nos concentrar no princípio principal do SRP:

Uma classe deve ter apenas um motivo para mudar.

A herança múltipla não força isso, mas tende a levar a ela. Se a classe substitui ou implementa suas classes base, isso tende a ser mais razões para mudar. Dessa maneira, a herança múltipla da interface tende a ser mais problemática do que a herança de classe. Se duas classes são herdadas, mas não substituídas, os motivos para mudar existem nas classes base e não na nova classe.

Os locais onde a herança múltipla não viola o SRP é quando todos, exceto um dos tipos básicos, não são algo que a classe está implementando, mas agem como uma característica que a classe possui. Considere IDisposableem c #. As coisas descartáveis ​​não têm duas responsabilidades; a interface funciona apenas como uma visão das classes que compartilham o traço, mas que têm responsabilidades decididamente diferentes.

Telastyn
fonte
2

Não na minha opinião. Para mim, uma única responsabilidade é "modelar um usuário do meu aplicativo". Talvez eu precise entregar esses dados por meio de um serviço da Web e armazená-los em um banco de dados relacional. Eu poderia fornecer essa funcionalidade herdando algumas classes mix-in.

Isso não significa que a classe tenha três responsabilidades. Isso significa que parte da responsabilidade de modelar um usuário requer suporte à serialização e persistência.

Kevin Cline
fonte
2

De modo nenhum. Em Java, você pode ter uma interface que represente algum tipo de objeto de domínio - um Foo, por exemplo. Pode precisar ser comparado a outros objetos Foo e, portanto, implementa Comparable (com a lógica de comparação externalizada em uma classe Comparator); pode ser que esse objeto também precise ser serializável, para que possa ser transportado pela rede; e pode precisar ser clonável. Assim, a classe implementa três interfaces, mas não possui lógica para suportá-las além da suportada pelo Foo.

Dito isto, é tolice levar o SRP a extremos. Se uma classe define mais de um método, está violando o SRP? Todos os objetos Java possuem um método toString () e um método equals (Object), o que significa que TODOS os objetos em Java são responsáveis ​​pela igualdade e pela auto-representação. Isso é uma coisa ruim?

Matthew Flynn
fonte
1

Eu acho que depende de como você define a responsabilidade. No exemplo clássico do iostream, ele não viola o SRP. O IOstream é responsável por gerenciar um fluxo bidirecional.

Depois de ficar sem responsabilidades primitivas, você passa para responsabilidades mais genéricas de alto nível, afinal como você define a responsabilidade do seu ponto de entrada principal? Sua responsabilidade deve ser 'ser o principal ponto de entrada' e não 'tudo o que meu aplicativo faz', caso contrário, é impossível não violar o SRP e produzir um aplicativo.

pedregoso
fonte