SOLID — Open-Closed Principle ft. Strategy Pattern

Victor Bacega
DEVPIRA
Published in
6 min readApr 7, 2021

Continuando a nossa série de SOLID (caso você não viu o primeiro artigo só clicar aqui ) hoje iremos abordar o principio de Aberto-Fechado!

Esse principio prega que as classes devem ser abertas e fechadas. Abertas para extensão porem fechadas para modificação

“ Que? Como assim aberta e fechada? Como uma classe pode ser aberta e fechada ao mesmo tempo? ”

Para entendermos o por que desse nome e o funcionamento dele, vamos entender qual problema ele veio solucionar.

Imagine que você tem uma classe com inúmeros métodos complexos ou com alguns simples, e te solicitam que você adicione um método ou fazer uma correção em um método existente. Neste cenário quais problemas podemos encontrar?

  • Dificuldade no entendimento da classes.
  • Dificuldade em saber a onde inserir o novo método.
  • Não tem garantia de que se você adicionar um código ele não ira quebrar algum o funcionamento da classes.
  • Duplicidade de variáveis dentro dos métodos (o mesmo nome de variável dentro de cada método).
  • Entre outros…

Bad Code

Irei mostrar um exemplo de uma classe que não segue esse principio:

Considerando a classe a cima, imagine que foi sugerido a você que adicione um método para pagamento em boleto e que modifique a taxa do cartão de credito para “0.12”.

Claro que em uma classe pequena e simples como essa não tem nem uma dificuldade, mas você consegue garantir com plena certeza que, ao adicionar essas 2 modificações nada dela ira parar de funcionar? Talvez sim, mas e em uma classe complexa e grande? Bem eu particularmente não consigo garantir, só depois de testar bem ela e certificar que ela está funcionando mas mesmo assim pode vir acontecer algum outro erro.

O Principio

Dado o exemplo agora iremos explicar o conceito de Aberto-Fechado:

  • Aberto:

Uma classe ela tem que ser aberta para extensão ou seja, tem que ser possível eu estender essa classe caso necessário.

  • Fechado:

Porem essa classe ela tem que ser fechada para modificações ou seja, depois de criada não posso implementar nem uma funcionalidade nova!
Salvo quando tem que concertar algum bug, você simplesmente vai lá e concerta!

Para entendermos melhor vamos refatorar a classe BadPayment e criar uma classe chamada Payment seguindo o principio do OCP — Open Closed Principle. E de bônus iremos utilizar um padrão de projeto que segue esse principio que é o Strategy!

Strategy

Primeiro vamos explicar bem rapidamente o que o padrão Strategy é, ele serve para resolvermos o seguinte problema, temos uma classe de Pagamentos e nela consiste 2 métodos, pagar com crédito e pagar com débito, porem depois de um tempo foi solicitado você adicionar pagamento a vista, mais para frente pediram para adicionar boleto, logo em seguida PIX, e depois pagamento com voucher, e você começou a perceber que a cada vez que você adicionava um método sua classe dobrava de tamanho e você tem que tomar muito cuidado para não quebrar os outros métodos que estão funcionado (já vimos isso em algum lugar né?).

Então o padrão Strategy serve para separar todos esses métodos chamando os de estratégias e com forme for necessitando passamos a estratégia como parâmetro para uma classe que chamamos de contexto, irei mostrar a seguir.

Good Code

Primeiro iremos criar uma interface que conterá os métodos no qual todas as estratégias tem em comum:

Simples e direta, uma interface com o método pagamento a onde recebe o valor e apenas executa.

Em seguida vamos fazer as nossos métodos de pagamento (estratégias)

Dinheiro

A classe Cash implementa a Interface IPayment e com isso criamos o nosso método para pagamento em dinheiro

Para entender o por que eu isolei o valor de desconto em vez de coloca-lo direto na função clique aqui e melhore a qualidade do seu código!

Crédito

Mesma coisa que a de dinheiro, porem em vez de aplicarmos um desconto, temos uma taxa do cartão de crédito.

Débito

Essa é mais simples, não tem taxa e nem desconto apenas o valor normal.

O Contexto

Aqui que a magica acontece, na classe contexto temos 2partes fundamentais para ela funcionar:

  • Construtor:
    Ele será responsável para pegarmos o tipo de pagamento no qual o cliente ira enviar para ele (ou seja, se é credito ou debito ou dinheiro) e em seguida setara na variável payment
  • Execução do Método genérico:
    Feito isso, temos um método no qual executara o método pagamento não importando qual é o tipo, ele apenas recebe o valor do pagamento e passa para a função de pagamento.

para finalizar e ver o funcionamento, vamos para a classe cliente a onde iremos consumir o contexto.

Cliente

A baixo está o método cliente:

Neste método estamos instanciamos a classe contexto passando como parâmetro o tipo do pagamento neste caso estamos usando o pagamento em Dinheiro (Linha 2 “new Cash()” ).

Com isso na linha 4 passamos o valor do pagamento que no caso é 500 para a função makeThePayment que na classe contexto faz a execução do método pay do tipo do pagamento selecionado

Resultado

Se estamos passando o valor 500 para o contexto no qual executa o método pay da classe Cash que passamos para o contexto, o que será impresso no final ?

De acordo com a classe Cash():

E se em vez de passarmos a classe Cash() na hora de instanciar a classe Contexto, passarmos a Credit() o que será impresso?

Com isso podemos criar inúmeros métodos de pagamentos em classes separadas e a nossa classe principal que é a Contexto se manterá intacta e se por acaso aparecer alguma modificação em algum tipo de pagamento você estará seguro que só ira afetar aquele tipo, pois está totalmente isolado!

Assim nossa Classe de pagamento que é o ContextPaymentType está aberto para extensão ou seja novos meios de pagamento pois basta criar uma estratégia nova!
Porem ele continua fechado para modificações pois não precisamos adicionar mais nada dentro dele!

Más Victor, como posso obedecer o OCP sem usar o padrão Strategy?

Um exemplo é, crie classes abstratas, estenda essas classes conforme a necessidade e sobrescreva os métodos, assim a classe abstrata se mantem pequena e isolada e as suas implementações ficam isoladas sem interferir uma nas outras!

Considerações Finais

Esse artigo ficou um pouco longo e talvez um pouco complexo, porem quando eu aprendi esse jogo da Strategy com o OCP fiquei maravilhado e decidi compartilhar isso com o vocês! Caso fique alguma duvida, sugestão ou elogios por favor não se acanhe pode escrever nos comentários que eu irei sempre responder! Muito obrigado por terem lido o artigo e muitos abraços!

Repositório do código no GitHub.

--

--