De que se trata o artigo:

O artigo trata de cinco princípios básicos do desenvolvimento de software orientado a objetos. Os princípios são o Single Responsibility Principle, o Open Closed Principle, o Liskov Substitution Principle, o Interface Segregation Principle e o Dependency Inversion Principle.


Para que serve:

Esses princípios têm como objetivo guiar o programador para que estes possam lidar de forma adequada com diversos aspectos da programação orientada a objetos, como o uso de herança e o controle de dependências entre objetos.


Em que situação o tema é útil:

O conhecimento desses princípios é importante para qualquer desenvolvedor que utilize linguagens orientadas a objetos para que ele possa sempre utilizar ao máximo o poder de suas ferramentas de trabalho. Além disso, é importante para que seja possível identificar problemas no código e refatorá-los, tornando o software mais flexível e de melhor manutenção.

Desenvolvendo software sólido:

Neste artigo são discutidos cinco princípios da programação orientada a objetos. O primeiro deles é o Single Responsibility Principle, que fala sobre a criação de classes coesas, que têm uma única responsabilidade. Em seguida, o Open Closed Principle fala sobre como manter o nosso código flexível, de forma que possamos modificar ou estender o comportamento do software sem que tenhamos que mexer em código já existente e, portanto, sem o receio de introduzir bugs em código que já está funcionando bem. O Liskov Substitution Principle fala sobre o uso da herança e quando ela foi realizada de forma adequada.

Para finalizar, são apresentados o Interface Segregation Principle e o Dependency Inversion Principle. O primeiro diz que devemos criar interfaces pequenas, para as quais é necessário garantir que todos os clientes irão implementar os seus métodos, e o segundo fala que, em um software, os módulos de mais alto nível não devem depender da implementação de módulos de mais baixo nível, e sim de abstrações, como o uso de interfaces.

Hoje em dia, a maioria dos desenvolvedores de software está utilizando linguagens orientadas a objetos, mas infelizmente, a maioria deles as utiliza sem o devido conhecimento, sem saber como extrair o máximo de benefícios dos recursos que elas oferecem. Muitos desenvolvedores, apesar de utilizar estas linguagens, programam de forma procedural, e assim, o código fica muito complexo e pouco flexível. Por isso, muitas vezes culpam a linguagem que utilizam por um problema causado pela falta de conhecimento.

Ao longo de nossa vida profissional já ouvimos diversos conselhos para melhorar o desenvolvimento de software orientado a objetos, como programar para interfaces, e não para implementações. Assim como já estudamos dezenas de design patterns e os utilizamos para solucionar problemas em nossos softwares. Mas de onde vieram todas essas ideias? Quais são as principais regras que devemos seguir quando estamos utilizando a orientação a objetos?

Este artigo tem como objetivo mostrar os cinco princípios básicos da programação orientada a objetos. Eles foram introduzidos por Robert C. Martin, no início dos anos 2000. Seguir esses princípios torna possível a criação de sistemas muito mais fáceis de manter e estender ao longo do tempo. Os princípios são diretivas que podem ser aplicadas enquanto se trabalha em um software para remover problemas no código através da refatoração até que ele esteja legível e flexível.

A refatoração (do inglês “refactoring”) é o ato de pegar o código já existente de um software e modificar a sua estrutura de forma a melhorá-la sem que haja uma alteração no comportamento do software.

Estes princípios lidam com diversos aspectos da programação orientada a objetos, por exemplo, como aumentar a coesão de uma classe e diminuir o acoplamento entre elas, como utilizar herança de forma correta, como lidar com as dependências de objetos, entre outros.

Os princípios analisados são: Single Responsibility Principle, Open Closed Principle, Liskov Substitution Principle, Interface Segregation Principle e Dependency Inversion Principle. As iniciais dos cinco princípios formam a palavra SOLID, daí o título do artigo.

Single Responsibility Principle

O Single Responsibility Principle, ou Princípio da Responsabilidade Única, foi introduzido por Robert C. Martin, em seu livro Agile Software Development: Principle, Patterns, and Practices. Nessa obra, ele diz que “uma classe deve ter apenas uma razão para mudar”. Esse princípio também é chamado de coesão e implica em dizer que uma classe deve fazer uma única coisa e fazê-la bem. Criar classes especializadas em fazer uma única coisa torna um sistema mais receptivo a mudanças, mais estável. Se uma classe tem mais de uma responsabilidade, ou seja, se ela realiza mais de uma função, então essas responsabilidades se tornam acopladas, de forma que mudanças em uma responsabilidade podem afetar a capacidade da classe em atender à outra. Esse tipo de acoplamento leva a classes e métodos frágeis, que se comportam de forma inesperada quando mudados.

Consideremos o exemplo da Listagem 1. Neste exemplo temos uma classe chamada Computador com um método ligar(). Esse método chama outros métodos privados da classe: exibir(), lerTeclado() e imprimir(). Ao chamar o método ligar(), o computador exibe um texto que pede que o usuário digite uma mensagem pelo teclado, lê a mensagem e a envia para impressão através do método imprimir().

Listagem 1. Exemplo com a classe Computador.


  01. public class Computador {
  02. 
  03.   public void ligar() {
  04.      exibir("Digite a mensagem que será impressa: ");
  05.      String msg = lerTeclado();
  06.      imprimir(msg);
  07.   }
  08.
  09.   private void exibir(String msg) {
  10.      System.out.println(msg);
  11.   }
  12.
  13.   private String lerTeclado() {
  14.      Scanner sc = new Scanner(System.in);
  15.      return sc.nextLine();
  16.   }
  17.
  18.   private void imprimir(String msg) {
  19.      System.out.println("Imprimindo: " + msg);
  20.   }
  21.       
  22. }

A classe Computador exposta no exemplo está quebrando o Single Responsibility Principle, pois ela tem múltiplas responsabilidades: a de exibir um texto na tela, ler a mensagem do teclado e imprimir a mensagem digitada. Se desejarmos mudar a forma como os textos são exibidos na tela, devemos mudar a classe Computador; se desejarmos mudar a forma de ler do teclado, devemos mudar a classe Computador; se desejarmos mudar a forma de impressão da mensagem, também devemos mudar a classe Computador. Ou seja, a classe Computador possui mais de uma razão para mudar.

Sendo assim, como mudar este cenário? Como tornar uma classe compatível com o Single Responsibility Principle? Para atingir esse objetivo, deveremos identificar as responsabilidades da classe, separá-las em classes coesas e fazer a classe que quebrava o princípio chamar os métodos destas novas classes. Podemos ver um exemplo dessa mudança na Listagem 2.

Listagem 2. Código do exemplo após a refatoração.


  01. public class Impressora {
  02.
  03.   public void imprimir(String msg) {
  04.      System.out.println("[Impressora] " + msg);
  05.   }
  06.
  07. }
  08.
  09. public class Monitor {
  10.
  11.   public void exibir(String msg) {
  12.      System.out.println("[Monitor] " + msg);
  13.   }
  14.
  15. }
  16.
  17. public class Teclado {
  18.
  19.   public String ler() {
  20.      Scanner sc = new Scanner(System.in);
  21.      return sc.nextLine();
  22.   }
  23.
  24. }
  25.
  26. public class Computador {
  27.
  28.   private Monitor monitor = new Monitor();
  29.   private Impressora impressora = new Impressora();
  30.   private Teclado teclado = new Teclado();
  31.
  32.   public void ligar() {
  33.      monitor.exibir("Digite a mensagem que será impressa: ");
  34.      String msg = teclado.lerDados();
  35.      impressora.imprimir(msg);
  36.   }
  37.
  38. }

Dessa forma, foram criadas três novas classes: Monitor, Impressora e Teclado. Cada uma delas tem a sua única responsabilidade. A classe Monitor possui um método chamado exibir() e é responsável por exibir textos na tela; a classe Teclado possui um método chamado lerDados(), que lê uma mensagem digitada pelo usuário no teclado e a retorna; e a classe Impressora possui um método chamado imprimir(), que é responsável por imprimir o texto que lhe foi passado como parâmetro.

Depois de criar essas classes, é necessário modificar a classe Computador, que passará a ter três atributos: um monitor, um teclado e uma impressora. O método ligar() irá agora chamar o método exibir() do Monitor, lerDados() do Teclado e imprimir() da Impressora ...

Quer ler esse conteúdo completo? Tenha acesso completo