Por que eu devo ler este artigo:Este artigo será útil para aprender a escrever código mais fácil de compreender e de modificar. O conceito de encapsulamento é explicado na teoria e na prática, com a ajuda de dois exemplos: um sistema de vendas e um jogo de ação em plataforma.

O código desses sistemas é analisado sob a ótica da orientação a objetos e são propostas melhorias. O artigo discute como as melhorias apresentadas podem facilitar o entendimento e a manutenção desses e de outros sistemas.

Com mais de 50 anos de história e muitos anos de maturidade, a orientação a objetos (OO) é um eficiente paradigma de programação. Código orientado a objetos tende a ser legível, claro, flexível quanto a mudanças e de fácil manutenção.

Apesar de ser uma eficiente solução, nem sempre ela é bem compreendida e aplicada. É comum encontrar getters e setters que violam encapsulamento e classes com excesso de responsabilidades. Quando um código aparentemente orientado a objetos não apresenta os benefícios esperados, é possível que os conceitos fundamentais tenham sido deixados de lado.

Este artigo parte de um conhecimento básico prévio do assunto e trata de um dos principais fundamentos da OO: o encapsulamento. São discutidos caminhos para o desenvolvimento de soluções verdadeiramente orientadas a objetos, que cumpram as metas de flexibilidade, clareza, legibilidade e manutenibilidade.

A programação orientada a objetos (POO) é familiar para muitos desenvolvedores da atualidade. Das linguagens de programação mais comuns hoje, grande parte oferece recursos desse paradigma. Algumas delas já são orientadas a objetos desde sua concepção, como Java, C# e Ruby.

Outras foram criadas como extensões de linguagens procedurais; é o caso de Objective-C, C++ e Object Pascal (usada no Delphi). Há aquelas que nasceram procedurais e, após alguns anos, passaram a oferecer OO como uma das alternativas de desenvolvimento, notadamente PHP.

Há, ainda, as que propõem uma orientação a objetos de maneira não tradicional, como Lua e JavaScript. Não importando qual for a linguagem usada, muitos desenvolvedores já estão acostumados a trabalhar com classes, objetos e métodos, além de sobrecarga, herança e polimorfismo.

Historicamente, o conceito de objetos como é conhecido hoje data do fim da década de 50 e início da década de 60. Na mesma época, também estava nascendo outra importante contribuição: a interface gráfica. Essas duas inovações, desde o início, caminharam juntas e impulsionaram o crescimento uma da outra.

Uma das tecnologias que mais ajudou a popularizá-las foi a linguagem de programação Smalltalk. Idealizada e desenvolvida por Alan Kay e sua equipe, na Xerox PARC, Smalltalk é uma linguagem puramente orientada a objetos, que foi publicada em 1980.

Ela foi responsável por levar a POO a conhecimento geral dos desenvolvedores. Tornou-se a primeira linguagem OO bem conhecida e influenciou praticamente todas as subsequentes (como C++, Java e C#). Além disso, impulsionou o desenvolvimento das interfaces gráficas e de muitos dos padrões de projeto conhecidos hoje, como o MVC (Model-View-Controller).

Assim, pode-se ter uma dimensão da importância dessa linguagem para o desenvolvimento da computação.

Mudança de paradigma

A POO surgiu como alternativa ao paradigma de programação procedural. Quando se fala desse paradigma, costuma-se tomar como referência a linguagem C. Pelo olhar dessa linguagem, um sistema é, essencialmente, um conjunto de funções que realizam tarefas em cima de estruturas de dados.

O foco está na sequência de passos a serem seguidos para se resolver um problema. Por exemplo: faça esta conta, peça esta entrada ao usuário, concatene estas duas strings, mostre esta informação na tela e assim por diante. Conceitualmente, um dos problemas desse paradigma é que ele está distante da maneira como os seres humanos enxergam o mundo.

Modelar os requisitos de um sistema pensando somente em funções e estruturas de dados pode ser uma tarefa difícil.

Outro problema típico desse paradigma é o uso extenso de variáveis globais, o que dificulta a manutenção e o reuso de código. Uma linguagem procedural normalmente induz o desenvolvedor a criar variáveis globais e compartilhar estado do sistema entre diferentes procedimentos e funções.

Dessa maneira, existirão pontos diferentes do código acessando e modificando os mesmos dados, já que eles são compartilhados. Isso acaba gerando um efeito indesejado: o alto acoplamento. Ao fazer uma modificação em uma parte do sistema, também é alterado o comportamento de outra parte. Isso pode introduzir falhas no sistema, caso o desenvolvedor não tenha conhecimento dos impactos de suas alterações.

Se tiver conhecimento dos impactos, precisará modificar as outras partes, também – o que, de qualquer maneira, é trabalho a mais.

Existem diversas questões que motivam o uso da orientação a objetos. Além de facilitar a modelagem dos problemas, ela simplifica a escrita de código com qualidade. Em contraste com o paradigma procedural, a OO promove reuso e manutenibilidade.

Grande parte do tempo de vida de um projeto de software é dedicado à sua manutenção. Novos requisitos surgem, requisitos antigos são alterados, necessidades do cliente mudam… Esse cenário é bem conhecido da maioria dos desenvolvedores. Para lidar com ele, é necessário preparar-se para as mudanças, que certamente virão.

Um código flexível quanto a mudanças é muito desejável, e é justamente o que se alcança ao se aplicar bem os conceitos da orientação a objetos.

Classes

A unidade de desenvolvimento em OO é a classe. Conceitualmente, uma classe possui atributos (que representam o estado do objeto) e operações (que especificam o comportamento do objeto). Atributos são os dados que pertencem a essa classe, similar às colunas de um registro de banco de dados.

Operações são os procedimentos e funções que manipulam esses dados. Na linguagem de modelagem UML, os termos usados são os mesmos: atributos e operações. Em Java, os atributos são chamados de campos e as operações são chamadas de métodos.

Conforme será visto ao longo do artigo, existem diversas características desejáveis em uma classe. Uma delas é que a classe possua uma responsabilidade bem definida, ou seja, faça poucas coisas. Outra é que ela seja sucinta, ou seja, tenha poucas linhas de código.

Outra característica desejável é que a classe ofereça um conjunto de operações adequado às necessidades de quem for usá-la. Para essas operações serem adequadas, é necessário que elas digam exatamente o que fazem e ocultem como exatamente fazem. É esse o conceito de encapsulamento, que será explicado melhor na próxima seção.

Uma boa aplicação da OO também costuma deixar o código mais simples e legível. Um código fácil de ler e compreender tem grandes chances de ser fácil de manter e de modificar. Mas é importante perceber que a orientação a objetos vai além de apenas facilitar o entendimento.

Como veremos, entender facilmente o que um código faz não necessariamente implica que ele esteja bem escrito. Às vezes, ele esconde falhas que talvez só sejam percebidas no futuro, quando o problema for mais grave. Em particular, no início de um projeto, o código costuma ser compreendido facilmente, mesmo quando não está bem escrito.

Aplicar a OO corretamente é essencial para se alcançar uma de suas principais metas: flexibilidade quanto a mudanças.

Encapsulamento

Como já foi dito, o encapsulamento é um dos conceitos fundamentais da orientação a objetos. Para motivar seu entendimento, analisemos algumas situações do mundo real.

Quando vamos a um restaurante à la carte, lemos o cardápio, fazemos um pedido ao garçom e, após algum tempo, o prato escolhido é levado pelo garçom à nossa mesa. Não precisamos nos preocupar com o procedimento de preparação daquele prato.

Só temos que fazer o pedido e efetuar o pagamento na saída. Não nos importa que tipo de panela, fogão e utensílios de cozinha foram usados na preparação, desde que o resultado final esteja como esperamos.

Quando queremos sacar dinheiro de um caixa eletrônico, inserimos nosso cartão, escolhemos a opção de saque, digitamos o valor desejado, entramos com nossa senha e o dinheiro é entregue. Não é necessário entender de mercado financeiro para realizarmos essa operação. Também não são necessários conhecimentos de eletrônica ou desenvolvimento de software para isso.

É fato que esses elementos são essenciais para que um banco funcione; no entanto, quando estamos na posição de clientes, só nos são exigidas quatro coisas: inserir o cartão, escolher a opção correta, digitar o valor e entrar com a senha.

O que acabamos de descrever foram serviços: o serviço de alimentação de um restaurante e o serviço de saque de um caixa eletrônico. Esses serviços estão disponíveis para qualquer pessoa usufruir, não sendo necessário possuir conhecimentos técnicos.

O usuário nem mesmo interfere no processo interno que levará ao resultado. Seria estranho se o garçom pedisse ao cliente que levasse suas próprias panelas para serem usadas pelo cozinheiro. Também não faria muito sentido se o caixa eletrônico, antes de liberar o saque para o cliente, solicitasse uma tomada de decisão a respeito das finanças do banco. Esses processos são internos aos serviços, não cabem ao usuário.

No contexto da programação orientada a objetos, encapsular significa oferecer ...

Quer ler esse conteúdo completo? Tenha acesso completo