Pular para o conteúdo principal

Verba Volant

POO: uma ideia

Sumário

1. Imagine

Imagine um carro.
Isso, pode ser um Porsche.
Ou um Volkswagen.

Agora vamos reduzir este carro aos elementos mais básico possíveis, e vamos chamar essa ação — imaginar um carro em sua essência — de abstrair.

Ok. Estamos prontos para abstrair esse carro.

Portanto vamos deixar esse carro apenas com as características (atributos) e ações (métodos) mais básicos possíveis.

Quais são as características (atributos, estados) de um carro? Várias.
Mas para deixar didático, vamos listar só três que são bem evidentes.

  • Cor

  • Ano de fabricação

  • Marca

Quais ações que você pode executar em um carro? Ou melhor, quais comportamentos (métodos) um carro pode ter? Vários. Mas, novamente, vamos listar apenas três.

  • Acelerar

  • Frear

  • Buzinar

Vamos chamar este carro na sua forma mais básica, isto é, na sua forma abstraída de classe Carro.
E uma classe é praticamente um…​
…​um molde, como um molde de roupa. Ou uma fôrma, como uma fôrma de bolo.

Pensemos: Com uma fôrma de bolo eu posso fazer, bolo de chocolate, bolo de laranja, bolo de limão etc…​
Com o molde de uma roupa, eu posso costurar uma roupa azul, verde, roxa etc..

E com nossa classe Carro, nos poderemos fazer um Fiat, um Ford, um BMW, um Toyota, um Mercedes-Benz, um Audi etc..
Pois todos os carros aceleram, freiam, buzinam e todos tem ano de fabricação, cor e marca.

Bom, para melhorar a visualização, vamos usar um diagrama simples, que representará a classe Carro.

|------------|
|   Carro    |
|------------|
|Cor         |
|Ano         |
|Marca       |
|------------|
|Acelerar () |
|Frear ()    |
|Buzinar ()  |
|------------|

O diagrama acima descreve a classe Carro, com seus atributos e métodos.
Colocamos parênteses na frente dos comportamentos, para indicar que são métodos.

Depois de ler esse artigo e entender bem os conceitos básicos, e caso queira pesquisar, o diagrama acima, trata-se de um diagrama de classes. Esse tipo de diagrama é baseado na Unified Modeling Language (UML), que é um modo padrão de podermos visualizar um projeto antes da etapa de programação. O diagrama acima está bem simplificado e não abordaremos UML neste artigo. Insisto para que só pesquise sobre UML após ter entendido bem os conceitos deste artigo.

Uma curiosidade é que uma função dentro de uma classe se chama método, e fora de uma classe se chama…​
…​função mesmo, não tem outro nome neste caso.

…​Isso, exatamente, um método é uma função que está dentro de uma classe.

Agora como escrever isto em linguagem de programação?
Bom, cada linguagem tem uma sintaxe, isto é, um jeito de escrever as coisas.

No python, a sintaxe é assim:

class Carro:
    # Método construtor (init) que inicializa os atributos
    def __init__(self, cor, ano, marca):
        self.cor = cor # Inicializando o atributo 'cor'
        self.ano = ano # Inicializando o atributo 'ano'
        self.marca = marca # Inicializando o atributo 'marca'

    # Métodos para o carro
    def acelerar(self):
        print(f"O carro {self.marca} acelerou!")

    def frear(self):
        print(f"O carro {self.marca} freou!")

    def buzinar(self):
        print(f"O carro {self.marca} buzinou!")

No Javascript, a sintaxe é assim:

class Carro {
    // Método construtor (constructor) que inicializa os atributos
    constructor(cor, ano, marca) {
        this.cor = cor; // Inicializando o atributo 'cor'
        this.ano = ano; // Inicializando o atributo 'ano'
        this.marca = marca; // Inicializando o atributo 'marca'
    }

    // Métodos para o carro
    acelerar() {
        console.log(`O carro ${this.marca} acelerou!`);
    }

    frear() {
        console.log(`O carro ${this.marca} freou!`);
    }

    buzinar() {
        console.log(`O carro ${this.marca} buzinou!`);
    }
}

2. A instância

Que tal usar um molde de uma camisa para costurar uma camisa cinza.
Que tal pegar uma fôrma quadrada para fazer um bolo quadrado.
Que tal pegar a classe Carro e criar um fusca, vermelho, 1968.

O processo de criar algo concreto,um objeto, a partir de um molde, fôrma ou classe, chama-se instanciar. E toda vez que você ler ou ouvir que uma classe foi instanciada, isso que dizer que um objeto foi criado a partir de uma classe.

Por tanto, a instância de uma classe é um objeto criado a partir dela. E o processo de criar este objeto chama-se instanciar.

Bem, há diversos modos de dizer uma mesma coisa. Assim, podemos falar o seguinte:

  • " Vamos instanciar a classe Carro para criar o objeto fusca, vermelho, 1968. "

Ou

  • "O objeto fusca, vermelho, 1968 é uma instância da classe Carro."

Uma classe pode ser usada várias vezes como molde para criar diferentes objetos. Por exemplo, a classe Carro pode ser instanciada para criar um Fusca, uma Kombi ou qualquer outro carro com características específicas.

Agora, veja como podemos instanciar (criar) os objetos meu_carro e meuCarro a partir da classe Carro, nas sintaxes do Python e do Javascript.

Em python:

class Carro:
    def __init__(self, cor, ano, marca):
        self.cor = cor
        self.ano = ano
        self.marca = marca

# Instanciando a classe Carro e criando o objeto meu_carro
meu_carro = Carro("Vermelho", 1968, "Fusca")

Em javascript:

class Carro {
    constructor(cor, ano, marca) {
        this.cor = cor;
        this.ano = ano;
        this.marca = marca;
    }
}

// Instanciando a classe Carro e criando o objeto meuCarro
let meuCarro = new Carro("Azul", 1988, "Fusca");

3. O que é método construtor

O método construtor (no Python chamado init e no JavaScript chamado constructor) é um método especial usado para inicializar o estado do objeto no momento em que ele é criado. Ele é chamado automaticamente quando você cria um novo objeto a partir da classe. O construtor serve para inicializar os atributos do objeto com valores passados como parâmetros.

4. O que é inicializar

Inicializar é o processo de atribuir um valor inicial a uma variável ou a um atributo de um objeto. Quando falamos de inicialização em programação, geralmente estamos nos referindo ao momento em que algo (uma variável, um objeto, ou qualquer estrutura de dados) recebe um valor ou estado inicial antes de ser usado durante a execução de um programa.

4.1. Inicializando uma variável

Em python:

x = 5  # A variável x é inicializada com o valor 5

Em javascript:

let y = 10;  // A variável y é inicializada com o valor 10

4.2. Inicializando um objeto

No caso de objetos, a inicialização envolve definir os atributos ou propriedades com valores iniciais. Isso é feito dentro do método construtor de uma classe.

Em python:

class Carro:
    def __init__(self, cor, ano, marca):
        self.cor = cor  # Inicializando o atributo 'cor'
        self.ano = ano  # Inicializando o atributo 'ano'
        self.marca = marca  # Inicializando o atributo 'marca'

meu_carro = Carro("Vermelho", 1968, "Fusca")

Em javascript:

class Carro {
    constructor(cor, ano, marca) {
        this.cor = cor;  // Inicializando o atributo 'cor'
        this.ano = ano;  // Inicializando o atributo 'ano'
        this.marca = marca;  // Inicializando o atributo 'marca'
    }
}

let meuCarro = new Carro("Azul", 1988, "Fusca");

5. Voltando ao método construtor

  • Python: O método init é invocado automaticamente quando um objeto é instanciado. Ele recebe a referência do próprio objeto (self) como primeiro parâmetro e outros parâmetros para inicializar os atributos.

  • JavaScript: O método constructor funciona da mesma maneira, sendo chamado ao instanciar um objeto. O this é utilizado para referenciar o objeto atual e seus atributos.

Exemplos:

  • No exemplo Python, a linha meu_carro = Carro("Vermelho", 1968, "Fusca") chama o método init, que inicializa os atributos cor, ano, e marca para o objeto meu_carro.

  • No exemplo JavaScript, a linha let meuCarro = new Carro("Azul", 1988, "Fusca") chama o método constructor, que realiza a mesma inicialização dos atributos.

6. Memórias Stack e Heap

Este é um pequeno adendo só para explicar bem por cima, o que acontece por debaixo dos panos, quando criamos um objeto. Assim vamos voltar ao processo de criação de um objeto. Quando instanciamos a classe Carro para criar o objeto meu_carro, o que acontece por debaixo dos panos é o seguinte:

Veja o programa em python, abaixo:

class Carro:
    def __init__(self, cor, ano, marca):
        self.cor = cor  # Inicializando o atributo 'cor'
        self.ano = ano  # Inicializando o atributo 'ano'
        self.marca = marca  # Inicializando o atributo 'marca'

meu_carro = Carro("Vermelho", 1968, "Fusca")

A linha

meu_carro = Carro("Vermelho", 1968, "Fusca")

realiza os seguintes passos:

  1. Criação do Objeto:

    • Um objeto é criado na região de memória chamada heap.

    • Seus atributos são inicializados:

      • cor: "Vermelho"

      • ano: 1968

      • marca: "Fusca"

  2. Endereço na Heap:

    • A heap atribui um endereço para o objeto, por exemplo, 0x0005.

  3. Referência na Stack:

    • Existe uma segunda região da memória chamada stack, onde a variável meu_carro é armazenada.

    • Essa variável não contém o objeto diretamente, mas sim uma referência ao endereço 0x0005 da heap.

Portanto, o objeto meu_carro existe na heap, enquanto a stack armazena a referência para acessá-lo. Isso permite que o programa trabalhe de forma eficiente, manipulando objetos grandes sem duplicá-los em várias partes da memória.

7. Acessando métodos e atributos ou explicação do ponto (.)

O ponto (.) é o carácter usado dendro da sintaxe das linguagens Python e Javascript para acessar os atributos e métodos de um objeto.

  • Quando você usa o ponto seguido de um nome de atributo (como meu_carro.cor ou meuCarro.cor), está acessando o valor armazenado naquela propriedade.

  • Quando você usa o ponto seguido de um nome de método (como meu_carro.acelerar() ou meuCarro.acelerar()), está chamando a função ou ação que o objeto pode realizar.

O ponto é o operador de acesso a membros. Em termos simples, ele indica que você está se referindo a uma parte específica do objeto (seja um valor armazenado ou uma ação que o objeto pode realizar).

Veja os exemplos abaixo:

No Python, a sintaxe é assim:

class Carro:
    # Método construtor (init) que inicializa os atributos
    def __init__(self, cor, ano, marca):
        self.cor = cor # Inicializando o atributo 'cor'
        self.ano = ano # Inicializando o atributo 'ano'
        self.marca = marca # Inicializando o atributo 'marca'

    # Métodos para o carro
    def acelerar(self):
        print(f"O carro {self.marca} acelerou!")

    def frear(self):
        print(f"O carro {self.marca} freou!")

    def buzinar(self):
        print(f"O carro {self.marca} buzinou!")

# Criando um objeto da classe Carro
meu_carro = Carro("Vermelho", 1968, "Fusca")

# Imprimindo os atributos
print(meu_carro.cor)  # Acessando o atributo 'cor'
print(meu_carro.ano)  # Acessando o atributo 'ano'
print(meu_carro.marca)  # Acessando o atributo 'marca'

# Chamando os métodos
meu_carro.acelerar()
meu_carro.frear()
meu_carro.buzinar()

No Javascript, a sintaxe é assim:

class Carro {
    // Método construtor (constructor) que inicializa os atributos
    constructor(cor, ano, marca) {
        this.cor = cor; // Inicializando o atributo 'cor'
        this.ano = ano; // Inicializando o atributo 'ano'
        this.marca = marca; // Inicializando o atributo 'marca'
    }

    // Métodos para o carro
    acelerar() {
        console.log(`O carro ${this.marca} acelerou!`);
    }

    frear() {
        console.log(`O carro ${this.marca} freou!`);
    }

    buzinar() {
        console.log(`O carro ${this.marca} buzinou!`);
    }
}

// Criando um objeto da classe Carro
let meuCarro = new Carro("Azul", 1988, "Fusca");

// Imprimindo os atributos
console.log(meuCarro.cor);  // Acessando o atributo 'cor'
console.log(meuCarro.ano);  // Acessando o atributo 'ano'
console.log(meuCarro.marca);  // Acessando o atributo 'marca'

// Chamando os métodos
meuCarro.acelerar();
meuCarro.frear();
meuCarro.buzinar();

Agora, sabendo dessa característica do ponto na sintaxe, podemos compreender um outro detalhe importante.

Note que dentro do método construtor da sintaxe Javascript existe o this, assim como na sintaxe Python o self.

Essas nomenclaturas são uma convenção.

Veja que o this não aparece como parâmetro do método construtor no javascript: constructor(cor, ano, marca).

Já no python o self aparece como parâmetro do método construtor: def init(self, cor, ano, marca).

Isso são decisões de design da linguagem, a equipe que as projetou optou para que fossem assim.

O fato é que estes parâmetros são a referência explícita à instância da classe.

Vamos entender melhor.

Que tal criar quatro instâncias da classe Carro:

  • Ford Mustang - Vermelho, 2024.

  • Ford Ranger - Azul, 2023.

  • Ford EcoSport - Branco, 2022.

  • Ford Fusion - Preto, 2020.

Em python:

# Criando um objeto classe carro
# ou criando uma instância da classe Carro
ford_mustang = Carro("Vermelho", 2024, "Mustang")

ford_ranger = Carro("Azul", 2023, "Ranger")

Em javascript:

// Criando um objeto classe carro
// ou criando uma instância da classe Carro
let fordEcoSport = new Carro("Branco", 2022, "EcoSport");

let fordFusion = new Carro("Preto", 2020, "Fusion");

Note que todas as instâncias usam a classe Carro, então como faremos a distinção de que uma se refere a um Mustang e uma outra se refere a um EcoSport? Usando a referência explicita!

É como se estivéssemos dizendo: classe carro, você mesma (self) vai receber um Ford Mustang, vermelho, 2024. Por tanto toda vez que você precisar de algum parâmetro de você mesma (self), como cor, ano e marca, agora vão ter a cor vermelha, o ano 2024 e a marca Mustang. Também armazene o endereço para este processo que você fez na variável ford_mustang, pois todas as vezes que precisarmos de fazer um objeto Ford Mustang, vamos usar esta variável.

Mas se estivéssemos no Javascript, seria classe carro, você mesma (this) vai receber um Ford Fusion, Preto, 2020. Por tanto toda vez que você precisar de algum parâmetro de você mesma (this), como cor, ano e marca, agora vão ter a cor preta, o ano 2020 e a marca Fusion. Também armazene o endereço para este processo que você fez na variável fordFusion, pois todas as vezes que precisarmos de fazer um objeto Ford Fusion, vamos usar esta variável.

No Python, a sintaxe é assim:

class Carro:
    # Método construtor (init) que inicializa os atributos
    def __init__(self, cor, ano, marca):
        self.cor = cor # Inicializando o atributo 'cor'
        self.ano = ano # Inicializando o atributo 'ano'
        self.marca = marca # Inicializando o atributo 'marca'

No Javascript, a sintaxe é assim:

class Carro {
    // Método construtor (constructor) que inicializa os atributos
    constructor(cor, ano, marca) {
        this.cor = cor; // Inicializando o atributo 'cor'
        this.ano = ano; // Inicializando o atributo 'ano'
        this.marca = marca; // Inicializando o atributo 'marca'
}

8. Conclusão

Leia e releira este artigo quantas vezes for necessário. Execute o código e preste atenção nos comentário dentro do código.

Tente explicar para alguém os conceitos aprendidos.

Tente se explicar o vocabulário novo.

  1. O que é uma instância?

  2. O que é um método construtor?

  3. O que é um objeto?

  4. O que é abstração?

  5. Para que são usadas as classes?

  6. Como eu crio um objeto no Javascript?

  7. Para que é usado o self no Python?

  8. O que é inicializar um objeto ou uma variável?

  9. Tente abstrair uma caneta.

  10. Tente abstrair uma câmera.

  11. Tente abstrair um celular.

Jogue os conceitos no ChatGPT ou Claude e peça para que esses modelos de IA Generativa corrija suas respostas, esclareça alguns conceitos que ficaram obscuros utilizando IA Generativa.

Espero que este artigo te ajude a compreender o conceito inicial de Programação Orientada a Objeto, para quando você pegar um livro sobre o tema, a curva de aprendizado seja menos íngreme.

FIM ■