Encapsulamento


 

O trabalho de desenvolvimento de sistemas é composto por diversas etapas e uma das últimas está em programar o sistema que equivale a construir “fisicamente” o que foi analisado e modelado. Para isso temos alguns métodos diferentes, temos formas tanto para analisar quanto para programar, especificamente para programação ainda falamos muito em estruturada ou orientada a objetos.

Existem muitas diferenças entre as duas técnicas e uma muito importante é a capacidade que as linguagens orientadas a objetos tem de promover uma proteção de suas estruturas internas em relação a outros módulos do sistema, capacidade essa que não está presente nas linguagens estruturadas.

Em linguagens orientadas a objetos utilizamos classes para programar, ou seja, ao invés de simplesmente criar funções soltas, agrupamos as estruturas dentro de classes. Geralmente as classes representam conceitos do mundo tais como Pessoa, Funcionario, Aluno, Curso e assim por diante. Já na programação estruturada o máximo que conseguimos para expressar esse tipo de conceito são registros (structs no C) que NÃO tem:

  1. Comportamento associado – a capacidade de aplicar certas funções à própria referência, ou seja, considerando que uma variável que referencia um registro conter funções que sendo executadas já considerem que o destino de sua execução é a própria variável.
  2. Controle de visibilidade dos campos – a capacidade de determinar se uma função ou módulo externo ao registro pode ou não acessar diretamente um campo definido.

Sabendo que essas características não estão presentes nas linguagens estruturadas, podemos partir para entender qual o efeito disso.

O primeiro caso, a falta de associação entre funções e variáveis implica em ter que passar para uma função uma referência sobre a qual os processamentos serão aplicados. Do ponto de vista prático isso não é tão complicado de se fazer, porém significa que não existe uma relação real entre uma função e um registro, a função acessa quaisquer dados de qualquer registro basta passar como referência e pronto. Poderiamos utilizar dentro de um registro uma lista encadeada para guardar ponteiros para funções tentando assim simular a associação, mas como as chamadas são dinâmicas isso não relacionaria efetivamente as funções com os registros. Continuariamos precisando passar a referência.

O segundo caso é um pouco mais grave, isto porque, como não é possível restringir o acesso aos campos definidos em um registro, qualquer função em qualquer parte do sistema pode acessar qualquer um dos campos desde que tenha uma referência para um registro. O efeito disso é a criação de uma dependência, que chamamos de acoplamento, entre o módulo e o registro, isso não parece tão grave porém podemos usar um exemplo para mostrar como um sistema pode quebrar.

Vamos considerar um registro que tem um array como estrutura de dados para armazenar diversas informações e um outro módulo que usa esse array. Só que para usar o array é necessário ordená-lo e isso tem suas funções próprias, como o registro não tem uma forma de fornecer a ordenação porque não tem qualquer tipo de comportamento associado, cabe ao módulo externo prover a sua própria maneira de garantir a ordenação. Se em algum momento o criador do registro achar que precisa modificar essa estrutura para uma lista encadeada o módulo externo, que foi criado por outra pessoa, vai falhar porque sua rotina de ordenação trabalha com arrays e não com listas encadeadas.

O uso de linguagens orientadas a objetos ajuda a resolver esse tipo de problema. Ajuda, mas não garante, isto porque se o programador tiver hábitos ruins de programação pode burlar as facilidades oferecidas. A primeira coisa a ter em mente é que as linguagens orientadas a objetos fornecem um conjunto de conceitos de mais alto nível e que são válidos para seus compiladores. Isto quer dizer que fisicamente a execução de um programa não muda em nada, teoricamente se você criar uma rotina em linguagem de máquina para acessar um atributo “privado” de um programa feito em Java ou C++ isso pode ser feito. Então o conceito de atributo privado só é reconhecido pelo compilador da linguagem, que durante o processo de compilação impede que um módulo externo acesse um atributo declarado como privado. Este tipo de recurso garante que atributo só seja acessado pela própria classe, qual a vantagem disso?

Quando marcamos um atributo como privado, somente a própria classe pode manipular, isso significa que para acessar seu valor ou para informar um novo valor, os módulos externos precisam usar métodos fornecidos pela própria classe (ou seja comportamentos associados à classe.) Esses métodos normalmente recebem o nome de setXXX/getXXX onde XXX equivale ao nome do atributo que manipulam. Esses métodos, chamados de acessores, permitem atribuir um valor (set) ou recuperar o valor atual (get) do atributo, e podem executar quaisquer processamentos extra que seu criado julgar necessário tais como validação, verificação ou inicialização.

Este tipo de mecanismo promove o que chamamos de encapsulamento porque cria uma cápsula protetora ao redor da estrutura interna da classe. Assim se tiver uma array os módulos externos não devem receber uma referência para esse array, ao invés disso devemos fornecer métodos para recuperar um elemento do array, para atribuir um elemento, para saber o tamanho do conjunto e assim por diante. Dessa forma sabemos que aquela classe tem alguma estrutura de dados interna que representa uma coleção, mas não precisamos (nem devemos) saber qual é a estrutura. Se em algum momento o criador da classe julgar adequado mudar a estrutura interna sabe que pode fazer isso sem que quebre nenhuma dependência.

Leave a Reply