Segregação de Interfaces em Python: projetando contratos enxutos com ABC e Protocol

O Princípio da Segregação de Interfaces (ISP — Interface Segregation Principle) é um dos pilares do SOLID e trata diretamente da qualidade dos contratos entre componentes. Em essência, ele afirma que uma classe não deve ser obrigada a depender de métodos que não utiliza. Essa regra incentiva o desenho de interfaces menores, mais coesas e representativas de um papel específico no sistema.

Na prática, o ISP força uma reflexão arquitetural: qual é a verdadeira responsabilidade dessa abstração? Se a resposta envolve comportamentos heterogêneos, a interface provavelmente está concentrando demasiadas responsabilidades — um sinal de design frágil e baixo reuso.

O problema das interfaces genéricas

Considere um caso comum: um módulo que define uma interface genérica de “dispositivo multifuncional”. Ela impõe à hierarquia de classes um contrato extenso, mesmo que nem todas as implementações precisem de todas as operações.

from abc import ABC, abstractmethod

class MultiFunctionDevice(ABC):
    @abstractmethod
    def print_document(self, document): pass

    @abstractmethod
    def scan_document(self, document): pass

    @abstractmethod
    def fax_document(self, document): pass


class BasicPrinter(MultiFunctionDevice):
    def print_document(self, document):
        print(f"Imprimindo: {document}")

    def scan_document(self, document):
        raise NotImplementedError("Este dispositivo não suporta digitalização")

    def fax_document(self, document):
        raise NotImplementedError("Este dispositivo não envia fax")

Aqui, BasicPrinter viola o ISP porque é forçada a implementar métodos irrelevantes. Qualquer alteração em MultiFunctionDevice pode afetar classes que não deveriam ter relação entre si.

Refinando o design com interfaces específicas

Para evitar esse problema, segmentamos as interfaces em abstrações menores e mais focadas:

from abc import ABC, abstractmethod

class Printable(ABC):
    @abstractmethod
    def print_document(self, document): pass

class Scannable(ABC):
    @abstractmethod
    def scan_document(self, document): pass

class Faxable(ABC):
    @abstractmethod
    def fax_document(self, document): pass


class BasicPrinter(Printable):
    def print_document(self, document):
        print(f"Imprimindo: {document}")


class MultiFunctionPrinter(Printable, Scannable, Faxable):
    def print_document(self, document):
        print(f"Imprimindo: {document}")

    def scan_document(self, document):
        print(f"Digitalizando: {document}")

    def fax_document(self, document):
        print(f"Enviando fax: {document}")

Cada interface é coesa e independente. As classes agora implementam apenas as operações relevantes às suas funcionalidades, reduzindo o acoplamento e melhorando a clareza estrutural.

Abordagem moderna com typing.Protocol

A partir do Python 3.8, typing.Protocol permite expressar contratos comportamentais baseados em tipagem estrutural (também chamada de duck typing verificado estaticamente). Esse recurso é especialmente compatível com o ISP, pois elimina a necessidade de herança explícita para validar conformidade de tipo.

from typing import Protocol

class Printable(Protocol):
    def print_document(self, document: str) -> None: ...

class Scannable(Protocol):
    def scan_document(self, document: str) -> None: ...


class BasicPrinter:
    def print_document(self, document: str) -> None:
        print(f"Imprimindo: {document}")


class SmartDevice:
    def print_document(self, document: str) -> None:
        print(f"Imprimindo: {document}")

    def scan_document(self, document: str) -> None:
        print(f"Digitalizando: {document}")


def print_any(printer: Printable, content: str) -> None:
    printer.print_document(content)

Observe que BasicPrinter e SmartDevice não herdam explicitamente de Printable ou Scannable, mas o type checker reconhecerá ambas as classes como compatíveis por possuírem os métodos exigidos. Essa abordagem é vantajosa porque:

Assim, Protocol é a forma moderna e idiomática de aplicar o ISP em projetos Python, tornando clara a separação de responsabilidades e preservando a flexibilidade da linguagem.

Conclusão

O ISP é mais que um princípio de design: é uma diretriz para compor sistemas orientados a abstrações coesas e independentes. No ecossistema Python, a evolução da tipagem com abc e Protocol oferece duas formas complementares de expressar esse princípio — uma baseada em herança nominal, outra em compatibilidade estrutural.

Projetar interfaces enxutas e especializadas é um ato de disciplina arquitetural: reduz o impacto de mudanças, aumenta a clareza e favorece a manutenibilidade a longo prazo. Em times maduros, a aplicação do ISP reflete um domínio avançado de separação de responsabilidades e uma compreensão profunda da dinâmica entre contrato e implementação.



Riverfount
Vicente Eduardo Ribeiro Marçal