11.8 Classes Abstratas
Vamos recordar nossa classe Funcionario :
class Funcionario:
python
def __init__(self, nome, cpf, salario=0): #inicialização dos atributos
#propriedades e outros métodos def get_bonificacao(self):
return self._salario \* 1.2
Considere agora nosso ControleDeBonificacao :
python
class ControleDeBonificacoes:
python
def __init__(self, total_bonificacoes=0):
self. total_bonificacoes = total_bonificacoes
python
def registra(self, obj): if(hasattr(obj, 'get_bonificacao')):self. total_bonificacoes += obj.get_bonificacao() else:
print('instância de {} não implementa o método get_bonificacao()'.format(self. class .__name ))
Nosso método registra() recebe um objeto de qualquer tipo. Todavia, estamos esperando que seja um Funcionario já que este implementa o método get_bonificacao() , isto é, podem ser objetos do tipo Funcionario e qualquer de seus subtipos. Dessa forma, qualquer subclasse que eventualmente venha ser escrita, sem prévio conhecimento do autor da ControleDeBonificacao podem ser implementadas.
Estamos utilizando aqui a classe Funcionario para o polimorfismo. Se não fosse por ela, teríamos um grande prejuízo: precisaríamos criar um método registra() para receber cada um dos tipos de Funcionario , um para Gerente , um para Diretor , etc. Repare que perder esse poder é muito pior do que a pequena vantagem que a herança traz em herdar código.
Todavia, em alguns sistemas, como é o nosso caso, usamos uma classe com apenas esses intuitos: de economizar um pouco código e ganhar polimorfismo para criar métodos mais genéricos, que se encaixem a diversos objetos.
Faz sentido ter um objeto do tipo Funcionario ? Esta pergunta é bastante relevante, já que instanciar um Funcionario pode gerar um objeto que não faz sentido no nosso sistema. Nossa empresa tem apenas Diretores , Gerentes , Secretários , etc… Funcionario é apenas uma classe que idealiza um tipo, define apenas um rascunho.
Vejamos um outro caso em que não faz sentido ter um objeto de determinado tipo, apesar da classe existir. Imagine a classe Pessoa e duas filhas: PessoaFisica e PessoaJuridica . Quando puxamos um relatório de nossos clientes (uma lista de objetos de tipo Pessoa , por exemplo), queremos que cada um deles seja ou uma PessoaFisica ou uma PessoaJuridica . A classe Pessoa , nesse caso, estaria sendo usada apenas para ganhar o polimorfismo e herdar algumas coisas - não faz sentido permitir instanciá-la.
Para o nosso sistema, é inadmissível que um objeto seja apenas do tipo Funcionario (pode existir um sistema em que faça sentido ter objetos do tipo Funcionario ou apenas Pessoa , mas, no nosso caso, não). Para resolver esses problemas, temos as classes abstratas.
Utilizaremos uma módulo do Python chamado abc que permite definirmos classes abstratas. Uma classe abstrata deve herdar de ABC (Abstract Base Classes). ABC é a superclasse para classes abstratas.
Uma classe abstrata não pode ser instanciada e deve conter pelo menos um método abstrato. Vamos ver isso na prática.
Vamos tornar nossa classe Funcionario abstrata:
import abc
python
class Funcionario(abc.ABC): # métodos e propriedadesDefinimos nossa classe Funcionario como abstrata. Agora vamos tornar nosso método get_bonificacao() abstrato. Um método abstrato pode ter implementação, mas não faz sentido em nosso sistema, portanto vamos deixá-lo sem implementação. Para definirmos um método abstrato, utilizamos o decorador @abstractmethod :
class Funcionario(abc.ABC):@abc.abstractmethod
def get_bonificacao(self): pass
Agora, se tentarmos instanciar um objeto do tipo Funcionario :
if name == ' main ': f = Funcionario()Acusa um erro:
TypeError: Can’t instantiate abstract class Funcionario with abstract methods get_bonificacao
Apesar de não conseguir instanciar a classe Funcionario , conseguimos instanciar suas filhas que são objetos que realmente existem em nosso sistema (objetos concretos):
class Gerente(Funcionario):outros métodos e propriedades
def get_bonificacao(self): return self._salario \* 0.15
java
if name == ' main ':gerente = Gerente(‘jose’, ‘222222222-22’, 5000.0, ‘1234’, 0) print(gerente.get_bonificacao())
Vamos criar a classe Diretor que herda de Fucionario sem o método get_bonificacao() :
class Diretor(Funcionario):
python
def __init__(self, nome, cpf, salario): super(). init (nome, cpf, salario)
java
if name == ' main ':diretor = Diretor(‘joao’, ‘111111111-11’, 4000.0)
Quando rodamos o código:
TypeError: Can’t instantiate abstract class Diretor with abstract methods get_bonificacao
Não conseguimos instanciar uma subclasse de Funcionario sem implementar o método abstrato get_bonificacao() . Agora, tornamos o método get_bonificacao() obrigatório para todo objeto que é subclasse de Funcionario . Caso venhamos a criar outras classes, como Secretaria e
Presidente, que sejam filhas de Funcionario , seremos obrigados e criar o método
get_bonificacao() , caso contrário, o código vai acursar erro quando executado.