Dado que o Python permite herança múltipla, como é a herança idiomática no Python?
Em linguagens com herança única, como Java, a herança seria usada quando se poderia dizer que um objeto "é-a" de outro objeto e você deseja compartilhar código entre os objetos (do objeto pai ao objeto filho). Por exemplo, você poderia dizer que Dog
é um Animal
:
public class Animal {...}
public class Dog extends Animal {...}
Porém, como o Python suporta herança múltipla, podemos criar um objeto compondo muitos outros objetos juntos. Considere o exemplo abaixo:
class UserService(object):
def validate_credentials(self, username, password):
# validate the user credentials are correct
pass
class LoggingService(object):
def log_error(self, error):
# log an error
pass
class User(UserService, LoggingService):
def __init__(self, username, password):
self.username = username
self.password = password
def authenticate(self):
if not super().validate_credentials(self.username, self.password):
super().log_error('Invalid credentials supplied')
return False
return True
Esse é um uso aceitável ou bom de herança múltipla em Python? Em vez de dizer que herança é quando um objeto "é-a" de outro objeto, criamos um User
modelo composto por UserService
e LoggingService
.
Toda a lógica para operações de banco de dados ou rede pode ser mantida separada do User
modelo, colocando-as no UserService
objeto e mantendo toda a lógica para efetuar login no LoggingService
.
Vejo alguns problemas com essa abordagem:
- Isso cria um objeto de Deus? Uma vez que
User
herda, ou é composta por,UserService
eLoggingService
está realmente seguindo o princípio da responsabilidade única? - Para acessar métodos em um objeto pai / próxima linha (por exemplo,
UserService.validate_credentials
temos que usarsuper
. Isso torna um pouco mais difícil ver qual objeto vai lidar com esse método e não é tão claro quanto, por exemplo, , instanciandoUserService
e fazendo algo comoself.user_service.validate_credentials
Qual seria a maneira Pythonic de implementar o código acima?
User
objetos têm em suas interfaces não apenas os membros definidos naUser
classe, mas também os definidos emUserService
eLoggingService
. Este não é um relacionamento "tem-a", porque a interface pública é copiada (embora não via cópia direta, mas sim por uma pesquisa indireta nas interfaces das superclasses).Além do fato de permitir várias superclasses, a herança do Python não é substancialmente diferente da Java, ou seja, membros de uma subclasse também são membros de cada um de seus supertipos [1]. O fato de o Python usar a digitação com patos também não faz diferença: sua subclasse possui todos os membros de suas superclasses, portanto, pode ser usada por qualquer código que possa usar essas subclasses. O fato de a herança múltipla ser efetivamente implementada usando a composição é um problema: a cópia automatizada das propriedades de uma classe para outra é o problema, e não importa se ela usa composição ou apenas adivinha magicamente como os membros devem trabalhar: tê-los está errado.
Sim, isso viola a responsabilidade única, porque você está dando aos seus objetos a capacidade de executar ações que não fazem parte lógica do que eles foram projetados para fazer. Sim, ele cria objetos "divinos", que é essencialmente outra maneira de dizer a mesma coisa.
Ao projetar sistemas orientados a objetos em Python, a mesma máxima que é pregada pelos livros de design Java também se aplica: prefira composição a herança. O mesmo acontece com (a maioria [2]) outros sistemas com herança múltipla.
[1]: você poderia chamar isso de um relacionamento "é-a", embora eu pessoalmente não goste do termo porque sugere a idéia de modelar o mundo real, e modelagem orientada a objetos não é a mesma que o mundo real.
[2]: Não tenho tanta certeza sobre C ++. O C ++ suporta "herança privada", que é essencialmente composição, sem a necessidade de especificar um nome de campo quando você deseja usar os membros públicos da classe herdada. Não afeta a interface pública da classe. Não gosto de usá-lo, mas não vejo nenhuma boa razão para não fazê- lo.
fonte