[Pesquisar este blog]

terça-feira, 2 de maio de 2017

Os Princípios SOLID::OCP (parte III)

Parte I | Parte II | Parte III | Parte IV | Parte V | Parte VI
Como visto nos post anteriores, os princípios de projeto e programação conhecidos como SOLID têm como objetivo orientar nas atividades de projeto e desenvolvimento de sistemas que possam exibir um longo ciclo de vida, ou seja, além de funcionarem adequadamente tais sistemas devem ser fáceis de manter e de ampliar. Além disso, estes princípios também devem auxiliar nas abordagens ágeis de construção de software.


São cinco os princípios que formam o acrônimo SOLID:
  • Single Responsability Principle
  • Open-Closed Principle
  • Liskov`s Substitution Principle
  • Interface Segregation Principle
  • Dependency Inversion Principle
Neste post será abordado o OCP ou Princípio aberto-fechado.

OCP::Open-Closed Principle

Módulos devem ser abertos para extensão, mas fechados para modificação.
O Open-Closed Principle ou Princípio aberto-fechado ou apenas OCP foi inicialmente proposto por Bertrand Meyer, em 1998, e indica que qualquer classe, módulo ou componente de software deveria ser construído de maneira que possa ser usado como base a criação de outros por meio dos mecanismos de extensão disponíveis (p.e., a herança na programação orientada a objetos); ao mesmo tempo restringindo e limitando as possibilidades de sua alteração direta.

A essência do OCP é a capacidade de estender o comportamento ou o funcionamento de um módulo sem que seja necessária sua modificação. Assim o projeto de uma classe é feito de modo que novas funcionalidades possam ser adicionadas, à medida em apareçam novos requisitos, mas exclusivamente por meio da extensão da própria classe (aberto para extensão). Assim, uma vez criada, a classe nunca deve ser modificada, exceto para correções nas funcionalidades existentes, isto é, que estavam presentes na sua origem (fechado para modificação).

As classes devem, portanto, ser projetadas de tal maneira que, quando surgem demandas para a modificação seu funcionamento (fluxo de controle ou comportamento), tudo que é requerido é a criação uma subclasse na qual sejam substituídas as operações adequadas. Para isto se combinam os mecanismos da herança (vista como especialização de um tipo) e de substituição de métodos (method override).

A figura que segue ilustra uma situação não aderente ao OCP, que pode ser denominada de cliente fechado. 


Uma classe é um cliente (Client) quando se utiliza de funcionalidades presentes em outra classe, aqui denominada servidora (Server). Se for necessário trocar o Server, a classe Client também deverá ser alterada, o que é, claramente um inconveniente.

Outra situação, bastante distinta, é ilustrada abaixo.


A classe cliente (Client) utiliza-se de uma classe denominada AbstractServer, que serve de base para a construção de uma classe servidora (Server) específica. Esta situação, denominada cliente aberto é aderente ao OCP, pois: a troca de Server não afeta a classe Client; além do fato de que AbstractServer poderia atender outros clientes distintos de Server.

Uma implementação-conceito de AbstractServer poderia ser como na classe Java de mesmo nome. As operações abstratas poderiam existir em qualquer número e com qualquer assinatura e tipo de retorno.

public abstract class AbstractServer {
  public abstract void operacao1();
  public abstract void operacao2();
}

Uma classe-concreta para Server poderia ser como:

public class Server extends AbstractServer {
  public Server() {
    System.out.println("Server<init>");
  }
  @Override
  public void operacao1() {
    // implementação específica
    System.out.println("Server.operacao1()");
  }
  @Override
  public void operacao2() {
    // implementação específica
    System.out.println("Server.operacao2()");
  }
}

Note que poderiam existir diferentes implementações de AbstractServer, cada uma contendo as especificidades necessárias a cada tipo de cliente ou grupo de clientes.
Uma fábrica, como ServerFactory, poderia prover instâncias de uma implementação selecionada para os clientes.

public final classe ServerFactory {
  public static AbstractServer createServer() {
    System.out.println("AbstractServer.CreateServer()");
    return new Server();
  }
}

Assim, qualquer cliente, como Client, poderia utilizar implementações concretas de AbstractServer, sem conhecer diretamente tais classes, obtendo as instâncias necessárias da fábrica ServerFactory.

public class Client {
  public static void main(String args[]) {
   AbstractServer server = ServerFactory.createServer();
   server.operacao1();
   server.operacao2(); 
}



Este princípio só é, de fato, útil, quando uma mudança se apresenta como necessária. Assim, uma recomendação válida é: num primeiro momento, codifique da maneira mais simples possível; e, quando surgir a necessidade de modificação, construa uma abstração que o proteja de modificações similares no futuro.

Segundo R. Martin, devemos manter as coisas que mudam com frequência separadas daquelas que não mudam. Além disso, se existirem dependências entre elas, as coisas que mudam com frequência devem depender das que não mudam! (E não o contrário!)

Como, em geral, as mudanças são comuns e frequentes do que desejamos, o OCP agrega muito valor. Novamente o trabalho de dividir a abstração de classe em partes é compensado pelas facilidades em evoluir.

Para Saber Mais

  • LARMAN, Craig. Utilizando UML e padrões: uma introdução à análise e ao projeto orientados à objetos e ao Processo Unificado. Porto Alegre: Bookman, 2007.
  • MARTIN, R. C.; et. al. Clean Code: a handbook of agile software craftsmanship. Boston: Pearson Education, 2009.
  • MARTIN, R. C. The Clean Coder. Upper Sadle River: Prentice-Hall, 2011.
  • MARTIN, R. C. Principles of OOD. Disponível em http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod, recuperado em 17/02/2017.
  • MARTIN, R. C. Get a SOLID start. Disponível em http://objectmentor.com, recuperado em 17/02/2017.
  • PAGE-JONES, Meilir. Fundamentos do Desenho Orientado a Objetos. São Paulo: Makron Books, 2001.
  • SOMMERVILLE, I. Software Engineering. 9th. Ed. Boston: Addison-Wesley, 2011.