A Base Secreta da Programação Orientada a Objetos em Python: Interfaces
Interfaces
Definição
Definem o que uma classe deve fazer ou ter (através de métodos e propriedades abstratas) e não como fazer ou como deve ser o que se tem. Como assim? As `interfaces`, ou `classes abstratas` trás as características que as subclasses devem ter.
Imagine uma cadeira. Nós sabemos que todas as cadeiras tem uma quantidade de pernas, algumas 2,outras 3, e ainda outras 4. Pode até ser que existam cadeiras com 5 pernas, entretanto não existe cadeira sem pernas. Sabemos também que toda cadeira assento, e obrigatoriamente tem recosto, se não tiver recosto, então não é cadeira, é banco, certo?
Pensando ainda no exemplo da cadeira, as características que são nativas de cadeiras, mas que cada objeto cadeira tem livre escolha, tais como: cor, modelo do assento, modelo do recosto isso vai depender de cada construção cadeira feito a partir das características bases principais.
Vamos supor que você queria produzir cadeiras gamers. Essas cadeiras, terão recosto, assento e pernas (neste caso as pernas são representadas pelo pistão, hastes e rodinhas). Assim, para fazer as cadeiras gamers você está tomando como base uma classe abstrata Cadeiras.
Entretanto, você já percebeu que até mesmo `cadeiras gamers` também são uma classe de cadeiras? Temos, cadeiras com apoio para as pernas, cadeiras com apoio para cabeça, cadeiras de com apoio para a lombar e mesmo cadeiras de diferentes `classes` terão objetos bem diferentes. Você saberia dizer quantos modelos de cadeiras gamers existem? Assim, Cadeiras Gamers é uma classe que herda as características de Cadeira, mas cada modelo implementa essas características de formas diferentes. Aqui está o conceito de `classe abstrata`.
Por que podemos dizer que Cadeiras é uma `classe abstrata`? O conceito de cadeira existe nas ideias, quando você ouve a plavra cadeira, você imagina o `objeto cadeira`, este objeto sim é concreto, este `objeto cadeira` é chamado de `instância` da ideia cadeira. Mas pense que mesmo para pensar em uma cadeira, você pensa em `modelo` de cadeira, este modelo é uma classe de cadeira que herdou características da `interface cadeira`.
Como podemos definir a palavra `instância`?
Instância é uma realização específica de uma classe, isto é, um objeto criado a partir de uma classe. Então temos em nossa cabeça a `classe abstrata cadeira`. Quando você constrói uma cadeira gamer, e decide qual modelo de cadeira gamer que construir, então você realiza a materialização de um objeto baseado na classe cadeira gamer, que por sua vez é uma representação da classe abstrata cadeira, este objeto é chamado de `instância da classe cadeira gamer`.
As classes abstratas são o conceito relacionado à classe cadeira, que não pode ser instanciada, pois ela é abstrata, ela só existe para nos dizer o que uma classe baseada em cadeira tem que ter para ser chamado de cadeira. Assim a classe abstrata Cadeira estabelece um `contrato` com suas subclasses, ela determina o que essas subclasses devem cumprir esse `contrato`.
A cadeira gamer, no entanto, é a classe que servirá para finalmente implementar a ideia cadeira, desde que, cumpra o contrato com a classe abstrata Cadeira.
As classes abstratas também podem ser chamadas de `interfaces`. É o modelo base para criação de outras classes que servirão para criar objetos.
Como assim?
Imagine que você irá fazer pinturas das letras para formar uma palavra na parede. Para realizar a pintura de forma que cada letra fique padronizada você pode usar `gabaritos`, assim você garante que todas as letras serão exatamente com os mesmos moldes. Imagine que vai escrever a palavra "Banana", duas letras se repetem: 'a' e 'n'. Como garantir que todas as 'a' sejam exatamente iguais? Usando o gabarito. Assim, o gabarito é uma interface para criação das letras 'a' visando garantir que cada letra saia exatamente no mesmo formato.
Mas concorda que cada uma das três 'a' da palavra 'banana' pode ter cor distinta uma das outras? Pois é a interface dá o molde, mas cada classe criada a partir da interface irá determinar quais serão as características de cada objeto, a interface só dita que aquela característica deve existir na subclasse e não como ela deve ser feita.
Em Python tem interface?
Pensando que as interfaces são classes não instanciáveis, que possuem apenas as assinaturas dos métodos, isto é, os métodos são declarados mas não têm seus conteúdos definidos. Igualmente há os atributos abstratos, que são características não definidas na classe abstrata, tais características devem existir na classe filha, e a subclasse definirá como será esse atributo.
Em Python não há a palavra reservada `Iterface`, então é possível criar classes abstratas e pode ser usadas em quantas classes forem necessárias.
Criando classes Abstratas com o módulo abc
Por padrão o Python não fornece classes abstratas. O Python tem um módulo que fornece base para definir classes abstratas. Este é o módulo `abc`, que têm `decoradores` que identificam os métodos e propriedades como `abstratos` e/ou `estáticos`.
Veja o exemplo da utilização do módulo abc na criação de uma classe. Para isso vamos usar o próprio exemplo que já estamos discutindo: Cadeira
from abc import ABC, abstractmethod, abstractproperty
class Cadeira(ABC):
@abstractproperty
def n_pernas(self):
pass
@abstractproperty
def recosto(self):
pass
class CadeiraGamer(Cadeira):
def __init__(self, n_pernas, recosto):
self._n_pernas = n_pernas
self._recosto = recosto
@property
def n_pernas(self):
return self._n_pernas
@property
def recosto(self):
return self._recosto
class CadeiraEscritorio(Cadeira):
def __init__(self, n_pernas, recosto):
self._n_pernas = n_pernas
self._recosto = recosto
@property
def n_pernas(self):
return self._n_pernas
@property
def recosto(self):
return self._recosto
class CadeiraJantar(Cadeira):
def __init__(self, n_pernas, recosto):
self._n_pernas = n_pernas
self._recosto = recosto
@property
def n_pernas(self):
return self._n_pernas
@property
def recosto(self):
return self._recosto
gamer_dt = CadeiraGamer(5, 'Longo')
print(f"Quantidade de pernas da {gamer_dt.__class__.__name__}: {gamer_dt.n_pernas}")
cadeira_jantar = CadeiraJantar(4, 'Longo')
cadeira_escritorio = CadeiraEscritorio(3, 'Curto')
print(f"Quantidade de pernas da {cadeira_jantar.__class__.__name__}: {cadeira_jantar.n_pernas}")
print(f"Tipo do recosto da {cadeira_escritorio.__class__.__name__}: {cadeira_escritorio.recosto}")
Em suma, as classes abstratas (interfaces) são a espinha dorsal de um design POO robusto e flexível. Elas não apenas nos guiam na construção de sistemas coesos e livres de erros, mas também abrem portas para a extensibilidade e a colaboração eficiente.
Agora, queremos saber de você: Em seus projetos, quais foram os cenários onde as interfaces ou classes abstratas se mostraram mais valiosas? Compartilhe suas experiências e vamos enriquecer essa discussão! 💬