Como a OOP evoluiu para incluir a noção de Propriedades

11

Eu vim de um background em C ++ e estou fazendo um C # no meu trabalho atual e acabei de ler muitas perguntas e respostas sobre qual é a diferença entre campos e propriedades públicas e todas as variações e encarnações disso pergunta básica (por exemplo, este post do SO e todas as perguntas relacionadas ). Todas essas questões são abordadas em termos de diferenças práticas que dão como certa a existência de um sistema de propriedades, mas acho que seria bom abordar esse assunto em termos do que os designers de todas as linguagens que decidiram apoiar propriedades no primeiro lugar estava pensando (confira a lista no artigo da Wikipedia aqui) Como o OOP evoluiu do C ++ / Java para se estender ao que o artigo da Wikipedia identifica de maneira interessante como um meio termo entre métodos e dados de membros:

"Ou seja, as propriedades são intermediárias entre o código do membro (métodos) e os dados do membro (variáveis ​​de instância) da classe, e as propriedades fornecem um nível mais alto de encapsulamento que os campos públicos".

O MSDN adiciona mais informações:

"Embora as propriedades sejam tecnicamente muito semelhantes aos métodos, elas são bastante diferentes em termos de cenários de uso. Elas devem ser vistas como campos inteligentes. Elas têm a sintaxe de chamada dos campos e a flexibilidade dos métodos".

Gostaria de saber como chegou a esse nível intermediário de encapsulamento que se mostrou útil para a programação em geral. Estou assumindo que esse conceito não estava presente na primeira encarnação de linguagens de programação que expressavam o paradigma OOP.

jxramos
fonte
8
O recurso de linguagem simples é apenas um açúcar sintático conveniente para métodos getter e setter. Se há razões mais profundas por trás da interpretação e terminologia, eu não sei.
Em uma rodada de especialistas em OO, o título da sua pergunta pode ser provocador e pode desviar o que você realmente estava perguntando. Não que eu seja um especialista em OO, sou apenas um "usuário de OO" por vários anos.
Doc Brown
É um detalhe sintático que sinto falta de passar de 'métodos sem parâmetros' em Python para C ++. BMI = bob.weight/sq(bob.height)lê melhor sem ()IMO.
OJFord
Eu acho que as propriedades estavam no Visual Basic original (não .net), como uma maneira de configurar o objeto Active X (COM). A grade de propriedades dessa ferramenta provavelmente teve algo a ver com a adoção de propriedades em idiomas. Observe que o Visual Basic original não era orientado a objetos de várias maneiras, mas tinha a capacidade de criar algo como classes.
precisa
Continuando: a janela de propriedades era uma maneira de configurar objetos sem escrever código. Foi chamado de desenvolvimento RAD. Este link contém uma imagem da janela de propriedades do VB6: microsoft.com/mspress/books/WW/sampchap/4068.aspx
Frank Hileman

Respostas:

5

É tudo sobre encapsulamento e o princípio de acesso uniforme.

Um objeto deve poder responder a uma mensagem retornando dados existentes ou executando um método, mas o remetente não deve saber qual é qual. Ou, se você vê isso do lado do remetente: o remetente deve poder acessar os dados existentes ou executar um método por meio de uma interface uniforme.

Existem várias maneiras de conseguir isso:

  • se livrar dos dados por completo, basta ter métodos (o Newspeak faz isso)

    • uma forma menos radical das opções acima: livrar-se dos dados na interface pública , ou seja, tornar os dados sempre privados, na interface pública, apenas expor métodos (Smalltalk, Ruby faz isso)
  • livrar-se de métodos completamente, basta ter dados (Self faz isso, "métodos" são apenas Methodobjetos atribuídos a variáveis ​​de instância, variáveis ​​de instância são pesquisadas com despacho virtual)

  • não faça distinção sintática ou semântica entre métodos e dados (Scala faz isso, acessar um campo é sintaticamente indistinguível de chamar um método sem uma lista de argumentos ( foo.bar), atribuir a um campo é sintaticamente indistinguível de chamar um método especialmente nomeado ( foo.bar = baz) é o mesmo como, por exemplo, foo.bar_=(baz)chamar um método nomeado foo_=e valores somente leitura podem ser substituídos ou implementados por um método sem lista de parâmetros (ou seja val foo) em uma superclasse pode ser substituída (ou abstract valimplementada) em uma subclasse com um método def foo)

Java, no entanto, não segue o Princípio de acesso uniforme. Em Java, é possível distinguir entre acessar dados e executar um método. foo.baré diferente de foo.bar(). A razão para isso é que os campos e métodos são semanticamente e sintaticamente distintos.

O C # tenta corrigir isso adicionando propriedades ao idioma, basicamente métodos que se parecem com campos. No entanto, as chamadas de método ainda parecem diferentes dos acessos a campos e propriedades. Agora, campos e propriedades têm acesso uniforme, mas os métodos ainda não.

Portanto, isso não resolve o problema: você não pode corrigir duas maneiras diferentes de acessar as coisas adicionando uma terceira maneira de acessar as coisas! Mesmo que essa terceira via pareça uma das outras duas, você ainda terá (pelo menos) duas maneiras diferentes. Você só pode corrigi-lo se livrando de todas as maneiras diferentes, exceto uma, ou se livrando das diferenças.

É perfeitamente bom ter uma linguagem com métodos, propriedades e campos, mas todos os três devem ter acesso uniforme.

Jörg W Mittag
fonte
1
Por que você (e outros) acha que a manutenção do Acesso Uniforme é tão importante? Com um método, você pode fornecer parâmetros para alterar o que ele faz ou age. Com um campo ou propriedade que você não tem essa capacidade, geralmente você apenas retorna dados (embora uma propriedade possa ser implementada como executando um método em vez de apenas expor um campo privado). Isso me parece intuitivo, talvez apenas porque eu estou acostumado, mas o que você ganharia em elegância ou produtividade ao impor um acesso uniforme? Você tem medo de vazar detalhes da implementação fora da classe?
Mike Suporta Monica
1
O UAP trata de reservar a liberdade de alterar os detalhes da implementação sem interromper a interface pública. O exemplo usual é adicionar log. Se eu oferecer acesso por meio de um método, posso alterar trivialmente as partes internas do método para gravar em um log. Com um campo público, não posso adicionar o log sem antes transformá-lo em um método, que quebra todo o código do cliente. Se não houver risco de alterar as alterações, o uso de métodos é obrigatório. As propriedades são o compromisso ideal porque são métodos completos, mas parecem campos. A adesão ao UAP melhora o encapsulamento e reduz o acoplamento rígido.
amon
Muitas respostas boas para essa pergunta, mas acho que essa acertou em cheio, retirando os aspectos mais fundamentais da natureza das linguagens de programação e elaborando sobre o conceito formal relevante da UAP. Solid
jxramos
18

Bem, não tenho 100% de certeza, mas acho que as coisas provavelmente são mais simples do que você espera. Nas escolas de modelagem OO dos anos 90, havia uma demanda por classes de modelagem com atributos de membro encapsulados e, quando implementado em linguagens como C ++ ou Java, isso geralmente leva ao código com muitos getters e setters, portanto, muito "ruído" código para um requisito relativamente simples. Observe que a maioria (provavelmente todas, não verificou isso) dos idiomas listados no artigo vinculado da Wikipedia começou a introduzir "propriedades" de objetos no final dos anos 90 ou posterior.

Eu acho que essa foi a principal razão pela qual os designers de linguagem decidiram adicionar um pouco de açúcar sintático para reduzir esse ruído. E, embora as propriedades certamente não sejam um "conceito essencial de OO", elas pelo menos têm algo a ver com orientação a objetos. Eles não mostram "uma evolução do OOP" (como o título da sua pergunta assume), mas suportam a modelagem de OO no nível da linguagem de programação para facilitar a implementação.

Doc Brown
fonte
Não tem nada a ver com programação orientada a objetos, exceto no sentido de que uma propriedade é uma abstração de um campo e os campos fazem parte da programação orientada a objetos. Mas as propriedades não são necessárias para a programação orientada a objetos, como você nota corretamente.
Frank Hileman
2
@FrankHileman: "uma propriedade é uma abstração de um campo, e os campos fazem parte da programação orientada a objetos" - bem, parece-me que você concorda que o conceito realmente tem pelo menos algo a ver com OOP. E foi por isso que você me rebaixou?
Doc Brown
3
ri muito. Você pode culpá-lo? Ele escorregou da privada e bateu a cabeça na pia. Enfim ... O jeito que eu sempre vi foi que as propriedades não são estritamente sobre OO. Eles ajudam no encapsulamento e o encapsulamento ajuda no OO.
MetaFight
1
Ha! Esta é a introdução de programmers.stackexchange para mim. Lol, muito mais caloroso do que a minha experiência equivalente no stackoverflow simples :-p Boa maneira de misturar o dia!
Jxramos
11

Você tem isso ao contrário (mais ou menos). O Cálculo Lambda existe como praticamente a base formal para linguagens de programação e existe há décadas. Não possui campos.

Para modelar o estado mutável, é necessário fazer duas abstrações. Um representa a configuração de algum estado, e outro que recupera esse estado (capítulo 13 na minha versão do TaPL para referência). Soa familiar? Do ponto de vista teórico , o OO não evoluiu para ter esse material. OO leu Linguagens de Programação 101 e deu um passo à frente.

De uma perspectiva prática , existem duas motivações bastante claras. Você vem de um background em C ++, então o que precisaria acontecer se você tivesse um campo público - digamos ... o texto em uma caixa de texto. O que acontece quando você deseja alterar seu design para que "sempre que essa caixa de texto for alterada, faça blá"? Você pode matar esse campo, criar uma função ou duas e conectar essa lógica, pois não pode confiar nos desenvolvedores para chamar "UpdateTextbox". Essa é uma alteração muito marcante na sua API (e infelizmente ainda é uma alteração marcante na implementação de propriedades do .NET). Esse tipo de comportamento está em todo lugar na API do Windows. Como isso é um grande negócio na Microsoft, o C # provavelmente queria tornar isso menos doloroso.

A outra grande motivação é o Java Beans e seus parentes. Várias estruturas foram construídas para usar a reflexão Java para procurar GetXe SetXemparelhar e tratá-las efetivamente como propriedades modernas. Mas, como não eram construções reais de linguagem, essas estruturas eram frágeis e pouco controladas. Se você digitasse um nome, as coisas simplesmente quebrariam silenciosamente. Se alguém fosse refatorado, nada mudaria para o outro lado da propriedade. E fazer todo o campo / get / set clichê foi detalhado e tedioso (mesmo para Java!). Como o C # foi desenvolvido amplamente como "Java com lições aprendidas", esse tipo de dor foi uma dessas lições.

Mas o mais importante é que o conceito de propriedade foi bem-sucedido. É fácil de entender, é fácil de usar. Isso ajuda muito na adoção e, como ferramenta , os programadores descobriram que as propriedades resolvem um subconjunto de problemas de maneira mais limpa do que funções ou campos.

Telastyn
fonte
7
Eu votei contra você principalmente por muita referência a porcaria formal irrelevante. As implementações reais de linguagens de programação têm coisas muito mais sérias a considerar do que se o modelo foi incluído ou não no cálculo lambda.
DeadMG
9
@DeadMG - isso certamente é verdade, mas, por outro lado, os implementadores de linguagens de programação invariavelmente se familiarizarão com essa porcaria formal irrelevante . Pensar que isso não impactou seus projetos é ingênuo.
Telastyn
3
Definitivamente, isso não é "uma porcaria formal irrelevante". No entanto, o cálculo lambda pode não ter sido considerado muito para as linguagens de programação iniciais. Nesse caso, as CPUs provavelmente seriam mais orientadas para essa mentalidade.
precisa
3
@FrankHileman - Tenho certeza Lisp considerou ...
Telastyn
4
@Telastyn - sim - e Lisp não deveria ser uma linguagem de programação originalmente, lembra?
precisa
3

No .net especificamente, as propriedades se originam dos dias antigos do Visual Basic, que, por acaso, não eram orientados a objetos da maneira como pensamos hoje. Ele foi amplamente construído em torno do então novo sistema COM, que tratava superficialmente tudo, não necessariamente como classes, mas em termos de componentes que expunham propriedades que poderiam ser acessadas tanto no código quanto nos editores gráficos. Quando o VB e o C # recém-criado foram mesclados no .net, o VB ganhou vários recursos de OOP e manteve propriedades, pois removê-los seria um passo atrás - imagine se a ferramenta de atualização automática de código que eles tinham no Visual Studio tivesse sido usada. substituindo todas as suas propriedades por getters e setters e estava quebrando a compatibilidade com todas as bibliotecas COM. Seria lógico apoiá-los em todos.

niwax
fonte
Eu não acho que você deva ignorar a ancestralidade do C # que veio do Delphi e, antes disso, Object Pascal e Turbo Pascal with Objects. Elas eram orientadas a objetos e tinham propriedades (embora eu não me lembre se o OP tinha propriedades do TP5.5 ou se foram adicionadas; presumivelmente, elas foram continuadas em C # por Anders porque eram pragmaticamente uma boa idéia.
amaca
Muito interessante que você foi o único a abordar o aspecto componente desta pergunta. Acabei de adicionar um comentário à minha pergunta com um link para um site que listava propriedades como parte de algum modelo de propriedade-método-evento que o C # satisfaz. Em seguida, pesquisei novamente na página da minha pergunta por "componente", alguns desembarques falsos positivos, mas acho que sua resposta se sobrepõe ao que eu vinculei.
jxramos
3

Propriedades não têm nada a ver com programação orientada a objetos, porque propriedades são apenas açúcar sintático. As propriedades parecem superficialmente campos e, no mundo .net, é recomendável que elas se comportem como campos de alguma maneira, mas não são campos em nenhum sentido. As propriedades são açúcar sintático para um ou dois métodos: um para obter um valor e outro para definir um valor. O método definido ou o método get podem ser omitidos, mas não os dois. Pode não haver campo armazenando o valor retornado pelo método get. Como eles compartilham uma sintaxe com os campos e porque costumam usar campos, as pessoas associam propriedades aos campos.

As propriedades têm vantagens sobre os campos:

  • Em um método de conjunto de propriedades, antes que um valor de propriedade seja armazenado, o novo valor ou o estado do objeto pode ser verificado em relação a um conjunto de pré-condições ou invariantes.
  • Um método de obtenção de propriedade pode existir por conveniência, se a recuperação do valor da propriedade puder ser logicamente considerada equivalente à recuperação de um valor de campo.
  • Um método de conjunto de propriedades pode executar outras operações, como notificar um objeto pai ou ouvir objetos de uma alteração em um valor de propriedade.

Como as propriedades são a abstração de campos e, por conveniência sintática, idiomas como C # adotaram a sintaxe de campo para propriedades.

Frank Hileman
fonte
2
Primeira linha do artigo da Wikipedia. "Uma propriedade, em algumas linguagens de programação orientadas a objetos, é um tipo especial de membro da classe ...". Portanto, sua primeira frase está completamente errada. E a parte restante não responde à pergunta.
Doc Brown
1
@ DocBrown: Resposta interessante. Muitos artigos da Wikipedia estão claramente incorretos. Presumo que você esteja defendendo o autor deste artigo específico?
precisa
1
Nem todos os recursos das linguagens de programação orientadas a objetos estão relacionados aos conceitos orientados a objetos.
precisa
1
Isso não muda o fato de você não estar respondendo à pergunta.
Doc Brown
3
Note-se que não é apenas o levantador que é opcional. Você pode ter apenas propriedades do configurador.
Telastyn
2

É uma questão de implementação versus implicação . As propriedades estavam no OOP antes de C ++ ou Java entrarem em cena (elas estavam lá, com alguma rugosidade nas bordas, no Simula, e são fundamentais para o Smalltalk). Entidades com propriedades são conceitualmente diferentes dos valores com código anexado. Os prefixos get & set em algumas convenções de idiomas servem apenas para enlamear as águas; eles informam a diferença entre campos e propriedades, supondo que os campos possam ser acessados ​​diretamente sem obter / definir de uma maneira idiomática para o idioma e com vazamento.

O objetivo principal do OOP é tratar as coisas como se fossem entidades no mundo "real", não apenas como estruturas com algum código misturado. Outro programador deve precisar saber muito, muito pouco, sobre como implementei as coisas, e não deve se preocupar com quais dos vários valores que eles têm permissão para obter e / ou definir são reais e virtuais. Se você encontrar um vetor meu, não precisará saber se estou armazenando ângulo e magnitude ou componentes reais e imaginários internos ao objeto vetorial. Se eu alterar a representação na V2.0 da minha biblioteca, isso não deve afetar o seu código (embora você possa aproveitar os novos recursos interessantes). Da mesma forma, existem propriedades que uma entidade pode ter que dependem de dados externos à entidade, mas que são indubitavelmente propriedades do ponto de vista lexical. Você pergunta às pessoas "quantos anos você tem", e não "faça o cálculo que me revelará sua idade", mesmo sabendo que os dados disponíveis para esse "objeto" são a data de nascimento (um membro imutável privado) e a data de hoje date (uma propriedade ambiental pública com incremento automático, dependente do fuso horário, horário de verão e Linha Internacional de Data). A idade é uma propriedade, não um método, mesmo que seja necessário algum cálculo para chegar lá e não possa (exceto em representações de brinquedos de coisas com vida útil limitada artificialmente) ser armazenada como um campo. mesmo que você saiba que os dados disponíveis para esse "objeto" são a data de nascimento (um membro imutável privado) e a data de hoje (uma propriedade ambiental pública com incremento automático, dependente do fuso horário, horário de verão e Linha Internacional de Data ) A idade é uma propriedade, não um método, mesmo que seja necessário algum cálculo para chegar lá e não possa (exceto em representações de brinquedos de coisas com vida útil limitada artificialmente) ser armazenada como um campo. mesmo que você saiba que os dados disponíveis para esse "objeto" são a data de nascimento (um membro imutável privado) e a data de hoje (uma propriedade ambiental pública com incremento automático, dependente do fuso horário, horário de verão e Linha Internacional de Data ) A idade é uma propriedade, não um método, mesmo que seja necessário algum cálculo para chegar lá e não possa (exceto em representações de brinquedos de coisas com vida útil limitada artificialmente) ser armazenada como um campo.

Em vez de pensar em propriedades como filho bastardo de campos e métodos, é muito mais gratificante considerar os métodos como um tipo especializado de propriedade - coisas que suas entidades podem fazer em vez de coisas que são. Caso contrário, você não está lidando conceitualmente com objetos / entidades, mas com coleções de dados que possuem código anexado a elas. As implementações podem ser idênticas, mas as implicações são diferentes.

Não é necessário dizer, no entanto, que essa abstração tem um custo. Se um programador que usa uma classe não pode dizer se está acessando dados como estão armazenados ou obtendo / configurando valores que precisam ser calculados, haverá um nível em que o idioma também é necessariamente incerto (e, portanto, pode requer que tudo exija código para intermediar entre acessadores / seletores e valores). Não há nada conceitualmente errado com "estruturas com código" - elas certamente podem ser muito mais eficientes - mas vazam a implementação por todo o lado, e essa é uma das coisas que o OOP deve eliminar.

Stan Rogers
fonte
Concordo com alguns pontos e discordo de outros, mas a mensagem geral é boa: algumas informações sobre um objeto precisam ser calculadas, mas ainda são tecnicamente informações que são, e não uma ação que pode ser executada .
Pharap
0

Absolutamente nada. Propriedades e POO não têm nada a ver uma com a outra. As propriedades nada mais são do que o açúcar sintático para chamadas de função e, portanto, têm exatamente o mesmo anexo de OOP que as chamadas de função fazem - ou seja, nenhuma.

A propósito, a Wikipedia está completamente incorreta. O padrão getMember / setMember que você vê em Java oferece exatamente as mesmas vantagens (e desvantagens) das propriedades em C #. E você pode replicar esse padrão mesmo em C, se desejar.

As propriedades em C # nada mais são do que açúcar sintático suportado por idioma.

DeadMG
fonte
3
Você já viu o conceito de propriedade em qualquer linguagem de programação fora de um contexto de classe ou objeto? Eu não tenho.
Doc Brown
1
@ DocBrown: eu implementei. O fato é que as propriedades são alvos de baixo valor para os autores de idiomas, pois sua melhoria em relação às chamadas de funções regulares é baixa.
DeadMG
1
OP está perguntando sobre a história das propriedades como um conceito em linguagens de programação populares. E, na verdade, no sentido histórico, há uma conexão entre propriedades e POO.
Doc Brown
2
Talvez eu tenha sido um pouco imprudente ao atribuir esse conceito de propriedades ao paradigma OOP per se, mas como um conceito certamente transcende as linguagens individuais e seus recursos. Talvez eu esteja lutando para articular a ontologia apropriada entre os dois. Não estou sugerindo, através do título da pergunta, que propriedades são uma característica / conceito fundamental de OOP que cria ou quebra o formalismo de OOP, e, no entanto, sinto que elas só têm uma realidade nesse espaço de OOP, e foi por isso que fiz a pergunta como tal. .
Jxramos
4
Eles não são apenas açúcar de sintaxe em C #, eles existem nos metadados do assembly como membros distintos e você pode fazer coisas com propriedades que não pode com campos, como a ligação de dados.
Andy