Herança Múltipla E Interfaces
Imagine que um Sistema de Controle do Banco pode ser acessado, além dos Gerentes, pelos Diretores do Banco. Teríamos uma classe Diretor .
class Diretor(Funcionario):def autentica(self, senha):verifica se a senha confere
E a classe Gerente :
class Gerente(Funcionario): def autentica(self, senha):verifica se a senha confere e também se o seu departamento tem acesso
Repare que o método de autenticação de cada tipo de Funcionario pode variar muito. Mas vamos aos problemas. Considere o SistemaInterno e seu controle: precisamos receber um Diretor ou Gerente como argumento, verificar se ele se autentica e colocá-lo dentro do sistema.
Vimos que podemos utilizar a função hasattr() para verificar se um objeto possui o método
autentica() :
class SistemaInterno:def login(self, funcionario):if (hasattr(obj, 'autentica')): # chama método autenticaelse:
# imprime mensagem de ação inválida
Mas podemos esquecer, no futuro, quando modelarmos a classe Presidente (que também é um funcionário e autenticável), de implementar o método autentica() . Não faz sentido colocarmos o método autentica() na classe Funcionario já que nem todo funcionário é autenticável.
Uma solução mais interessante seria criar uma classe no meio da árvore de herança, a
FuncionarioAutenticavel :
class FuncionarioAutenticavel(Funcionario): def autentica(self, senha):verifica se a senha confere
E as classes Diretor , Gerente e qualquer outro tipo de FuncionarioAutenticavel que vier a existir em nosso sistema bancário passaria a estender de FuncionarioAutenticavel . Repare que FuncionarioAutenticavel é forte candidata a classe abstrata. Mais ainda, o método autentica() poderia ser um método abstrato.
O uso de herança simples resolve o caso, mas vamos a uma outra situação um pouco mais complexa: todos os clientes também devem possuir acesso ao SistemaInterno . O que fazer?
Uma opção é fazer uma herança sem sentido para resolver o problema, por exemplo, fazer Cliente estender de FuncionarioAutenticavel . Realmente resolve o problema, mas trará diversos outros. Cliente definitivamente não é um FuncionarioAutenticavel . Se você fizer isso, o Cliente terá, por exemplo, um método get_bonificacao() , um atributo salario e outros membros que não fazem o menor sentido para esta classe.
Precisamos, para resolver este problema, arranjar uma forma de referenciar Diretor , Gerente e
Cliente de uma mesma maneira, isto é, achar um fator comum.
Se existisse uma forma na qual essas classes garantissem a existência de um determinado método, através de um contrato, resolveríamos o problema. Podemos criar um “contrato” que define tudo o que uma classe deve fazer se quiser ter um determinado status. Imagine:
contrato Autenticavel
- quem quiser ser Autenticavel precisa saber fazer: autenticar dada uma senha, devolvendo um booleano
Quem quiser pode assinar este contrato, sendo assim obrigado a explicar como será feita essa autenticação. A vantagem é que, se um Gerente assinar esse contrato, podemos nos referenciar a um Gerente como um Autenticavel .
Como Python admite herança múltipla, podemos criar a classe Autenticavel :
class Autenticavel:def autentica(self, senha):verifica se a senha confere
E fazer Gerente , Diretor e Cliente herdarem essa classe:
class Gerente(Funcionario, Autenticavel): # código omitidoclass Diretor(Funcionario, Autenticavel): # código omitidoclass Cliente(Autenticavel): # código omitidoOu seja, Gerente e Diretor além de funcionários são autenticáveis! Assim, podemos utilizar o
SistemaInternopara funcionários autenticáveis e clientes:
class SistemaInterno: def login(self, obj):if (hasattr(obj, 'autentica')): obj.autentica()return True else:
print(f'{} não é autenticável {self. class . name }') return Falseif name == ' main ':diretor = Diretor(‘João’, ‘111111111-11’, 3000.0, ‘1234’)
gerente = Gerente(‘José’, ‘222222222-22’, 5000.0, ‘1235’)
cliente = Cliente(‘Maria’, ‘333333333-33’, ‘1236’)
sistema = SistemaInterno() sistema.login(diretor) sistema.login(gerente) sistema.login(cliente)
Note que uma classe pode herdar de muitas outras classes. Mas vamos aos problemas que isso pode gerar. Por exemplo, várias classes podem possuir o mesmo método.