Anotações Capítulo 6: Objects and Data Structures
Tags: books, codequality
Published: 2021-06-11 17:10:27

- **“Há um motivo para declararmos nossas variáveis como privadas. Não queremos que ninguém dependa delas”**;
- Assim temos a liberdade para alterar o tipo ou a implementação;
- Porque, então, tantos programadores adicionam automaticamente métodos de acesso em seus objetos, como se fossem públicas?

## Abstração de dados
- Representação de dados de um ponto no plano cartesiano:
- **Caso concreto**:
```
public class Point {
    public double x;
    public double y;
}
```
- **Caso abstrato**:
```
public interface Point {
    double getX();
    double getY();
    void setCartesian(double x, double y);
    double getR();
    double getTheta();
    void setPolar(double r, double theta);
}
```
- “Assim, no **“caso abstrato”** não há como dizer se a implementação possui coordenadas retangulares ou polares. Pode não ser nenhuma! E ainda assim a interface representa de modo claro uma estrutura de dados.”
- “Os métodos exigem uma regra de acesso. Você pode ler as coordenadas individuais independente, mas deve configurá-las juntas como uma operação atômica”.
- Já no **“caso concreto”** está implementada em coordenadas retangulares, e nos obriga a manipulá-las independentemente. Isso expõe a implementação. 
- A implementação no **“caso concreto”** seria exposta mesmo se as variáveis fossem privadas e estivéssemos usando métodos únicos de escrita e leitura de variáveis.
- **“Ocultar a implementação não é só uma questão de colocar uma camada de funções entre as variáveis. Ocultar a implementação tem a ver com abstrações!”**;
- Uma classe não passa suas variáveis simplesmente por meio de métodos getters e setters.
- Em vez disso, ele expõe interfaces abstratas que permitem que seus usuários manipulem a *essência* dos dados, sem precisar saber sua implementação.
- Veja as listagens abaixo, a primeira usa termos concretos para comunicar o nível de combustível de um veículo, enquanto a segunda faz isso com a abstração da porcentagem.
```
public interface Vehicle {
    double getFuelTankCapacityInGallons();
    double getGallonsOfGasoline();
}
```
```
public interface Vehicle {
    double getPercentFuelRemaining();
}
```
No primeiro caso, você tem certeza de que são apenas métodos acessores (getter e setter). No segundo, não há como saber o tipo de dados.
- **Nos casos acima, o segundo é preferível. Não queremos expor os detalhes de nossos dados**.
- Queremos expressar nossos dados de forma abstrata. Isso não se consegue meramente através de interfaces e/ou getters e setters. É preciso pensar bastante na melhor maneira de representar os dados que um objeto contém.
- A pior opção é adicionar levianamente métodos getter e setter.

## Anti-Simetria de Dados/Objeto
- **Os objetos usam abstrações para esconder seus dados, e expõem as funções que operam em tais dados**.
- **As estruturas de dados expõem seus dados e não possuem funções significativas**.
- Veja a classe de forma procedural:
```
public class Square {
    public Point topLeft;
    public double side;
}

public class Rectangle {
    public Point topLeft;
    public double height;
    public double width;
}

public class Circle {
    public Point center;
    public double radius;
}

public class Geometry {
    public final double PI = 3.141592653589793;

    public double area(Object shape) throws NoSuchShapeException {
        if (shape instanceof Square) {
            Square s = (Square)shape;
            return s.side * s.side;
        } else if (shape instanceof Rectangle) {
            Rectangle r = (Rectangle)shape;
            return r.height * r.width;
        } else if (shape instanceof Circle) {
            Circle c = (Circle)shape;
            return PI * c.radius * c.radius;
        }
        throw new NoSuchShapeException();
    }
}
```
Nesse exemplo a classe `Geometry` opera sobre as três outras classes que são simples estruturas de dados sem qualquer atividade. 
- Esse exemplo anterior é procedural, mas nem sempre. Se adicionássemos uma função `perimeter()` à `Geometry`, as demais classes não seriam afetadas! Assim como outras que dependesse delas!
- Porém se adicionarmos uma nova classe `shape`, teremos que alterar todas as funções em `Geometry`.
- Agora uma solução orientada a objeto:
```
public class Square implements Shape {
    private Point topLeft;
    private double side;

    public double area() {
        return side*side;
    }
}

public class Rectangle implements Shape {
    private Point topLeft;
    private double height;
    private double width;

    public double area() {
        return height * width;
    }
}

public class Circle implements Shape {
    private Point center;
    private double radius;
    public final double PI = 3.141592653589793;

    public double area() {
        return PI * radius * radius;
    }
}
```
O método `area()` é *polifórmico*, assim não é necessária a classe `Geometry`. Portanto, se adicionarmos uma nova forma, nenhuma das funções existentes serão afetadas, mas se adicionarmos uma nova função, todas as classes `shape` deverão ser alteradas.

- Duas definições complementares, mas praticamente opostas:
**“O código procedural (usado em estruturas de dados) facilita a adição de novas funções sem precisar alterar as estruturas de dados existentes. O código orientado a objeto (OO), por outro lado, facilita a adição de novas classes sem precisar alterar as funções existentes”**.
- O inverso também é verdade:
**“O código procedural dificulta a adição de novas estruturas de dados, pois todas as funções teriam de ser alteradas. O código OO dificulta a adição de novas funções, pois todas as classes teriam de ser alteradas”**.
- **“O que é difícil para a OO é fácil para o procedural, e o que é difícil para o procedural é fácil para a OO”**.
- Quando desejarmos adicionar novos tipos de dados em vez de novas funções, os objetos e OO são mais apropriados. 
- Por outro lado, se desejarmos adicionar novas funções em vez de tipos de dados, estruturas de dados e código procedural são mais adequados.
- **Programadores experientes sabem que a ideia de que tudo é um objeto “é um mito”. Às vezes, você realmente “deseja” estruturas de dados simples com procedimentos operando nelas**.


## A lei de Demeter
- **“Um módulo não deve enxergar o interior dos objetos que ele manipula”**. 
- Objetos escondem seus dados e expõem as operações.
- Um objeto não deve expor sua estrutura interna por meio dos métodos acessores, pois isso seria expor sua estrutura interna.
- A lei fala que um método “f” de uma classe “C” só deve chamar 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 variável “C”**.
- O método **não** deve chamar os métodos em objetos retornados por qualquer outra das funções permitidas.
- “Fale apenas com conhecidos, não com estranhos”.
- Um exemplo que viola essa lei:
```
final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();
```

## Trem descarrilhado

- Esse código acima costuma ser chamado de acidente de trem, pois parece com um monte de carrinhos de trem acoplados.
- Cadeias de chamadas como essa geralmente são consideradas descuidadas e devem ser evitadas. É melhor dividi-las assim:
```
Options opts = ctxt.getOptions();
File scratchDir = opts.getScratchDir();
final String outputDir = scratchDir.getAbsolutePath();
```
- **Essa função acima tem muito conhecimento**.
- Se `ctxt`, `options` e `scracthDir` forem objetos, então este código viola a lei, já que sua estrutura interna está exposta. Porém, se forem apenas estruturas de dados sem atividades, então elas naturalmente expõem suas estruturas internas, e não se aplica a lei.
- O uso de assessores (getters) confunde as questões. Se o código tiver escrito como abaixo, não teríamos dúvidas:
```
final String outputDir = ctxt.options.scratchDir.absolutePath;
```
- **Seria menos confuso se as estruturas de dados tivessem apenas variáveis públicas e nenhuma função, e os objetos apenas variáveis privadas e funções públicas**.


## Híbridos
- Essa confusão leva a estruturas híbridas ruins, que são metade objeto e metade estrutura de dados.
- Funções que fazem algo significativo, e variáveis ou métodos de acesso e de alteração públicos, que para todos os efeitos, tornam públicas as variáveis privadas, deixando com que funções externas usem tais variáveis de forma como um programa procedural usaria.
- Esses híbridos dificultam tanto a adição de novas funções como de novas estruturas de dados.
- **São a pior coisa em ambas as condições. Evite criá-los**.

## Estruturas ocultas

- Se `ctxt` for um objeto, devemos dizê-lo para fazer algo, não devemos perguntá-lo sobre sua estrutura interna.
- Devemos sempre pedir para que o próprio objeto faça a ação:
```
BufferedOutputStream bos = ctxt.createScratchFileStream(classFileName);
```
- Esse código acima é algo razoável para um objeto fazer.
- Dessa forma o `ctxt` esconde sua estrutura interna e evita que a função atual viole a Lei de Demeter ao navegar por objetos os quais ela não deveria enxergar.


## Objetos de transferência de dados
- **A forma perfeita de uma estrutura de dados é uma classe com variáveis públicas e nenhuma função**.
- Geralmente chama-se isso de objeto de transferência de dados, ou DTO.
- Os DTOs, são estruturas muito úteis, para se comunicar com banco de dados ou mensagens e assim por diante.
- O formulário “bean” abaixo, têm variáveis privadas manipuladas por métodos de escrita e leitura, e o aparente encapsulamento dos beans parece fazer alguns puristas da OO se sentirem melhores:
```
public class Address {
    private String street;
    private String streetExtra;
    private String city;
    private String state;
    private String zip;

    public Address(String street, String streetExtra, String city, String state, String zip) {
        this.street = street;
        this.streetExtra = streetExtra;
        this.city = city;
        this.state = state;
        this.zip = zip;
    }

    public String getStreet() {
        return street;
    }

    public String getStreetExtra() {
        return streetExtra;
    }

    public String getCity() {
        return city;
    }

    public String getState() {
        return state;
    }

    public String getZip() {
        return zip;
    }
}
```



## O Active Record
- São formas especiais de DTOs;
- São estruturas de dados com variáveis públicas;
- Mas eles tipicamente possuem métodos de navegação, como save e find.
- São traduções diretas das tabelas de bancos de dados ou de outras fontes de dados.
- Costumamos ver desenvolvedores tratando essas estruturas de dados como se fossem objetos, colocando regras de negócios. Isso é complicado, pois cria um híbrido.
- A solução para o ponto anterior é tratar o Active Record como uma estrutura de dados e criar objetos separados que contenham as regras de negócio e que ocultem seus dados internos.

## Conclusão

- Objetos expõem as ações e ocultam os dados. Assim, facilita a adição de novos tipos de objetos sem precisar modificar as ações existentes e dificulta a inclusão de novas atividades em objetos existentes.
- As estruturas de dados expõem os dados e não possuem ações significativas. Assim, facilita a adição de novas ações às estruturas de dados existentes e dificulta a inclusão de novas estruturas de dados em funções existentes.
- Às vezes buscamos flexibilidade para adicionar novos tipos de dados, e optamos por objetos.
- Às vezes buscamos flexibilidade para adicionar novas ações, e optamos por estrutura de dados e procedimentos.
- **“Bons desenvolvedores de software entendem essas questões sem preconceito e selecionam a abordagem que melhor se aplica no momento”**.