[ Conteúdo ] Programação orientado a objetos ( javaScript )
Introdução:
Hoje trago a vocês um pouco sobre o que é e como funciona Programação orientada a objetos (OOP). Os exemplos que eu vou utilizar não vão ser necessariamente em javaScript e sim pseudocodigo, o mesmo da documentação do MDN. Mas sempre tendo em mente que o foco é javaScript.
O que é programação orientada a objetos?
O que é exatamente, imagino que a descussão se extenda bastante... O que posso afirmar com toda a certeza é que programação orientada a objetos é um paradigma fundamental em diversas linguagens incluindo C++ e Java. Não vou me aprofundar muito sobre tudo que envolve orientação a objeto, que é muito extenso. A ideia é uma visão geral da coisa.
Podemos dividir OOP em 4 partes: Classes, Instância, herança e encapsulação. Vou descrever todos esses conceitos de forma geral logo adiante.
OOP em javaScript é diferente do modelo mais clássico. Segundo a documentação é mais fléxivel e poderoso... Comentem
Classes e Instâncias
Quando vamos modelar um problema em termos de orientação a objeto, devemos abstrair as definições em diversos pedaços e ir montando o nosso sistema. Podemos representar ele de diversos tipos que queremos em nosso nosso sistema.
Vamos supor que estamos em uma escola, e queremos representa-la utilizando o paradigma de OOP. Então, em uma escola temos professores, que podem avaliar alunos e também tem um nome. Além disso ele pode se apresentar e obviamente ele é professor de alguma matéria, então teria que adicionar mais uma propriedade. Representado em pseudocodigo ficaria assim:
class Professor
properties
name
teaches
methods
grade(paper)
introduceSelf()
Onde temos name
e teaches
como uma propriedade, e temos dois métodos que são grade()
e introduceSelf()
A classe por si só não faz nada sozinha. Se faz necessário um construtor e instância-la para podemos fazer algo com esta classe desta forma:
class Professor
properties
name
teaches
constructor
Professor(name, teaches)
methods
grade(paper)
introduceSelf(
Geralmente o nome do construtor é o mesmo da classe. O construtor leva dois parâmetro, ambos são propriedades, pois queremos que vá mudando para cada professor. Os métodos são funções que vão fazer algo com base nas propriedades (geralmente), portanto quase nunca mexe-mos nisso.
Vamos instânciar o construtor:
walsh = new Professor("Walsh", "Psychology");
lillian = new Professor("Lillian", "Poetry");
walsh.teaches; // 'Psychology'
walsh.introduceSelf(); // 'My name is Professor Walsh and I will be your Psychology professor.'
lillian.teaches; // 'Poetry'
lillian.introduceSelf(); // 'My name is Professor Lillian and I will be your Poetry professor.'
É importante notar que se você utiliza javaScript, já deve ter ouvido falar de prototype... É um mecanismo que vou explicar em uma página diferente, mas esteja ciente que essa é uma diferença fundamental do javaScript para outros linguagens baseadas em OOP.
No código acima, estamos criando duas instâncias da classe professor
Herança
Em uma escola não tem somente professores, mas tem alunos também. E poderiamos criar uma outro classe dessa forma para estudantes:
class Student
properties
name
year
constructor
Student(name, year)
methods
introduceSelf
Mas, alguma desses propriedades são as mesmas que tem na classe professor
, vamos repeti-las...? A resposta é: definitavamente não! É gasto de memória atoa. Podemos fazer que uma classe herde propriedades de outra classe para evitar essa repetição.
Se abstrairmos ainda mais o conceito de escola, professores e alunos são pessoas, então podemos criar uma clase chamada Person
e passar propriedades que são comuns para Studant
e Professor
.
class Person
properties
name
constructor
Person(name)
methods
introduceSelf()
class Professor : extends Person
properties
teaches
constructor
Professor(name, teaches)
methods
grade(paper)
introduceSelf()
class Student : extends Person
properties
year
constructor
Student(name, year)
methods
introduceSelf()
Note que Professor
e Studant
estão herdando a propriedade name
de Person
. Pense em Person
como um pai e que Professor
e Studant
são filhos de Person
Assim, declaramos name
apenas uma vez evitando repetir uma mesma propriedade que faz a mesma coisa.
Note que os métodos são repetidos. Isso é porque os três querem se apresentar, mas de maneiras diferentes.
walsh = new Professor("Walsh", "Psychology");
walsh.introduceSelf(); // 'My name is Professor Walsh and I will be your Psychology professor.'
summers = new Student("Summers", 1);
summers.introduceSelf(); // 'My name is Summers and I'm in the first year.'
Podemos implementar um valor padrão para Person
pratt = new Person("Pratt");
pratt.introduceSelf(); // 'My name is Pratt.'
Está característica de um mesmo método ter o mesmo nome, mas diferentes implementações, se chama polimorfismo.
Encapsulação:
Encapsulação é uma característica muito útil para controlar o que é disponível para outros códigos ou não. Objetos prover uma interface para outros código que querem usar, eles, mas mantendo seu próprio estado interno. O objeto tem um estado interno mantido privado, o que quer dizer que nenhum outro objeto pode acessar seus métodos.
Isso é útil quando queremos alterar uma implementação interna sem ter que procurar e atualizar todas as propriedade em código. É como um firewall entre objetos para o resto do sistema.
Vamos supor que queremos filtrar os alunos, queremos que apenas alunos que esteja estejam acima do primeiro ano do ensino médio, poderiamos fazer algo assim:
if (student.year > 1) {
// allow the student into the class
}
O problema aqui é que se quisermos mudar isso depois? E se quisermos adicionar mais condições a longo prazo? Ou se quiser tomar várias outras ações com base nisso? Teríamos que alterar tudo manualmente para cada condição que utilize esse trecho de código.
Para contornar isso podemos utilizar um método que implementa uma lógica para isso:
class Student : extends Person
properties
year
constructor
Student(name, year)
methods
introduceSelf()
canStudyArchery() { return this.year > 1 }
if (student.canStudyArchery()) {
// allow the student into the class
}
Agora se quisermos mudar, apenas mudamos a lógica do método, que vai impactar em todas as condionais que utilizam essa lógica, alías, vai impactar tudo que utilize essa mesma lógica!
Isso é uma das possíbilidades de encapsulação, mas não é a única. Aqui vou mostrar que em algumas linguagens é permitido deixar propriedades privadas impedindo outros objetos utilizarem essa propriedade.
class Student : extends Person
properties
private year
constructor
Student(name, year)
methods
introduceSelf()
canStudyArchery() { return this.year > 1 }
student = new Student('Weber', 1)
student.year // error: 'year' is a private property of Student
Conclusão
ISso foi o básico do básico por trás de OOP. Se cometi algum eventual erro, por favor, me corrijam!.