/ Getting Started

Clean Code - Objetos e estruturas de dados

Olá pessoal, esperamos que esta série de artigos ajudem vocês a se tornarem melhores desenvolvedores e perpetuem sua disciplina na codificação. Neste artigo vamos falar sobre Objetos, estruturas de dados e conhecimento da estrutura interna. Boa leitura!

Há um motivo para declararmos nossas variáveis como privadas. Não queremos que ninguém dependa ou tenha cohecimento sobre a estrutura delas. Por quê, então, tantos programadores adicionam automaticamente métodos de acesso (getters & setters) em seus objetos, expondo suas variáveis com se fossem públicas?

Abstração de dados

Ocultar a implementação não é uma questão de colocar uma camada de funções entre as variáveis. Devemos abstrair todas as informações que remetem aos dados que estão sendo manipulados. Uma classe não passa suas variáveis por métodos de escrita e leitura, ao invés disso ela expõe interfaces abstratas que permitem aos usuários manipular a essência dos dados sem precisar conhecer a implementação.

Concreto:

public class Point {
    public double x;
    public double y;
}

Abstrato:

public interface Point {
    double getX();
    double getY();
    void setCartesian(double x, double y);
    double getR();
    double getTheta();
    void setPolar(double r, double theta);
}

Anti-simetria data/objeto

Os objetos utilizam-se de abstrações para esconder seus dados e expõem as funções que operam tais dados. As estruturas de dados expõem seus dados e não possuem funções significativas.

Concreto:

public interface Vehicle {
    double getFuelTankCapacityInGallons();
    double getGallonsOfGasoline();
}

Abstrato:

public interface Vehicle {
    double getPercentFuelRemaining();
}

O primeiro exemplo é citado no livro como uma estrutura de dados, pois através dele é possível acessar informações importantes sobre a estrutura da codificação, como: os tipos das variáveis manipuladas pelos métodos. Porém no segundo exemplo você não tem conhecimento sobre os dados ou como são manipulados.

É importante evidenciar que Estruturas de dados possuem maior facilidade em construir novas funcionalidades, porém possuem dificuldade com a construção de novos objetos. Observe o seguinte exemplo:

public class Quadrado {
    public Point superiorEsquerdo;
    public double valorLado;
}
public class Geometria {
    public final double PI = 3.141592653589793;
    public double area(Object shape) throws NoSuchShapeException{
        if (shape instanceof Quadrado) {
            Quadrado s = (Quadrado)shape;
            return s.valorLado * s.valorLado;
        }
        else
            throw new NoSuchShapeException();
    }
}

Neste exemplo podemos notar que todas as funcionalidades pertencem à classe Geometria, logo a adição de funcionalidades é simplificada e não interfere nas demais classes. Isso torna o exemplo em programação funcional, segundo o livro.

Já se utilizando de polimorfismo temos o seguinte exemplo:

public class Quadrado implements Shape {
    private Point superiorEsquerdo;
    private double valorLado;
    public double area() {
        return valorLado*valorLado;
    }
}
public class Retangulo implements Shape {
    private Point superiorEsquerdo;
    private double altura;
    private double largura;
    public double area() {
        return altura * largura;
    }
}

Neste exemplo temos implementada a interface Shape, nela estão todas as funcionalidades necessárias para as demais. Podemos notar que dessa forma a criação de novos objetos derivados de Shape é facilitada, porém se adicionarmos uma nova funcionalidade a Shape temos de modificar todas as outras.

A lei de Demeter

Há uma nova heurística chamada lei de Demeter, na qual um módulo não deve enxergar o interior dos objetos que ele manipula. Como já evidenciamos na seção anterior, objetos escondem seus dados e expõem as operações. Um objeto não deve expor sua estrutura interna por meio de métodos de acesso, pois está expondo.

A lei de Demeter ou princípio do menor conhecimento foi proposta por Ian Holland em 1987, com o objetivo de minimizar os problemas de acoplamento fraco. Mais precisamente, a lei de Demeter diz que um método f de uma classe C só deve conhecer os métodos de:

  • C
  • Um objeto criado por f
  • Um objeto passado como parâmetro para f
  • Um objeto dentro de uma instância da classe C

Nossos métodos falam apenas com conhecidos a eles e nunca acessam métodos em objetos retornados por qualquer outra das funções permitidas.

Exemplo:

final String outputDir = ctxt.getOptions()
    .getScratchDir().getAbsolutePath();

O código acima viola a Lei de Demeter, pois ele chama a função getScratchDir() no valor retornado de getOptions() e chama o getAbsolutPath() no valor retornado de getScratchDir().

Gostaríamos de trazer alguns exemplos deste problema, separando entre as linguagens java e javascript para melhor entendimento.

Exemplo Java:

public class Motor {
	public String nomeMotor;
	public Motor(String nomeMotor) {
		nomeMotor = this.nomeMotor;
	}
    
	public void sentidoHorario(int movimentos) {
		System.out.println("Rotacionou sentido 
                    horário: "+movimentos+" movimentos");
	}
    
	public void sentidoAntiHorario(int movimentos) {
		System.out.println("Rotacionou sentido 
                anti horário: "+movimentos+" movimentos");
	}
}
public class ObjetoMecanico {
	public Motor RotacaoMotor = new Motor("Rotacionar");
}
public class Controleremoto {
	ObjetoMecanico objMec = new ObjetoMecanico();
	public void rotacionarSentidoHorario() {
		objMec.RotacaoMotor.sentidoHorario(10);
	}
	public void rotacionarSentidoAntiHorario() {
		objMec.RotacaoMotor.sentidoAntiHorario(5);
	}
}

Exemplo Javascript:

function Person(name, adress) {
   this.name = name;
   this.adress = new Adress('Rui Barboza', 330);
   this.displayName = function() {
       console.log(Name: ${this.name});
   }
   this.getAdress = function(){
       return this.adress;
   }
}
function Adress(street, number){
   this.street = street;
   this.number = number;
  
   this.displayAdress = function(){
       console.log(Adress: ${this.street}, 
       Number: ${this.number});
   }
}

var person = new Person('Alan);
person.getAdress().displayAdress()

Carrinhos de trem

Os carrinhos de trem são chamadas a funções sequenciais e normalmente violam a lei de Demeter. Elas se chamam assim pois são semelhantes ao acoplamento ocorrido em vagões de trem, visualize o seguinte trecho:

final String outputDir = ctxt.getOptions()
    .getScratchDir().getAbsolutePath();

Podemos escrever da seguinte forma:

Options opts = ctxt.getOptions();
File scratchDir = opts.getScratchDir();
final String outputDir = scratchDir.getAbsolutePath();

Este trecho viola a Lei de Demeter? Com muita certeza é uma enorme quantidade de informações trocadas entre as funções. A função de chamada sabe como navegar por muitos objetos diferentes, conhecendo desde o objeto Options até o ctxt.

Tomando Options e ctxt como objetos podemos afirmar que há uma violação da lei de Demeter, lembrando que os objetos devem ocultar sua estrutura interna.

Objetos de transferência de dados

Os DTOs (sigla em inglês) são estruturas muito úteis, especialmente em se comunicar com bancos de dados. Estes costumam se tornar os primeiros em uma série de estágios de tradução que convertem dados brutos de um banco de dados em objetos.
Estas estruturas não possuem funcionalidades, apenas métodos acessores de suas variáveis.

O Active Record

Os active records são instâncias especiais de DTOs, eles são estruturas de dados com variáveis públicas e possuem métodos de navegação como salvar e buscar. Porém não devemos implementar regras de negócio diretamente em nossos active records, pois cria um híbrido entre objetos e estrutura de dados.

Conclusão

Lembre-se sempre de que objetos expõem suas ações e ocultam os dados. Essa característica facilita a adição de novos tipos de objetos sem precisar modificar ações existentes. As estruturas de dados expõem seus dados e não possuem ações significativas. Bons desenvolvedores de software entendem essas questões sem preconceito e selecionam a abordagem que melhor se aplica no momento.

Referências

Robert C. Martin, Clean Code: A Handbook of Agile Software Craftsmanship.
Disponível em https://www.investigatii.md/uploads/resurse/Clean_Code.pdf.

Augusto, Felipe. Conceitos de Código Limpo.
Disponível em https://github.com/felipe-augusto/clean-code-javascript#Índice