Encapsulamento


O Encapsulamento é um dos pilares da Orientação a Objetos. Mas você sabe o que realmente significa esse conceito?

Unsplash image Photo by Erwan Hesry on Unsplash

Introdução

De acordo com o dicionário, Encapsulamento significa: colocar ou encerrar em cápsula; capsular.

Uma cápsula não pode — ou não deveria — ser quebrada. O mundo externo ao objeto não pode saber o que há em seu interior a menos que o objeto queira lhes dizer “com suas próprias palavras”, ou seja, através de métodos que retornam informações mas não necessariamente seu Estado.

O mundo externo só deve conhecer os métodos públicos de um objeto. Seus métodos públicos representam sua Interface para fazer o que ele deve fazer. Qualquer método ou atributo interno ao objeto é de propriedade dele e de mais ninguém.

Atributos e métodos privados estão encapsulados no objeto e nunca deveriam ser acessados externamente.

Essa é a teoria.

Encapsulamento pelo Código

Toda a Orientação a Objetos é sobre encapsulamento e envio de mensagens.

Infelizmente, ambos os conceitos não são implementados perfeitamente em Object Pascal ou em nenhuma outra linguagem mainstream do mercado como Java e C#.

E isso gera muitas dúvidas.

Vamos falar sobre Encapsulamento e tentar expandir nossas mentes além do código, além dos conceitos pré-concebidos e do básico.

O que é estar encapsulado? Essa pergunta parece tola, mas veremos que depende do ponto de vista do observador para respondê-la.

Por exemplo. Um atributo privado está encapsulado no objeto e não pode ser acessado pelo mundo externo, certo?

type
  TFoo = class
  private
     FValue: Integer;
  public
     function GetValue: Integer;
  end;

function TFoo.GetValue: Integer;
begin
  Result := FValue;
end;

procedure TMainForm.ExecButton(Sender: TObject);
var
  F: TFoo;
begin
  F := TFoo.Create;
  ShowMessage(F.GetValue.ToString);
  F.Free;
end;

No exemplo acima, o mundo externo representado pela instância de TMainForm consegue acessar o atributo privado da instância da classe TFoo?

Não, você diria.

O atributo privado está sendo acessado por um método público que o “protege”. Além disso, como o atributo é primitivo, ele não é passado por referência. Então, o mundo externo não pode nem mesmo alterá-lo.

Está correto.

Então, vamos alterar um pouco o código.

type
  TFoo = class
  private
     FValue: TDataValue;
  public
     function GetValue: TDataValue;
  end;

function TFoo.GetValue: TDataValue;
begin
  Result := FValue;
end;

procedure TMainForm.ExecButton(Sender: TObject);
var
  F: TFoo;
begin
  F := TFoo.Create;
  F.GetValue.Free;
  F.Free;
end;

Agora GetValue retorna uma instância de objeto.

É possível criar o objeto F, acessar (indiretamente) seu atributo privado e destruí-lo chamando Free, poisGetValue retornar a referência direta do atributo.

Ora, o atributo não é público! Não temos um setter para ele e, mesmo assim, o “mundo externo” consegue destruí-lo? Ele não deveria ser intocável externamente?

Vejamos agora:

type
  TFoo = class
  public
     Value: TDataValue;
  end;

Nessa versão a classe TFoo tem um atributo público Value.

Se o mundo externo acessar esse atributo, estaríamos quebrando o encapsulamento do objeto?

Na verdade, não.

Não há nada encapsulado no objeto e, portanto, por definição não há nenhuma quebra de encapsulamento!

Talvez a linguagem não devesse permitir que atributos fossem públicos, mas esse seria um debate para outro artigo.

Quer outro exemplo? Imagine que GetValue agora é privado. Imagine uma outra classe chamada TBar com essa definição:

type
  TDataValueFunc = function: TDataValue of object; 

  TBar = class
  private
     FFunc: TDataValueFunc;
  public
     constructor Create(Func: TDataValueFunc);
     function Data: TDataValue;
  end;

function TBar.Data: TDataValue;
begin
  Result := FFunc;
end;

E o (novo) construtor de TFoo seria assim:

constructor TFoo.Create;
begin
  FBar := TBar.Create(@GetValue);
  FBar.Data;
end;

Não estaria TBar.Data executando um código privado de TFoo, mesmo que indiretamente? Um objeto “acessando” um método privado de outro objeto. Seria essa uma quebra de encapsulamento?

O que eu estou tentando lhe mostrar é que não é apenas o código puramente técnico que irá definir um bom nível de encapsulamento no seu projeto.

É necessário que você pense em encapsulamento no nível de design do projeto e não apenas na implementação técnica das classes.

Conclusão

Vimos que a linguagem Object Pascal permite-nos acessar atributos e métodos privados, mesmo que indiretamente.

O mesmo pode ser feito em outras linguagens consideradas “mais” Orientadas a Objetos.

Então, discutir sobre encapsulamento no nível puramente técnico da linguagem é perda de tempo. Seria muito difícil — mas não impossível — implementar um código onde todos os atributos privados não fossem acessados, de alguma maneira, pelo mundo externo sem perder performance ou complicar o modelo de classes apenas para afirmar que os atributos estão protegidos e intocados.

No mundo real, sistemas devem ter boa performance e serem simples de manutenção.

É melhor quebrar algumas regras e deixar as coisas simples do que ser purista e não ter nenhum benefício real.

Finalmente, o conceito de Encapsulamento na Orientação Objetos é mais sobre como projetar a comunicação entre os objetos e módulos utilizando o mínimo necessário ao invés de se preocupar unicamente com a sintaxe utilizada.

Até logo.

Posts Relacionados

  • Memória Segura Utilizando Instâncias de Interfaces

  • Classes Mutáveis vs Objetos Imutáveis

  • Implementando Interfaces Utilizando Diferente Assinaturas de Métodos

  • Usando Paths ao invés de Diretivas de Compilação

  • Trabalhando com Exceções em Requisições HTTP

  • Tipo object Continua Vivo

  • Array de Objetos

  • Variáveis Locais Deveriam ter Nomes Curtos

  • Como Dividir e Organizar o Código em Formulários com Muitos Widgets

  • Pascal Deveria ser Modernizado?