Base de Conhecimento

Princípios SOLID em Delphi [1] – O Princípio da Responsabilidade Única

Princípio da responsabilidade única

Hoje, vamos mergulhar no S dos SOLID Princípios: O Princípio da Responsabilidade Única. Mas antes de começarmos, apenas uma rápida atualização sobre o SOLID.

SOLID é um acrônimo para um conjunto de cinco princípios de desenvolvimento de software que, se seguidos, se destinam a ajudar os desenvolvedores a criar código flexível e limpo. Os cinco princípios são:

O princípio da responsabilidade única — As classes devem ter uma única responsabilidade e, portanto, apenas um único motivo para mudar.

O princípio aberto/fechado — As classes e outras entidades devem ser abertas para extensão, mas fechadas para modificação.

O Princípio de Substituição Liskov — Os objetos devem ser substituíveis por seus subtipos.

O princípio de segregação da interface — Os clientes não devem ser forçados a depender de interfaces que não utilizam.

O Princípio da Inversão de Dependência — Depende mais das abstrações do que das concreções.


Portanto, o princípio da responsabilidade única. Como o nome sugere, cada classe em um programa deve ter uma única responsabilidade por apenas uma parte da funcionalidade do programa. Mas isso parece mais fácil do que é. O que é exatamente uma parte de um programa, e como saber quando separar a funcionalidade? É muito simples dizer que uma classe só deve fazer uma coisa.

Robert C. Martin expressa o princípio como “Reunir as coisas que mudam pelas mesmas razões”. Separar as coisas que mudam por diferentes razões”, e mais recentemente “Este princípio é sobre as pessoas“. Isso deve nos indicar a direção correta.

Quando você escreve um módulo de software, você quer ter certeza de que quando as mudanças são solicitadas, essas mudanças só podem se originar de uma única pessoa, ou de um único grupo estreitamente acoplado de pessoas representando uma única função empresarial estritamente definida. Isto significa que um módulo ou classe de software deve ter uma responsabilidade para esse grupo particular de pessoas.

É mais fácil explicar isto por meio de um exemplo. Vamos dar uma olhada nesta classe a seguir:

type
  TShip = class
  private
    FPosition: TPoint;
    FHeading: Integer;
    FSpeed: Integer;
    FCargoLoad: string;
  public
    procedure SetHeading(NewHeading: Integer);
    procedure SetSpeed(NewSpeed: Integer);
    function GetCoordinate: TPoint;
    procedure PlotCourse;
    procedure LoadCargo(NewCargo: string);
    procedure PrintCargo;
    procedure ReportPosition;
    procedure CalculateProfit;
  end;

 

Esta é uma classe que está fazendo um par de coisas, tudo sobre administrar a posição e o curso de um navio, sua carga e algumas coisas para relatar. Como este é um breve exemplo para mostrar como pensar sobre o Princípio de Responsabilidade Única, não preste muita atenção aos detalhes do código em si; trata-se de uma grande visão geral da estrutura desta classe em particular.

Acho que todos nós conhecemos este tipo de classe de “Deus”. Normalmente, repletas de muita funcionalidade e código, e gerenciando uma parte ou módulo em particular de seu programa. A questão é, se precisamos fazer algumas mudanças nesta classe, como podemos refatorar esta classe para ter certeza de reunir as coisas que mudam pelo mesmo motivo, e separar aquelas coisas que mudam por diferentes motivos.

Vamos parar por um momento e pensar sobre as responsabilidades deste código em relação ao (grupo de) pessoas. Podemos definir um par de pessoas específicas que terão alguma responsabilidade com relação à administração do navio, direção e rumo, e ferramentas de relatório. Portanto, digamos que neste caso definamos um capitão, um navegador, um comandante de carga e um gerente financeiro. Se você pensar nestas diferentes funções, de repente é muito fácil separar esta classe em diferentes módulos, com apenas uma responsabilidade para essa função em particular. Devemos ter uma classe para definir o rumo e a potência (capitão), uma para gerenciar a posição e traçar o rumo do navio (navegador), uma para gerenciar a carga deste navio (comandante da carga) e uma para todos os nossos relatórios (financeiro) (gerente financeiro).

Nossas novas classes podem agora se parecer com isto:

type
  TShipLocation = class
  private
    FPosition: TPoint;
  public
    function GetCoordinate: TPoint;
    procedure PlotCourse;
    procedure ReportPosition;
  end;

  TShipMovement = class
  private
    FHeading: Integer;
    FSpeed: Integer;
  public
    procedure SetHeading(NewHeading: Integer);
    procedure SetSpeed(NewSpeed: Integer);
  end;

  TCargo = class
  private
    FCargoLoad: string;
  public
    procedure LoadCargo(NewCargo: string);
    procedure PrintCargo;
  end;

  TShipReport = class
  public
    procedure CalculateProfit;
  end;

  TShip = class
  private
  public
    // reference to subclasses
  end;

 

Você teria feito o mesmo se não tivesse pensado nas pessoas por trás das responsabilidades da classe? Talvez, mas eu posso imaginar que a classe ShipLocation e ShipMovement poderiam ter acabado na mesma classe.

Então, o que acontece agora se recebermos um pedido do capitão para adicionar um propulsor de proa para facilitar a condução do navio em pequenos canais? Basta fazer esta mudança na classe ShipMovement, sem afetar nenhuma das outras classes. E se quisermos implementar um novo sistema de carregamento de carga? Basta alterar a classe Cargo, novamente sem tocar em nenhuma das outras classes.

Espero que agora você veja porque o princípio da responsabilidade única é realmente sobre pessoas, ou atores, e a responsabilidade da funcionalidade dos módulos ou classes de seu programa em relação a essas pessoas. E, claro, você pode aplicar isto em diferentes níveis de seu programa, desde módulos a classes até funções específicas.

Se você sempre tiver este princípio em mente ao refatorar ou projetar um módulo ou classe, tenho certeza de que seu código será melhor mantido e é fácil de mudar.

Torne-se um herói da GDK Delphi! Subscreva aqui para nossa lista de correio da Delphi!

Written by Marco Geuze
Diretor

Contato

Deixe-nos ajudá-lo a realizar seus sonhos.