Os princípios SOLID — OCP

Open/closed principle (OCP) — Princípio aberto fechado

Felippe Roberto Bayestorff Duarte
3 min readFeb 14, 2018

Continuando do artigo anterior, vamos falar sobre o segundo princípio, OCP.

O OCP, princípio aberto/fechado, diz que “Entidades de software (classes, módulos, funções, etc) devem estar abertas para extensão, mas fechadas para modificação.” (Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification).

Certo, mas o que isso significa na prática?

O que pode acontecer, quando esse princípio é quebrado, é que uma alteração em um objeto crie alterações em cascata em outros objetos.

Para explicar isso, nada melhor que o código. Pense numa aplicação onde o usuário realiza o login através de um formulário com email e senha:

<?phpclass Autenticacao
{
public function login($email, $senha)
{
//compara email no banco, hash da senha
//caso positivo seta alguns valores na sessão
//ao final retorna true ou false
}
}

Até aqui OK, parece que tudo funcionaria normalmente, a não ser que você decida colocar uma nova forma de login, por exemplo com uso de OAuth como Google, Facebook, Twitter e outros serviços oferecem.

Aí o que você faz? Modifica a classe original?

<?php
//include Google API
class Autenticacao
{
public function login($cliente, $provedor)
{
if($provedor == 'Google') {
return $this->loginGoogle($cliente);
} else {
return $this->loginSistema($cliente);
}
}
public function loginSistema($cliente)
{
//compara email no banco, hash da senha
//caso positivo seta alguns valores na sessão
//ao final retorna true ou false
}
public function loginGoogle($cliente)
{
//realiza login pela API do google
//caso sucesso, coloca valores na sessão
//ao final retorna true ou false
}
}

Pronto, agora tem uma função para o login “normal” com usuário e senha, e outro com login via Google. Então percebe que a demanda por Facebook e Twitter é grande e cria novos métodos:

<?php
//include Google API
//include Facebook API
//include Twitter API
class Autenticacao
{
public function login($cliente, $provedor)
{
if($provedor == 'Google') {
return $this->loginGoogle($cliente);
} elseif($provedor == 'Facebook') {
return $this->loginFacebook($cliente);
} elseif($provedor == 'Twitter') {
return $this->loginTwitter($cliente);
} else {
return $this->loginSistema($cliente);
}
}
public function loginSistema($cliente)
{
//compara email no banco, hash da senha
//caso positivo seta alguns valores na sessão
//ao final retorna true ou false
}
public function loginGoogle($cliente)
{
//realiza login pela API do google
//caso sucesso, coloca valores na sessão
//ao final retorna true ou false
}
public function loginFacebook($cliente)
{
//realiza login pela API do facebook
//caso sucesso, coloca valores na sessão
//ao final retorna true ou false
}
public function loginTwitter($cliente)
{
//realiza login pela API do twitter
//caso sucesso, coloca valores na sessão
//ao final retorna true ou false
}
}

Agora a classe está abrangendo várias formas de login, mas o tamanho e complexidade dela crescem a cada novo serviço. Além disso, mantê-la fica cada vez mais custosa, pois cada alteração precisa ser verificada se não impactou os serviços de login, e ainda quebra o princípio da responsabilidade única (SRP) que vimos anteriormente, pois a classe agora tem não uma, mas 4 razões para mudar.

Certo, mas qual seria o caminho adequado? Conforme prega o OCP, o ideal aqui seria termos uma classe base, e as demais classes estendendo essa classe base, cada uma com sua lógica para seu serviço.

No nosso contexto, a classe Base pode ser uma nova classe, ou pode ser a própria classe que faz autenticação via sistema. Esta classe fica responsável por realizar todos os procedimentos necessários para o login, as demais classes realizam a autenticação nos serviços e retornam para a classe base realizar os procedimentos pós-autenticação:

<?php
class Autenticacao
{
public function login($cliente)
{
//compara email no banco, hash da senha
//caso positivo seta alguns valores na sessão
//ao final retorna true ou false
}
}
//include Google API
class AutenticacaoGoogle extends Autenticacao
{
public function login($cliente)
{
//realiza login pela API do google
//caso sucesso, coloca valores na sessão
//ao final retorna true ou false
}
}
//include Facebook API
class AutenticacaoFacebook
{
public function login($cliente)
{
//realiza login pela API do facebook
//caso sucesso, coloca valores na sessão
//ao final retorna true ou false
}
}
//include Twitter API
class AutenticacaoTwitter
{
public function login($cliente)
{
//realiza login pela API do twitter
//caso sucesso, coloca valores na sessão
//ao final retorna true ou false
}
}

Com isso temos várias classes que implementam o mesmo método de formas distintas, porém cada uma dentro do seu escopo.

Espero que tenham gostado. O próximo artigo será sobre o LSP - Princípio da substituição de Liskov.

Os códigos estão no repositório: https://github.com/felippeduarte/medium/tree/master/solid

Referências: Robert C. Martin

--

--