Lá pelos idos dos anos 70 começava a surgir uma indústria de software de verdade, antes algo muito mais acadêmico e com os sistemas mais focados no hardware em si. Com o avanço do processamento nos microcomputadores, os desenvolvedores estavam se dando ao luxo de programar sistemas cada vez mais complexos, úteis e com melhor usabilidade para os usuários. Foi nessa época que por exemplo a interface gráfica e o mouse foram inventados, nos laboratórios da Xerox PARC. Também nessa época foi criada a Programação Orientada à Objetos.
Diferente do paradigma anteriormente mais utilizado, a Programação Procedural, onde nós tínhamos poucas abstrações e uma ênfase maior no algoritmo em si, na POO abstraímos todo o negócio do sistema a ser desenvolvido no que chamamos de objetos e toda a lógica gira em torno da criação e interação com e entre eles.
No artigo de hoje vamos estudar um pouco sobre este paradigma de programação, talvez o mais utilizado na atualidade, utilizando Javascript como ferramenta nos exemplos.
Se preferir, você pode assistir ao início do vídeo abaixo, que tem o mesmo conteúdo.
Vamos lá!
#1 – O que é um Objeto?
No conceito de POO (Programação Orientada à Objetos) um objeto é uma abstração em software de um elemento de negócio da sua aplicação. Por exemplo, imagine uma loja virtual. Os objetos seriam os produtos, as categorias, os clientes, os pedidos e muito mais. Essa é a primeira forma de pensar e mais óbvia, mas vamos nos aprofundando mais aos poucos.
Me refiro a esta primeira camada da modelagem orientada à objetos de um sistema como “primeira forma” pois todos estes exemplos são elementos que possuem características e comportamentos fáceis de mapear dentro da proposta da aplicação (loja virtual), pois eles são aparentes no sistema, é fácil percebermos os atributos (é como chamamos as características de um objeto) do produto como nome, descrição, fotos, preço, peso.
Abaixo um exemplo de objeto utilizando a linguagem JavaScript.
1 2 3 4 5 6 7 8 9 |
const produto1 = { nome: "Monitor LG 22\"", descricao: "Monitor bacana", fotos: ["http://1.jpg"], preco: 400, peso: 2000 } |
“Então objetos são elementos do sistema que possuem atributos?”
Em um primeiro nível, sim. Isso porque os atributos definem como um objeto é, mas não como ele se comportará dentro do sistema, motivo pelo qual devemos seguir avançando na análise.
#2 – Comportamentos de Objetos
Além dos atributos dos objetos, é comum que eles possuam comportamentos, que chamamos de métodos em POO. Isso porque os objetos não costumam ser meras “estruturas de dados”, mas sim “células” com vida própria e que juntas fazem o sistema funcionar. Assim, um produto poderia ter métodos para calcular subtotal baseado em quantidade comprada ou ainda o frete baseado em um CEP do cliente e seus atributos internos.
Abaixo um exemplo evoluindo nosso objeto anterior para que ele também possa ter comportamentos.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
const produto1 = { nome: "Monitor LG 22\"", descricao: "Monitor bacana", fotos: ["http://1.jpg"], preco: 400, peso: 2000, calcularSubtotal: (quantidade) => { return quantidade * preco; }, calcularFrete: (cep) => { //conexão com API da transportadora aqui } } |
Além dessa segunda forma ainda óbvia de mapear temos outras análises a serem feitas e que acabam gerando outros objetos e camadas na aplicação. Não vamos entrar em todas pois esta é uma introdução ao assunto, mas por exemplo, um objeto também pode ser um componente que agrupa ou interage com estes outros objetos citados. Por exemplo, no ecommerce, o componente de pagamento, o carrinho de compras, a gestão de frete…também são objetos!
Isso porque se parar para pensar, o frete por exemplo não depende apenas de um produto comprado, mas de TODOS produtos que o usuário colocou no pedido, o que faz com que esse tipo de regra de negócio fique melhor dentro um objeto de negócio para gestão do frete, que recebe os produtos, o CEP e faz o cálculo considerando o pacote completo.
Abaixo um exemplo de objeto gestor de frete que faz o cálculo considerando uma única entrega para um conjunto de produtos e o CEP do cliente.
1 2 3 4 5 6 7 8 9 |
const gerenciadorFrete = { cepEmpresa: "94000000", chaveApi: "123456", calcularFrete: (cep, produtos) => { //chama API da transportadora simulando um pacote com todos produtos } } |
Repare como este objeto possui também suas próprias características, como o CEP do depósito de onde partem os produtos e a chave de API da transportadora. Já o nosso objeto de produto não precisa mais ter o cálculo de frete nele, já que isso agora é responsabilidade do gestor de frete.
Ou seja, na prática, todos os elementos que possuem características e comportamentos dentro do sistema em questão e/ou interagem entre si podem ser modelados como objetos dentro deste paradigma.
Mas como garantir que estes objetos sigam as regras de atributos e métodos que definimos na análise OO?
Através de classes de objetos!
#3 – O que são classes de objetos?
Em POO nós classificamos os objetos conforme seus atributos e comportamentos em comum. Assim, todos objetos que se parecem com um produto, por exemplo, são agrupados como sendo da classe Produto. Essa classificação garante padronização entre os objetos, aumentando a coesão entre objetos de mesma classe, aumentando reuso de código, diminuindo bugs, garantindo a compatibilidade nas interações entre os objetos e facilitando a manutenção do sistema como um todo.
Peguemos o nosso objeto produto1 apresentado anteriormente. Se tivéssemos um produto2, teríamos que definir novamente suas características e funcionalidades, certo? Como abaixo.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
const produto2 = { nome: "Mouse Óptico", descricao: "Mouse RGB", fotos: ["http://2.jpg"], preco: 100, peso: 100, calcularSubtotal: (quantidade) => { return quantidade * preco; }, calcularFrete: (cep) => { //conexão com API da transportadora aqui } } |
Usando classes essa repetição toda não é necessária, pois nós especificamos uma vez a classe Produto e dizemos que, toda vez que quisermos um novo produto, que ele deve seguir todas as regras da classe Produto, apenas atribuindo novos valores aos atributos. Como no exemplo abaixo, onde eu crio um novo (new) objeto produto2 a partir da classe Produto.
1 2 3 |
const produto2 = new Produto("Mouse Óptico", ...);//incompleto, apenas ilustrativo |
Desta forma, podemos criar uma analogia que as classes funcionam como plantas de casas. Você não mora na planta, certo? Você usa a planta para construir casas. Da mesma forma a classe NÃO é um objeto, mas você usa ela para criar objetos seguindo um padrão.
Mas Luiz, eu poderia fazer isso apenas com os conhecimentos básicos de JS, usando por exemplo funções compartilhadas entre os objetos e módulos.
Sim, poderia, mas se você fizer isso tudo na mão (sem uso de classes) você estaria “reinventando a roda”, pois as classes justamente já fornecem essa mecânica “na caixa”. Claro, eu não mostrei ainda como criar essas ditas “classes de objetos” e neste artigo eu nem vou pois ficaria muito extenso. Além disso eu já ensino a criar classes em outros dois tutoriais aqui do blog:
Com esses tutoriais acima você aprende tudo que precisa saber para um rápido começo com classes em JS e TS, sendo que as características ue citei anteriormente como benefícios do uso de classes são muito potencializadas se você usar TS ao invés de JS, porque a definição de uma classe não tem efeitos muito diferentes do que um novo type.
#4 – Vantagens de Desvantagens
E por fim, para encerrarmos esta nossa rápida introdução à Programação Orientada à Objetos, vale ressaltarmos os prós e contras desta abordagem.
Como vantagens do que vimos aqui nós temos:
- a abstração do negócio para objetos permite discussões mais focados no negócio e no valor a ser entregue, inclusive entre profissionais técnicos e não-técnicos;
- a decomposição dos conceitos do domínio/negócio da aplicação em estruturas menores, tornam o problema geral mais fácil de entender;
- esta componentização aumenta também o reuso de código, facilitando a manutenção futura (produtividade maior no futuro);
- a padronização dos objetos em classes diminui a complexidade geral e aumenta a compatibilidade entre os diversos elementos da aplicação;
Mas como nem tudo são flores, temos como desvantagens da POO:
- a quantidade de classes, objetos e interações entre eles pode levar a uma curva maior de entendimento inicial do sistema;
- quando os objetos e classes são mal modelados, muitas vezes fica pior do que quando não usamos eles;
- a produtividade inicial é menor, pois se escreve mais código para chegar no mesmo resultado (prático);
- a performance geral de programas orientados a objetos é inferior a programas procedurais (nada a se preocupar em situações normais, apenas em ambientes extremamente limitados, como embarcados);
Assim, podemos concluir essa nossa introdução à POO por aqui. Espero que tenha conseguido entender os fundamentos e começar a aplicá-la em seus projetos.
Até a próxima!
Olá, tudo bem?
O que você achou deste conteúdo? Conte nos comentários.