Antes da OOP, os membros da estrutura de dados foram deixados públicos?

45

Quando uma estrutura de dados (por exemplo, uma fila) é implementada usando uma linguagem OOP, alguns membros da estrutura de dados precisam ser privados (por exemplo, o número de itens na fila).

Uma fila também pode ser implementada em uma linguagem procedural usando um struct e um conjunto de funções que operam no struct . No entanto, em uma linguagem procedural, você não pode tornar os membros de um struct private. Os membros de uma estrutura de dados implementados em uma linguagem procedural foram deixados públicos ou houve algum truque para torná-los privados?

    
por Christopher 18.07.2018 / 22:36
fonte

8 respostas

141

OOP não inventou o encapsulamento e não é sinônimo de encapsulamento. Muitas linguagens OOP não possuem modificadores de acesso no estilo C ++ / Java. Muitas linguagens não-OOP têm várias técnicas disponíveis para oferecer encapsulamento.

Uma abordagem clássica para o encapsulamento é closures , conforme usado na programação funcional . Isso é significativamente mais antigo que o OOP, mas é de certa forma equivalente. Por exemplo. em JavaScript, podemos criar um objeto como este:

function Adder(x) {
  this.add = function add(y) {
    return x + y;
  }
}

var plus2 = new Adder(2);
plus2.add(7);  //=> 9

O objeto plus2 acima não possui um membro que permita acesso direto a x - ele é totalmente encapsulado. O método add() é um encerramento sobre a variável x .

A linguagem C oferece suporte a alguns tipos de encapsulamento por meio de seu mecanismo de arquivo de cabeçalho , particularmente a técnica ponteiro opaco . Em C, é possível declarar um nome de estrutura sem definir seus membros. Nesse ponto, nenhuma variável do tipo dessa estrutura pode ser usada, mas podemos usar ponteiros para essa estrutura livremente (porque o tamanho de um ponteiro de estrutura é conhecido em tempo de compilação). Por exemplo, considere este arquivo de cabeçalho:

#ifndef ADDER_H
#define ADDER_H

typedef struct AdderImpl *Adder;

Adder Adder_new(int x);
void Adder_free(Adder self);
int Adder_add(Adder self, int y);

#endif

Agora podemos escrever código que use essa interface do Adder sem ter acesso a seus campos, por exemplo:

Adder plus2 = Adder_new(2);
if (!plus2) abort();
printf("%d\n", Adder_add(plus2, 7));  /* => 9 */
Adder_free(plus2);

E aqui estariam os detalhes da implementação totalmente encapsulados:

#include "adder.h"

struct AdderImpl { int x; };

Adder Adder_new(int x) {
  Adder self = malloc(sizeof *self);
  if (!self) return NULL;
  self->x = x;
  return self;
}

void Adder_free(Adder self) {
  free(self);
}

int Adder_add(Adder self, int y) {
  return self->x + y;
}

Há também a classe de linguagens de programação modulares , que enfoca o nível de módulo interfaces. A família da linguagem ML incl. OCaml inclui uma abordagem interessante para módulos chamados functors . OOP ofuscou e em grande parte substituiu a programação modular, mas muitas vantagens da OOP são mais relacionadas à modularidade do que à orientação a objetos.

Há também a observação de que classes em linguagens OOP como C ++ ou Java geralmente não são usadas para objetos (no sentido de entidades que resolvem operações através de ligação tardia / envio dinâmico), mas apenas para > tipos de dados abstratos (onde definimos uma interface pública que oculta detalhes internos de implementação). O artigo Sobre Entendendo a Abstração de Dados, Revisitado (Cook, 2009) discute essa diferença em mais detalhes.

Mas, sim, muitos idiomas não possuem nenhum mecanismo de encapsulamento. Nestes idiomas, os membros da estrutura são deixados públicos. No máximo, haveria uma convenção de nomenclatura que desencorajasse o uso. Por exemplo. Eu acho que Pascal não tinha nenhum mecanismo de encapsulamento útil.

    
por 18.07.2018 / 23:16
fonte
31

Primeiro, ser procedural versus orientado a objetos não tem nada a ver com público versus privado. Muitas linguagens orientadas a objetos não têm noção de controle de acesso.

Em segundo lugar, em "C" - que a maioria das pessoas chamaria de procedimento, e não orientada a objetos, existem muitos truques que você pode usar para efetivamente tornar as coisas privadas. Um muito comum é usar ponteiros opacos (por exemplo, void *). Ou - você pode encaminhar declarar um objeto e simplesmente não defini-lo em um arquivo de cabeçalho.

foo.h:

struct queue;
struct queue* makeQueue();
void add2Queue(struct queue* q, int value);
...

foo.c:

struct queue {
    int* head;
    int* head;
};
struct queue* makeQueue() { .... }
void add2Queue(struct queue* q, int value) { ... }

Veja o SDK do Windows! Ele usa HANDLE e UINT_PTR, e coisas assim para serem identificadores genéricos para memória usados em APIs - efetivamente tornando as implementações privadas.

    
por 18.07.2018 / 23:14
fonte
13

"Tipos de dados opacos" era um conceito bem conhecido quando fiz meu curso de ciência da computação há 30 anos. Nós não cobrimos OOP, pois não era de uso comum na época e a "programação funcional" era considerada mais correta.

Modula-2 tinha apoio direto para eles, veja link .

Já foi explicado por Lewis Pringle como o forward declarando uma estrutura pode ser usada em C. Ao contrário do Módulo-2, uma função de fábrica tinha que ser fornecida para criar o objeto. ( Os métodos virtuais também eram fáceis de implementar em C tendo o primeiro membro de uma estrutura como um ponteiro para outra estrutura que continham ponteiros de função para os métodos.)

Muitas vezes também foi usada a convenção. Por exemplo, qualquer campo que comece com “_” não deve ser acessado fora do arquivo que pertence aos dados. Isso foi facilmente imposto pela criação de ferramentas de verificação personalizadas.

Todo projeto de grande escala em que trabalhei (antes de me mudar para C ++ e C #) tinha um sistema para impedir que dados "privados" fossem acessados pelo código errado. Foi apenas um pouco menos padronizado do que é agora.

    
por 19.07.2018 / 13:55
fonte
9

Note que existem muitos idiomas OO sem a capacidade interna de marcar membros como privados. Isso pode ser feito por convenção, sem a necessidade de o compilador impor a privacidade. Por exemplo, as pessoas muitas vezes prefixarão as variáveis privadas com um sublinhado.

Existem técnicas para dificultar o acesso a variáveis "privadas", sendo a mais comum a expressão do PIMPL . Isso coloca suas variáveis privadas em uma estrutura separada, com apenas um ponteiro alocado em seus arquivos de cabeçalho públicos. Isto significa uma desreferencia extra e um elenco para pegar qualquer variável privada, algo como ((private_impl)(obj->private))->actual_value , que fica chato, então na pratica raramente é usado.

    
por 18.07.2018 / 23:06
fonte
4

As estruturas de dados não tinham "membros", apenas campos de dados (supondo que fosse um tipo de registro). A visibilidade geralmente foi definida para todo o tipo. No entanto, isso pode não ser tão limitador quanto você pensa, porque as funções não faziam parte do registro.

Vamos voltar e ter um pouco de história aqui ...

O paradigma de programação dominante antes da OOP foi chamado de programação estruturada . O objetivo principal inicial era evitar o uso de instruções de salto não estruturadas ("goto" s). Este é um paradigma orientado ao fluxo de controle (enquanto o OOP é mais orientado a dados), mas ainda era uma extensão natural dele tentar manter os dados logicamente estruturados como o código.

Outro avanço da programação estruturada foi ocultação de informações , a ideia de que as implementações da estrutura do código (que provavelmente mudar com bastante frequência) deve ser mantido separado da interface (que idealmente não vai mudar tanto). Agora é um dogma, mas antigamente, muitas pessoas realmente consideravam melhor que cada desenvolvedor conhecesse os detalhes de todo o sistema, então isso já foi uma idéia controversa. A edição original do Mês do Homem Mítico de Brook argumentou contra a ocultação de informações.

Linguagens de programação posteriores projetadas explicitamente como boas Linguagens de Programação Estruturadas (por exemplo, Modula-2 e Ada) geralmente incluíam a ocultação de informações como um conceito fundamental, construído em torno de algum tipo de conceito de uma facilidade coesa de funções (e quaisquer tipos, constantes e objetos que podem ser necessários). Na Modula-2 eles eram chamados de "Módulos", em Ada "Pacotes". Muitas linguagens OOP modernas chamam o mesmo conceito de "namespaces". Esses namespaces eram a base organizacional do desenvolvimento nessas linguagens e, para a maioria das finalidades, podiam ser usadas de maneira semelhante às classes OOP (sem suporte real para herança, é claro).

Assim, em Modula-2 e Ada (83) você poderia declarar qualquer rotina, tipo, constante ou objeto em um namespace privado ou público, mas se você tivesse um tipo de registro, não havia maneira (fácil) de declarar algum registrar campos públicos e outros privados. Todo o seu registro é público ou não é.

    
por 19.07.2018 / 20:34
fonte
0

Em C, você já poderia passar ponteiros para tipos declarados, mas indefinidos, como outros disseram, restringindo o acesso a todos os campos.

Você também pode ter funções privadas e públicas em uma base de módulo para módulo. As funções declaradas estáticas no arquivo de origem não são visíveis para o exterior, mesmo se você tentar adivinhar seu nome. Da mesma forma, você pode ter variáveis globais estáticas no nível do arquivo, o que geralmente é uma prática ruim, mas permite o isolamento em uma base de módulo.

Provavelmente, é importante ressaltar que a restrição de acesso como uma convenção bem padronizada, em vez de uma construção reforçada por linguagem, funciona muito bem (consulte o Python). Além disso, restringir o acesso a campos de objetos sempre protegerá o programador quando houver necessidade de alterar o valor dos dados dentro de um objeto após a criação. Que já é um cheiro de código. Indiscutivelmente, C e, em particular, a palavra-chave const do C ++ para métodos e argumentos de função é uma ajuda muito maior para o programador do que a final do Java bastante pobre.

    
por 20.07.2018 / 11:44
fonte
0

Se a sua definição de Público for a capacidade de acessar a implementação e dados / propriedades por meio de seu próprio código a qualquer momento, a resposta é simplesmente: Sim . No entanto, foi abstraído por diversos meios - dependendo da linguagem.

Espero que isso tenha respondido sucintamente à sua pergunta.

    
por 20.07.2018 / 16:01
fonte
-1

Aqui está um contra-exemplo muito simples: em Java, interface s define objetos, mas class es não. Um class define um tipo de dados abstrato, não um objeto.

Ergo, sempre que você usa private em um class em Java, você tem um exemplo de uma estrutura de dados com membros privados que não é orientada a objetos.

    
por 19.07.2018 / 04:47
fonte