Programação Orientada Objetos e Programação Procedural - entendendo a diferença
Published: 2022-01-30 15:58:04

Em algum momento na nossa carreira de desenvolvedores topamos com uma discussão sobre paradigmas de linguagens de programação. E como é importante entendermos mais sobre as vantagens que uma linguagem pode nos trazer para fazermos a escolha mais vantajosa para nosso projeto, entender a diferença e as vantagens dos paradigmas de programação vai nos ajudar. Então, foi pensando nisso que resolvi escrever esse post explicando a diferença e trazendo alguns exemplos práticos de dois paradigmas de programação, o procedural e o orientado a objetos.

# Programação orientada a objetos

A Programação orientada objetos (POO) se baseia na ideia de que tudo são classes e objetos, as classes são como agrupadores e objetos são os participantes desses grupos. Cada classe define atributos e métodos que são comuns a todos pertencentes a essa classe, nesse caso a cada objeto que seja uma instância da classe. Os objetos podem possuir um atributo que seja  _IGUAL _  a todos pertencentes á classe, que são chamados de atributos da classe, ou atributos que apesar de definirem a mesma coisa não possuem o mesmo valor, chamados de atributos do objeto (como por exemplo o nome, toda pessoa possui um nome, mas não obrigatoriamente toda pessoa possui o mesmo nome). Os métodos são abstrações para esconder os detalhes dos objetos, expondo apenas as funções que operam seus dados.
Outro conceito muito importante no mundo de POO, é o de **polimorfismo**, que simplificando é o princípio de que classes derivadas de uma mesma superclasse (classe mãe) expõem métodos que possuiem a  **mesma identificação porém com implementações diferentes**, pois cada classe possui sua singularidade e por isso comportamentos distintos para realizar a mesma ação. Exemplificando: imaginamos a superclasse `VeiculoDeLomocoção`, ela define que todas suas derividas devem possuir o método `limpar`, a classe derivada `Carro` implementa o método `limpar` que seria passar lava-jato e cera em todo carro, já a classe `Bicicleta` implementa limpar como apenas passar uma mangueira no veículo. Entrentanto, esse comportamento interno não importa pra quem vê a classe de fora. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3fhqdeai8hzf0ftyghfk.png)

# Programação Procedural

Por outro lado, a Programação Procedural (PC) ou também chamada como **Programação Estruturada** se resume a modularizar instruções da execucação do programa em **procedimentos**. Os procedimentos contêm um conjunto de passos computacionais a serem executados. Um dado procedimento pode ser chamado a qualquer hora durante a execução de um programa, inclusive por outros procedimentos ou por si mesmo.  Os procedimentos podem receber parâmetros para realizar manipulações e podem ou não retornar valores, e diferente do conceito da programação funcional, procedimentos também podem causar efeitos colaterais como os métodos de objetos.
Então simplificando a diferença de conceitos entre uma função e um procedimento é de que uma função sempre retornaria algum valor e não modificaria nenhum parâmetro fora de seu escopo, já os procedimentos podem modificar algo fora de seu escopo e não necessariamente retornam algum valor. **Essa alteração de dados de fora de seu escopo que são os chamados de efeitos colaterais.**

# Na prática 💪
No livro [Clean Code do Uncle Bob](https://g.co/kgs/QzZy9o), no capítulo acerca de Estrutura de Dados e Objetos ele menciona um exemplo muito claro sobre a diferença desses dois paradigmas, então resolvi trazer o mesmo exemplo nesse post para analisarmos a implementação do mesmo programa nos dois paradigmas e compararmos suas diferenças.

Imagine o seguinte cenário, precisamos calcular a área de algumas formas geométricas, cada uma possui uma fórmula diferente para o cálculo de sua área.

Em POO a implementação aconteceria mais ou menos da seguinte forma

💡 Consideramos `Shape` como sendo uma interface que define o contrato para que uma forma seja uma forma geométrica

```
public class Square implements Shape {
    private double sideValue;
   
    //construtor

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

public class Rectangle implements Shape {
    private double heigth;
    private double width;

    //construtor

    public double area() {
        return width* heigth;
    }
}
```
Se quissemos calcular a área de qualquer uma dessas formas, bastaria:

`public double squareAreaValue = new Square(4);`

Já em PC a implementação seria diferente:

```
public class Square {
    public double sideValue;
}

public class Rectangle {
    public double heigth;
    public double width;
}

public class Geometry{
    public double area(Object shape) throws NoSuchShapeException{
        if (shape instanceof Square) {
            Square s = (Square)shape;
            return s.sideValue* s.sideValue;
        }
        else if (shape instanceof Rectangle ) {
            Rectangle r = (Rectangle )shape;
            return r.width* r.heigth;
        }
        else
            throw new NoSuchShapeException();
    }
}
```
Se quissemos calcular a área de uma dessas formas no último exemplo fariamos o seguinte:

```
public Square square = new Square();
square.sideValue = 4;
public Geometry geometryCalc = new Geometry();
public double squareAreaValue = geometryCalc.area(square);
```

## Comparando:

Uma das maiores diferenças na minha opnião é a forma em que os dados são tratados, note que no exemplo de POO nós manipulamos os **objetos** mas não temos acesso direto aos valores e nem mesmo sabemos como os valores são utilizados para o cálculo da área, pois os objetos expõem apenas o método necessário para obter a área da forma geométrica e abstraem seu funcionamento interno. 
Já no exemplo de PC percebemos os valores como uma **estrutura de dados**, e segundo Uncle Bob
>  As estruturas de dados expõem seus dados e não possuem funções significativas para a sua manipulação. 

Mas diferente do primeiro exemplo nós conseguimos manipular diretamente esses valores e realizar implementação que desejamos com eles.

Outra diferença, também citada em Clean Code é que a facilidade e a dificuldade de implementação são invertidas em cada um dos paradigmas. Como assim? Note que ao usar as estruturas de dados em PC, podemos adicionar novos procedimentos facilmente, por que precisamos alterar apenas um local, porém para adicionar uma nova estrutura de dados teriamos que alterar todos os procedimentos que as utilizam - imagine se tivessemos um procedimento para calculo da área, outro para perimetro, outro para mediatrix, todos teriam que ser alterados para prever essa nova forma geométrica.
Mas em POO temos o contrário, para adicionar um novo método seria mais trabalhoso por que teriamos que alterar todas as classes de formas geométrica para implementar esse novo método, já para adicionar uma nova classe (uma nova forma geométrica) precisariamos alterar apenas um local, que seria criando a nova classe seguindo a interface `Shape`.

Além disso, mesmo com os exemplos estando na mesma linguagem, notamos que cada um faz o uso dos recursos da linguagem de maneira diferente, mostrando que uma linguagem não é obrigatoriamente presa a um paradigma.