01 nov 2016

Operação Lava-Java! Nº 3 – Princípio do Aberto e Fechado

OCP – OPEN CLOSED PRINCIPLE

“Entidades, classes, módulos e funções deveriam ser abertos para extensão e fechados para modificação”

O ciclo de vida de um software faz com que ele seja suscetível a mudanças. Um software bem arquitetado te possibilita fazer essas mudanças alterando o mínimo de código possível, ao invés de reescrever o comportamento das classes. Portanto, deve-se adicionar a novas features modificando minimamente as existentes.
Este princípio faz com que os desenvolvedores não fiquem receosos em adicionar features no projeto, pelo medo de mexer no código de outro desenvolvedor e quebrar algo. O conceito é bem simples, mas como será a implementação?

Vamos ao exemplo dos veículos, na imagem abaixo podemos ver um design mal elaborado que não respeita o princípio. Toda vez que adicionar um novo tipo de veículo, será necessário alterar a classe MechanicService para suportar o mesmo. Veja algumas desvantagens desta implementação:

  • Toda vez que adicionar um tipo de vehicle será necessário readequar os tests unitários.
  • Novos desenvolvedores sem conhecimento desta classe, podem não a modificar toda vez que adicionar um veículo. Isso provavelmente abrirá brechas para bugs
  • Se possuir uma lógica complexa, os desenvolvedores terão que dedicar uma parte do tempo tentando entender o código antes de implementar.

lavajava-bad


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MechanicService {

     public void repairVehicle(Vehicle v) {

          if (v.type == CAR)
               repairCar(v);
          else if (v.type == TRUCK)
               repairTruck(v);
          else if (v.type == MOTORCYCLE)
               repairMotorcycle(v);
     }

     public void repairCar(Car c) {....}
     public void repairTruck(Truck t) {....}
     public void repairMotorcycle(Motorcycle m) {....}
}

Para corrigir essa situação, é necessário abstrair a implementação da classe MechanicService e transferir a responsabilidade de reparar para as classes concretas, fazendo com que a service só conheça sua abstratação, seja ela uma classe abstrata ou uma interface. Desta forma, não será necessário alterar a service toda vez que adicionar um novo tipo de Vehicle, respeitando o princípio. Algumas vantagens desse design:

  • Deixa de ter um modelo anêmico.
  • Não tem necessidade de modificar os tests unitários toda vez que adicionar um vehicle novo.
  • Não tem necessidade de entender a lógica de MechanicService.

lavajava-good


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class MechanicService {

     public void repairVehicle(Vehicle v) {
          v.repair();
     }
}

public class Vehicle {
     abstract void repair();
}

public class Car extends Vehicle {
     public void repair() {
          // do Something
     }
}

public class Truck extends Vehicle {
     public void repair() {
          // do Something
     }
}

public class Motorcycle extends Vehicle {
     public void repair() {
          // do Something
     }
}

Existem outras formas de implementação que levam a minima modificação de classes existentes, o Decorator Pattern faz com que o design do seu código respeite o princípio sem usar herança e faz uso de composição. É indicado utilizar sempre a Composição em relação à Herança.

Referências

O que é um modelo anêmico: http://blog.caelum.com.br/o-que-e-modelo-anemico-e-por-que-fugir-dele/
Decorator pattern: http://www.oodesign.com/decorator-pattern.html
The S.O.L.I.D. Principles of OO and Agile Design: https://www.youtube.com/watch?v=t86v3N4OshQ
Open Closed Principle: http://www.oodesign.com/open-close-principle.html

Gabriel Suaki – Software Engineer at redspark.
GitHub: GSuaki
Twitter: @gsuaki

Gabriel Suaki
About Gabriel Suaki

Software Engineer at redspark Gabriel Suaki - Software Engineer at redspark.

Leave a Comment