Podemos expor interfaces em Ruby como fazemos em java e aplicar os módulos ou classes Ruby para implementar os métodos definidos por interface.
Uma maneira é usar a herança e o método_missing para obter o mesmo, mas existe alguma outra abordagem mais apropriada disponível?
Respostas:
Ruby tem interfaces como qualquer outra linguagem.
Observe que você deve ter cuidado para não confundir o conceito de Interface , que é uma especificação abstrata das responsabilidades, garantias e protocolos de uma unidade com o conceito de
interface
que é uma palavra-chave na programação Java, C # e VB.NET línguas. Em Ruby, usamos o primeiro o tempo todo, mas o último simplesmente não existe.É muito importante distinguir os dois. O que é importante é a interface , não o
interface
. O nãointerface
diz nada de útil. Nada demonstra isso melhor do que as interfaces de marcador em Java, que são interfaces que não possuem nenhum membro: basta dar uma olhada emjava.io.Serializable
ejava.lang.Cloneable
; esses doisinterface
significam coisas muito diferentes, embora tenham exatamente a mesma assinatura.Então, se dois
interface
s que significam coisas diferentes têm a mesma assinatura, o que exatamente ointerface
par está garantindo a você?Outro bom exemplo:
Qual é a interface de
java.util.List<E>.add
?element
está na coleçãoE qual desses realmente aparece no
interface
? Nenhum! Não há nada nointerface
que diz que oAdd
método deve mesmo adicionar , ele também pode remover um elemento da coleção.Esta é uma implementação perfeitamente válida de que
interface
:Outro exemplo: onde
java.util.Set<E>
realmente diz que é, você sabe, um conjunto ? Lugar algum! Ou mais precisamente, na documentação. Em inglês.Em praticamente todos os casos de
interfaces
, tanto do Java quanto do .NET, todas as informações relevantes estão na verdade nos documentos, não nos tipos. Portanto, se os tipos não dizem nada de interessante, por que mantê-los? Por que não ficar apenas com a documentação? E é exatamente isso que Ruby faz.Observe que existem outras linguagens nas quais a Interface pode realmente ser descrita de uma forma significativa. No entanto, essas linguagens normalmente não chamam a construção que descreve a Interface "
interface
", eles a chamamtype
. Em uma linguagem de programação de tipo dependente, você pode, por exemplo, expressar as propriedades de que umasort
função retorna uma coleção do mesmo comprimento que o original, que cada elemento que está no original também está na coleção classificada e que nenhum elemento maior aparece antes de um elemento menor.Resumindo: Ruby não tem equivalente a Java
interface
. Ele faz , no entanto, tem um equivalente a um Java interface , e é exatamente o mesmo que em Java: documentação.Além disso, assim como em Java, os testes de aceitação também podem ser usados para especificar interfaces .
Em particular, em Ruby, a Interface de um objeto é determinada pelo que ele pode fazer , não o que
class
é ou o quemodule
ele se mistura. Qualquer objeto que tenha um<<
método pode ser anexado. Isso é muito útil em testes de unidade, onde você pode simplesmente passar umArray
ou um emString
vez de um mais complicadoLogger
, emboraArray
eLogger
não compartilhe um explícito,interface
exceto pelo fato de que ambos têm um método chamado<<
.Outro exemplo é
StringIO
, que implementa a mesma interface comoIO
e, portanto, uma grande porção da interface deFile
, mas sem qualquer partilha ancestral comum alémObject
.fonte
interface
é inútil, perdendo o sentido de seu uso. Teria sido mais fácil dizer que ruby é digitado dinamicamente e que tem um foco diferente em mente e torna conceitos como IOC desnecessários / indesejados. É uma mudança difícil se você está acostumado com o projeto por contrato. Algo de que Rails poderia se beneficiar, que a equipe principal percebeu como você pode ver nas versões mais recentes.interface
- chave Java pode não fornecer todas as informações relevantes, mas fornece um local óbvio para colocar a documentação. Eu escrevi uma classe em Ruby que implementa (suficiente) IO, mas fiz isso por tentativa e erro e não fiquei muito feliz com o processo. Também escrevi várias implementações de uma interface minha, mas documentar quais métodos são necessários e o que eles devem fazer para que outros membros de minha equipe possam criar implementações provou ser um desafio.interface
construção é de fato necessária apenas para tratar tipos diferentes como iguais em linguagens de herança única com tipagem estática (por exemplo, tratarLinkedHashSet
eArrayList
ambos como aCollection
), não tem praticamente nada a ver com Interface como mostra esta resposta. Ruby não é tipado estaticamente, portanto, não há necessidade para a construção .Experimente os "exemplos compartilhados" de rspec:
https://www.relishapp.com/rspec/rspec-core/v/3-5/docs/example-groups/shared-examples
Você escreve uma especificação para sua interface e então coloca uma linha nas especificações de cada implementador, por exemplo.
Exemplo completo:
Atualização : Oito anos depois (2020), ruby agora tem suporte para interfaces tipadas estaticamente via sorvete. Consulte Classes e interfaces abstratas na documentação do sorbet.
fonte
Ruby não tem essa funcionalidade. Em princípio, ele não precisa deles, pois o Ruby usa o que é chamado de digitação de pato .
Existem algumas abordagens que você pode seguir.
Escreva implementações que levantem exceções; se uma subclasse tentar usar o método não implementado, ela falhará
Junto com o acima, você deve escrever um código de teste que reforce seus contratos (o que outro post aqui chama incorretamente de Interface )
Se você se pega escrevendo métodos vazios, como o tempo todo, então escreva um módulo auxiliar que capture
Agora, combine o acima com os módulos Ruby e você estará perto do que deseja ...
E então você pode fazer
Deixe-me enfatizar mais uma vez: isso é rudimentar, pois tudo em Ruby acontece em tempo de execução; não há verificação de tempo de compilação. Se você combinar isso com o teste, poderá detectar erros. Ainda mais, se você levar o exposto acima, provavelmente poderá escrever uma Interface que execute a verificação da classe na primeira vez que um objeto dessa classe for criado; tornando seus testes tão simples quanto chamar
MyCollection.new
... sim, por cima :)fonte
Como todos aqui disseram, não existe um sistema de interface para Ruby. Mas, por meio da introspecção, você mesmo pode implementá-lo facilmente. Aqui está um exemplo simples que pode ser melhorado de várias maneiras para ajudá-lo a começar:
Remover um dos métodos declarados em Person ou alterar o número de argumentos gerará a
NotImplementedError
.fonte
Não existem interfaces no modo Java. Mas há outras coisas que você pode desfrutar no ruby.
Se você quiser implementar algum tipo de tipo e interface - de forma que os objetos possam ser verificados se eles têm alguns métodos / mensagens que você deseja deles -, você pode então dar uma olhada em rubycontracts . Ele define um mecanismo semelhante aos PyProtocols . Um blog sobre verificação de tipos em ruby está aqui .
As abordagens mencionadas não são projetos vivos, embora o objetivo pareça bom no início, parece que a maioria dos desenvolvedores de ruby pode viver sem verificação de tipo estrita. Mas a flexibilidade do ruby permite implementar a verificação de tipo.
Se você deseja estender objetos ou classes (a mesma coisa no ruby) por certos comportamentos ou de alguma forma ter o método ruby de herança múltipla, use o mecanismo
include
ouextend
. Cominclude
você pode incluir métodos de outra classe ou módulo em um objeto. Comextend
você pode adicionar comportamento a uma classe, de forma que suas instâncias tenham os métodos adicionados. Essa foi uma explicação muito curta, no entanto.Em minha opinião, a melhor maneira de resolver a necessidade da interface Java é entender o modelo de objeto ruby (consulte as palestras de Dave Thomas, por exemplo). Provavelmente você esquecerá as interfaces Java. Ou você tem um aplicativo excepcional em sua programação.
fonte
Como muitas respostas indicam, não há como em Ruby forçar uma classe a implementar um método específico, herdando de uma classe, incluindo um módulo ou algo semelhante. A razão para isso é provavelmente a prevalência de TDD na comunidade Ruby, que é uma maneira diferente de definir a interface - os testes não apenas especificam as assinaturas dos métodos, mas também o comportamento. Portanto, se você quiser implementar uma classe diferente, que implemente alguma interface já definida, você deve ter certeza de que todos os testes foram aprovados.
Normalmente, os testes são definidos de forma isolada, usando simulações e stubs. Mas também existem ferramentas como o Bogus , que permite definir testes de contrato. Esses testes não apenas definem o comportamento da classe "primária", mas também verificam se os métodos fragmentados existem nas classes cooperantes.
Se você está realmente preocupado com interfaces em Ruby, eu recomendaria usar uma estrutura de teste que implementa testes de contrato.
fonte
Todos os exemplos aqui são interessantes, mas faltam a validação do contrato de Interface, quero dizer, se você deseja que seu objeto implemente todas as definições de métodos de Interface e somente estes você não pode. Portanto, proponho um exemplo rápido e simples (pode ser melhorado com certeza) para garantir que você tenha exatamente o que espera ter através de sua Interface (O contrato).
considere sua interface com os métodos definidos como esse
Então você pode escrever um objeto com pelo menos o contrato de Interface:
Você pode chamar seu objeto com segurança através de sua interface para garantir que você é exatamente o que a interface define
E você também pode garantir que seu objeto implemente todas as suas definições de métodos de interface
fonte
Estendi um pouco a resposta de carlosayam para minhas necessidades adicionais. Isso adiciona alguns reforços e opções adicionais à classe Interface:
required_variable
eoptional_variable
que oferece suporte a um valor padrão.Não tenho certeza se você gostaria de usar esta meta programação com algo muito grande.
Como outras respostas declararam, é melhor você escrever testes que reforcem adequadamente o que você está procurando, especialmente quando você deseja começar a aplicar parâmetros e valores de retorno.
Advertir este método apenas lança um erro na chamada do código. Os testes ainda seriam necessários para a aplicação adequada antes do tempo de execução.
Exemplo de Código
interface.rb
plugin.rb
Eu usei a biblioteca singleton para o padrão fornecido que estou utilizando. Desta forma, quaisquer subclasses herdam a biblioteca singleton ao implementar esta "interface".
my_plugin.rb
Para minhas necessidades, isso requer que a classe que implementa a "interface" a subclasse.
fonte
O próprio Ruby não tem equivalente exato a interfaces em Java.
No entanto, como essa interface às vezes pode ser muito útil, desenvolvi uma gema para Ruby, que emula interfaces Java de uma maneira muito simples.
É chamado
class_interface
.Funciona de forma bastante simples. Primeiro instale a gema
gem install class_interface
ou adicione-a ao seu Gemfile e rundbundle install
.Definindo uma interface:
Implementando essa interface:
Se você não implementar uma determinada constante ou método ou o número do parâmetro não corresponder, um erro correspondente será gerado antes que o programa Ruby seja executado. Você pode até determinar o tipo das constantes atribuindo um tipo na interface. Se nulo, qualquer tipo é permitido.
O método "implements" deve ser chamado na última linha de uma classe, pois essa é a posição do código onde os métodos implementados acima já estão verificados.
Mais em: https://github.com/magynhard/class_interface
fonte
Percebi que estava usando o padrão "Erro não implementado" demais para verificações de segurança em objetos para os quais queria um comportamento específico. Acabei escrevendo uma gema que basicamente permite usar uma interface como esta:
Ele não verifica os argumentos do método. Faz a partir da versão0.2.0
. Exemplo mais detalhado em https://github.com/bluegod/rintfonte