Como os pilares da Programação Orientada a Objetos (POO) se conectam ao SOLID

1. Abstração
Foca no comportamento essencial isolando as características essenciais de um objeto, ignorando os detalhes de implementação.
A abstração é um dos pilares da Programação Orientada a Objetos (POO) que ajuda a reduzir a complexidade no desenvolvimento de software. Ela permite que os programadores representem entidades do mundo real de maneira simplificada, focando apenas nos aspectos essenciais para o sistema.
Isso permite que seu código de negócio não conheça como o pagamento será salvo: pode ser banco, arquivo, API.
2. Encapsulamento
Protege o estado interno do objeto ermite ocultando os detalhes internos de implementação e definindo uma interface de uso externo segura.
O encapsulamento é um princípio fundamental da Programação Orientada a Objetos que consiste em ocultar os detalhes internos de um objeto, expondo apenas o que é necessário para sua interação.
3. Herança
Permite reuso de comportamento e especialização por meio de classes derivadas.
A herança é um dos pilares fundamentais da Programação Orientada a Objetos que permite estender funcionalidades de uma classe já existente. Com ela, é possível criar novas classes (chamadas de subclasses ou classes filhas) que herdam características (atributos e métodos) de uma classe base (superclasse ou classe pai).
4. Polimorfismo
Permite que objetos de diferentes classes possam responder de maneira diferente a uma mesma chamada de método, mantendo uma interface comum.
O polimorfismo é um conceito essencial da POO que permite que um mesmo método se comporte de maneiras diferentes, dependendo do objeto que o executa. Ele dá flexibilidade ao código, permitindo que classes distintas respondam à mesma ação de formas específicas.
Os 5 Princípios SOLID
1. Single Responsibility Principle (SRP) – Princípio da Responsabilidade Única
Reúna as coisas que mudam pelos mesmos motivos. Separe as coisas que mudam por motivos diferentes.
Evite misturar regras de negócio com a camada de interface gráfica. Também separamos comandos SQL de protocolos de comunicação. Fazemos isso porque queremos manter isolado o código que muda por motivos diferentes, evitando que alterações em uma parte afetem outras de forma indesejada. Assim, garantimos que módulos que evoluem por razões distintas não fiquem entrelaçados por dependências desnecessárias.
Se uma classe faz muitas coisas diferentes, ela fica difícil de entender e modificar. Separando as responsabilidades, deixamos o código mais organizado e modular.
2. Open/Closed Principle (OCP) – Princípio Aberto/Fechado
O código deve estar aberto para extensão, mas fechado para modificação. Isso significa que devemos ser capazes de adicionar novas funcionalidades sem alterar o código existente.
Isso quer dizer que devemos usar interfaces ou classes abstratas para definir o comportamento, e usando polimorfismo esse comportamento pode variar em tempo de execução.
3. Liskov Substitution Principle (LSP) – Princípio da Substituição de Liskov
Subtipos devem ser substituíveis por seus tipos base sem quebrar a aplicação.
Se uma subclasse altera o comportamento esperado da classe pai, podemos ter problemas imprevisíveis no código.
Existem 3 regras que devem ser seguidas pelas subclasses:
- Pré-condições não devem ser fortalecidas na subclasse: Pré-condições representam os requisitos necessários para que um método possa ser executado corretamente, os parâmetros esperados. Por exemplo, se um método aceita um número (positivo ou negativo), uma subclasse não deve restringir esse comportamento para aceitar apenas números positivos. Fazer isso fortalece a pré-condição, e pode quebrar a lógica do programa, especialmente em casos onde se espera que o método aceite números negativos.
-
Pós-condições não devem ser enfraquecidas na subclasse: As pós-condições definem o que o método retorna após sua execução. Uma subclasse não deve retornar menos do que o esperado. Por exemplo, se o contrato do método exige um valor booleano, a subclasse deve retornar um
true
oufalse
. Embora seja possível “fortalecer” o contrato (por exemplo, retornando sempretrue
), retornar outros tipos comostring
,number
,null
, ou lançar exceções inesperadas, quebra a expectativa da aplicação — especialmente em linguagens dinamicamente tipadas. -
A invariância deve ser preservada na subclasse: Invariância se refere ao estado interno do objeto, que deve ser protegido e controlado por meio de encapsulamento. Subclasses não devem violar esse contrato, modificando diretamente atributos da superclasse ou alterando sua lógica de maneira inconsistente. É essencial respeitar os modificadores de visibilidade (como
private
eprotected
) e manter o comportamento coerente em toda a hierarquia de classes.
4. Interface Segregation Principle (ISP) – Princípio da Segregação de Interface
Nenhuma classe deve ser forçada a depender de métodos que não usa.
É melhor ter múltiplas interfaces específicas do que uma interface gigante cheia de métodos inúteis.
Por que isso importa? (segundo Uncle bob)
Mesmo hoje, trabalhando com linguagens compiladas, ainda somos afetados por problemas relacionados à recompilação e reimplantação desnecessária. Imagine o seguinte cenário:
- O Módulo A depende de uma interface do Módulo B apenas para um subconjunto de seus métodos.
- Se outro método dessa interface for alterado, mesmo que o Módulo A não o utilize, ele precisará ser recompilado e reimplantado.
Esse acoplamento em tempo de compilação, mesmo sem dependência em tempo de execução, é um problema real — especialmente em linguagens estaticamente tipadas como Java, C#, C++, Go, Swift etc.
5. Dependency Inversion Principle (DIP) – Princípio da Inversão de Dependência
Dependa de abstrações, não de implementações concretas, ou seja abstrações não devem depender de detalhes. Detalhes devem depender de abstrações.
Isso significa que implementando esse princípio voce vai quebrar a dependência direta entre regras de negócio (nível alto) e detalhes de implementação (nível baixo). Ele propõe que as dependências devem sempre apontar para abstrações, nunca diretamente para implementações concretas.
A injeção de dependências resolve o vínculo entre os dois em tempo de execução.
RELAÇÃO: PILARES da POO → PRINCÍPIOS SOLID
Pilar da POO | Conexões com Princípios SOLID | Como se Conectam |
---|---|---|
Abstração | OCP, DIP, ISP, SRP | Permite ocultar complexidade, definindo interfaces que isolam o “o quê” do “como”. Facilita o desacoplamento entre módulos. Base para Dependency Inversion e Interface Segregation. |
Encapsulamento | SRP, LSP, OCP | Protege o estado interno dos objetos, separando responsabilidades. Ajuda a manter coesão e evitar que alterações em um detalhe impactem em outros. |
Herança | OCP, LSP | Permite a extensão de comportamento sem modificação (essência do Open/Closed). Deve ser usada com cautela para não violar substituição (Liskov). |
Polimorfismo | OCP, DIP, LSP, ISP | Permite que objetos de diferentes tipos sejam tratados de forma uniforme via interfaces. Viabiliza inversão de dependência, extensão sem modificação e substituição segura de implementações. |
Código tem que ser simples
Simplicidade não é ausência de estrutura ou codar com falta de boas praticas e principios
Dizer apenas “Código tem que ser simples” sem oferecer princípios claros é como ser um surdo em bingo.
Com o entendimento básico dos fundamentos de POO fica mais facil entender os principios SOLID
https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle
https://blog.cleancoder.com/uncle-bob/2020/10/18/Solid-Relevance.html
https://www.branas.io/blog/solid-lsp-liskov-substitution-principle.htmlpo
https://hub.asimov.academy/blog/programacao-orientada-a-objetos-conceito-e-pilares/