... Como isso nos força a criar getters e setters, que na prática geralmente são totalmente estranhos? Existe algum bom motivo de design de linguagem para que as interfaces na maioria dos idiomas (todos?) Não permitam que os campos de membros ajudem a atender às especificações da interface?
Na maioria dos idiomas existentes, os membros são tratados fundamentalmente de maneira diferente dos métodos, mas será que isso acontece? Em uma linguagem teórica, não poderíamos ter uma interface que (a) especificou um método zero-args como getter, mas aceitou um membro legível como implementador e (b) especificou um método de argumento único como setter, mas aceitou um campo de membro gravável como seu implementador, considerando um idioma que suportava a especificação de controle de acesso direto a variáveis quando elas são declaradas? Uma linguagem prototípica que permita herança múltipla (para abordar o ponto muito válido que alguns entrevistados fizeram) seria ideal para algo assim.
Por favor, informe-me se algo assim já existir.
fonte
Respostas:
Não por qualquer motivo teoricamente fundamentado. Eu acho que as razões pelas quais você não vê muitas vezes são práticas:
A primeira é como você lida com essas coisas em tempo de execução. Em Java ou C ++,
foo.x = 5
é fácil lidar com uma atribuição a um membro público do tipo primitivo, como : coisas5
em algum registro, descobrir onde na memória ox
membrofoo
vive e colocar o registro lá. Em um idioma como C #, em que as classes podem encapsular tarefas de interceptação para membros ou pré-calcular como esses membros avaliarão , o compilador não terá a prioriconhecimento sobre se a implementação de uma classe disponibilizada em tempo de execução usa esse recurso. Isso significa que todas as atribuições e avaliações de membros públicos devem ser adiadas para a implementação. * As operações que envolvem muito acesso a esses membros acabam sofrendo um impacto no desempenho porque estão constantemente fazendo chamadas de sub-rotina que podem ser tratadas mais rapidamente com regularidade. atribuições em linha. Deixar a avaliação e a tarefa separadas dos getters e setters dá ao desenvolvedor algum controle sobre como eles são tratados em uma determinada situação.O segundo é a idade. Com a possível exceção do F #, nenhum dos idiomas atualmente em uso muito amplo tem menos de uma década e, mesmo recentemente, os ciclos adicionais necessários para possibilitar a avaliação e a tarefa interceptadas podem não ter sido aceitáveis.
Terceiro é a inércia. Eu acho que, até certo ponto, ainda há algum estigma associado à atribuição direta a membros do público, mesmo que a linguagem permita que a classe os manipule e as pessoas ainda pensem em termos de melhoras e melhoras. Isso vai se desgastando com o tempo, assim como aconteceu com todas as pessoas que estavam com o nariz no ar sobre as pessoas que escreveram programas em idiomas de nível superior em vez de assembly.
* Existe uma desvantagem que vem com isso: uma linguagem tem que ficar muito complicada ao adiar avaliações / atribuições para a classe ou recusar em tempo de execução se o código que usa avaliações / atribuições em linha para uma determinada classe tenta se vincular a uma implementação que as intercepta . Pessoalmente, acho que a última é uma má ideia, porque força a recompilação de código que usa uma API inalterada.
fonte
Sim.
Os campos de membros são dados por instância. Os dados devem realmente existir em algum lugar da memória para cada instância - deve haver um endereço ou uma sequência de endereços reservados para ele. Em Java, .NET e muitas outras linguagens OO, esses dados vão para a pilha.
Métodos são dados por classe. Eles são indicadores de uma tabela. É assim que os métodos obtêm despacho virtual. Na verdade, existe apenas uma instância alocada, por classe.
Uma interface é essencialmente um tipo especial de classe que contém apenas métodos, sem dados de instância. Isso faz parte da definição de interface. Se contivesse algum dado, não seria mais uma interface, seria uma classe abstrata. Ambas as opções são boas, é claro, dependendo do tipo de padrão de design que você está tentando implementar. Mas se você incluísse dados da instância nas interfaces, removeria tudo o que as torna interfaces.
OK, então por que um método de interface não pode apontar para um campo de membro? Porque não há nada no nível da classe para apontar. Novamente, uma interface nada mais é que uma tabela de métodos. O compilador pode gerar apenas uma tabela e todos os métodos nele devem apontar para o mesmo endereço de memória. Portanto, é impossível para os métodos de interface apontar para os campos da classe, porque o campo da classe é um endereço diferente para cada instância.
Talvez o compilador possa aceitar isso de qualquer maneira e gerar um método getter para o qual a interface aponte - mas isso essencialmente se torna uma propriedade . Se você quer saber por que o Java não tem propriedades, bem ... essa é uma história muito longa e tediosa na qual eu prefiro não entrar aqui. Mas se você estiver interessado, você pode querer ter um olhar para outras linguagens OO que fazer implementar propriedades, como C # ou Delphi. A sintaxe é realmente fácil:
Sim, é só isso. Importante: Esses não são campos, a "barra int" da
Foo
classe é uma propriedade automática e o compilador está fazendo exatamente o que acabei de descrever acima: gerando automaticamente getters e setters (assim como o campo de membro de suporte).Então, para recapitular a resposta à sua pergunta:
fonte
isso é resultado da ocultação de informações, ou seja, esses membros não devem estar visíveis como campos, mas como propriedades que podem ou não ser armazenadas / armazenadas em cache localmente
um exemplo é o código de hash que muda quando o objeto é alterado, portanto, é melhor calculá-lo quando solicitado e não sempre que o objeto é alterado
Além disso, a maioria das linguagens é herança única + multi-implementos e permitir que os campos façam parte de interfaces coloca você em território de herança múltipla (só falta uma implementação padrão de funções), que é um campo minado de casos extremos para acertar
fonte
As variáveis de membro são a implementação da classe, não a interface. Você pode alterar a implementação; portanto, outras classes não devem ter permissão para se referir a essa implementação diretamente.
Considere uma classe com os seguintes métodos - sintaxe C ++, mas isso não é importante ...
Parece bastante óbvio que haverá uma variável membro chamada
mode
oum_mode
similar que armazena diretamente esse valor de modo, fazendo o que uma "propriedade" faria em alguns idiomas - mas isso não é necessariamente verdade. Há muitas maneiras diferentes de lidar com isso. Por exemplo, o método Set_Mode pode ...Feito isso, muitos outros métodos da classe de exemplo podem chamar métodos apropriados dessa classe interna por meio do ponteiro, obtendo um comportamento específico do modo sem precisar verificar qual é o modo atual.
O ponto aqui é menos que há mais de uma maneira possível de implementar esse tipo de coisa e muito mais que em algum momento você possa mudar de idéia. Talvez você tenha começado com uma variável simples, mas todas as instruções do switch para identificar o modo em todos os métodos estão sofrendo. Ou talvez você tenha começado com a coisa de apontador de instância, mas isso acabou sendo muito pesado para a sua situação.
Acontece que coisas assim podem acontecer para qualquer dado de membro. Pode ser necessário alterar as unidades nas quais um valor é armazenado de milhas para quilômetros, ou você pode achar que o seu enumerar de identificação exclusiva do caso não pode mais identificar exclusivamente todos os casos sem considerar algumas informações extras ou o que for.
Isso também pode acontecer para métodos - alguns são puramente para uso interno, dependem da implementação e devem ser privados. Mas muitos métodos fazem parte da interface e não precisam ser renomeados ou o que quer que seja, apenas porque a implementação interna da classe foi substituída.
De qualquer forma, se sua implementação for exposta, outro código começará inevitavelmente a depender dela, e você ficará preso a mantê-la. Se sua implementação estiver oculta de outro código, essa situação não poderá acontecer.
O bloqueio do acesso aos detalhes da implementação é chamado de "ocultação de dados".
fonte
Como outros observaram, uma interface descreve o que uma classe faz, não como ela faz. É essencialmente um contrato ao qual as classes herdadas devem estar em conformidade.
O que você descreve é um pouco diferente - uma classe que fornece alguma implementação, mas talvez não toda, como uma forma de reutilização. Elas são geralmente conhecidas como classes abstratas e são suportadas em várias linguagens (por exemplo, C ++ e Java). Essas classes não podem ser instanciadas por si mesmas, apenas podem ser herdadas por classes concretas (ou abstratas).
Novamente, como já foi observado, as classes abstratas tendem a ter maior valor em idiomas que suportam herança múltipla, pois podem ser usadas para fornecer partes de funcionalidade adicional a uma classe maior. Geralmente, isso é considerado um estilo ruim, pois geralmente um relacionamento de agregação ("tem-a") seria mais apropriado
fonte
As propriedades do Objective-C fornecem esse tipo de funcionalidade com seus acessadores de propriedades sintetizados. Uma propriedade Objective-C é essencialmente apenas uma promessa de que uma classe fornece acessadores, normalmente do formulário
foo
para o getter esetFoo:
para o setter. A declaração de propriedade especifica certos atributos dos acessadores relacionados ao gerenciamento de memória e aos comportamentos de encadeamento. Há também uma@synthesize
diretiva que diz ao compilador para gerar acessadores e até a variável de instância correspondente, para que você não precise se preocupar em escrever getters e setters ou declarar um ivar para cada propriedade. Há também algum açúcar sintático que faz com que o acesso à propriedade pareça acessar os campos de uma estrutura, mas na verdade resulta em chamar os acessadores de propriedade.O resultado disso é que posso declarar uma classe com propriedades:
e então posso escrever um código que parece acessar diretamente ivars, mas na verdade usa métodos de acessador:
Já foi dito que os acessadores permitem separar a interface da implementação, e o motivo que você deseja fazer é preservar sua capacidade de alterar a implementação no futuro sem afetar o código do cliente. A desvantagem é que você precisa ser disciplinado o suficiente para escrever e usar acessadores apenas para manter suas opções futuras em aberto. O Objective-C facilita a geração e o uso de acessadores, assim como o uso direto de ivars. De fato, como eles cuidam de grande parte do trabalho de gerenciamento de memória, o uso de propriedades é muito mais fácil do que acessar diretamente os ivars. E quando chegar a hora de alterar sua implementação, você sempre poderá fornecer seus próprios acessadores em vez de permitir que o compilador os gere para você.
fonte
"A maioria" dos idiomas? Quais idiomas você investigou? Em linguagens dinâmicas, não há interfaces. Existem apenas três ou quatro linguagens OO de tipo estatístico e com popularidade: Java, C #, Objective-C e C ++. C ++ não possui interfaces; em vez disso, usamos classes abstratas, e uma classe abstrata pode conter um membro de dados público. C # e Objective-C suportam propriedades, portanto, não há necessidade de getters e setters nesses idiomas. Isso deixa apenas o Java. Presumivelmente, o único motivo pelo qual o Java não suporta propriedades é que tem sido difícil introduzi-las de maneira compatível com versões anteriores.
fonte
Algumas das respostas mencionam 'ocultação de informações' e 'detalhes de implementação' como os métodos de interface da razão de preferência dos campos. Eu acho que o principal motivo é mais simples que isso.
As interfaces são um recurso de linguagem que permite trocar o comportamento, fazendo com que várias classes implementem uma interface. O comportamento de uma classe é definido por seus métodos. Os campos, por outro lado, refletem apenas o estado de um objeto.
Obviamente, os campos ou propriedades de uma classe podem fazer parte do comportamento, mas não são a parte importante do contrato definido pela interface; eles são apenas um resultado. É por isso que, por exemplo, o C # permite definir propriedades em uma interface e em Java você define um getter na interface. É aqui que um pouco de ocultação de informações entra em jogo, mas esse não é o ponto principal.
Como eu disse antes, as interfaces permitem que você troque o comportamento, mas vamos dar uma olhada na parte da troca. Uma interface permite alterar a implementação subjacente , criando uma nova classe de implementação. Portanto, ter uma interface composta por campos não faz muito sentido, porque você não pode mudar muito sobre a implementação de campos.
O único motivo pelo qual você deseja criar uma interface somente de campo é quando você quer dizer que alguma classe é um determinado tipo de objeto. Eu enfatizei a parte 'é uma', porque você deve usar herança em vez de interfaces.
Nota lateral: Eu sei que a composição preferida em relação à herança e composição geralmente só pode ser alcançada usando interfaces, mas o uso de interfaces apenas para 'marcar' uma classe com algumas propriedades também é ruim. A herança é excelente quando usada adequadamente, e desde que você se atenha ao Princípio da Responsabilidade Única, não deverá ter problemas.
Quero dizer, nunca trabalhei com uma biblioteca de controle de interface do usuário, por exemplo, que não usa herança. A única área em que as interfaces entram em jogo aqui é no provisionamento de dados, por exemplo,
ITableDataAdapter
para vincular dados a um controle de tabela. Esse é exatamente o tipo de coisa para a qual as interfaces se destinam, para ter um comportamento intercambiável .fonte
Em Java, o motivo é que as interfaces permitem apenas especificar métodos .
Portanto, se você quiser algo com um campo no contrato de interface, precisará fazê-lo como método getter e setter.
Consulte também esta pergunta: Quando os Getters e Setters são justificados
fonte
O objetivo de uma interface é especificar funções que você deve implementar para reivindicar o suporte a essa interface.
fonte
Os membros não são permitidos nas interfaces porque, então, esses idiomas teriam que lidar com o mal horrendo que é a herança múltipla.
fonte