Qual é a diferença entre os prefixos "to" e "as" do nome do método? [fechadas]

78

Qual é a diferença entre os prefixos "to" e "as" do nome do método, como

  • listar(),
  • asList (),
  • etc ...

Quando usar qual ao projetar um método?

Daniel Hári
fonte

Respostas:

120

A toXYZ()função é esperado para fazer uma conversão, e para retornar uma nova independente objeto (embora imutabilidade permite a otimização, java.lang.String.toString()apenas retorna o objeto).
Como exemplo, em C ++, temos o std::bitset::to_ulong()que pode falhar facilmente e toda uma infinidade de to_string(), todos fazendo uma conversão (mais ou menos) complexa e alocando memória.

Uma asXYZ()função, por outro lado é esperado para retornar uma visão (potencialmente diferente) da fonte, fazendo o mínimo de trabalho.
Como exemplo, em C ++, temos o std::as_const()que apenas retorna uma referência constante e o mais envolvido, std::forward_as_tupleque também se refere a seus argumentos por referência.

Desduplicador
fonte
19
Acontece que existe algum precedente para isso. Em geral, As [algo] será semelhante a um elenco, enquanto To [algo] constrói um novo objeto (de um tipo diferente) a partir de um existente. No caso do ToList, será O (n) e quase certamente mais caro que um "As". Veja stackoverflow.com/questions/17968469/…
Robert Harvey
5
Faz parte da seleção de nomes descritivos e, mesmo que essa distinção específica não seja codificada no padrão de codificação, violá-la quebra o Princípio de menor espanto .
Deduplicator
11
Eu o descreveria como, instintivamente, pensaria em "para" como "mudar para" e "como" como "tratar como". O primeiro implica trabalho para mudar a coisa, o segundo implica que é apenas uma diferença na maneira como você trabalha com ele.
Latty
6
Se isso significa alguma coisa, deve significar isso. Eu sugeriria que, muitas vezes, isso não significa nada, portanto, não confiaria nele no código aleatório de terceiros (sem ver algo nos documentos). Seria uma boa convenção adotar em uma base de código.
Ben
4
Essa resposta também é precisa no C ++: por exemplo, std::to_string(x)cria um novo objeto de seqüência de caracteres, mas std::as_const(x)cria uma referência (uma exibição) do objeto existente.
Quuxplusone 5/07
13

Honestamente, pode ser apenas nomear inconsistência. Se olhar para objetos de biblioteca padrão em Smalltalk, por exemplo, todos os métodos que fazem "conversões complexas" ou "retornar representações simples em outro tipo" são prefixados com as, como em asString, asFloat, asSeconds, e o método padrão para converter qualquer coisa para qualquer outra coisa, as: aClass.

Em Ruby, os mesmos tipos de métodos têm o prefixo to_, como em to_s, to_a, to_h, para curto string, arraye hashrespectivamente.

Nenhuma das bibliotecas padrão parece diferenciar entre diferentes tipos de conversões, provavelmente porque deve ser considerada como um detalhe de implementação.

No entanto, em Java, vemos muitas confusões. Como você mencionou, há toString, asListe assim por diante. Acredito que essas são apenas uma inconsistência de nomes, porque se você tentar definir um significado diferente para cada prefixo, sempre encontrará um contra-exemplo em algum outro lugar da biblioteca padrão.

Portanto, em qualquer caso, eu diria que o importante é que você e sua equipe escolham um prefixo e o usem consistentemente em todo o código. A consistência é a chave, para que as pessoas não se perguntem como você precisava.

MichelHenrich
fonte
1
Não acredito em: escolher um estilo para toda a equipe, em vez disso, escolher um estilo para casos semelhantes em um módulo, de forma consistente.
Daniel Hári
12
Em Java, toStringcria uma nova string totalmente desconectada do objeto (e não há outra maneira por causa da imutabilidade). O mesmo vale, por exemplo, para Collection#toArray, enquanto Arrays#asListretorna uma visão da matriz, que é bidirecionalmente conectada (a mutação da matriz altera a lista e vice-versa). Portanto, é bastante consistente, embora possa haver exceções. Escolher um prefixo estaria errado. Se houvesse Arrays#toList, esperaria criar uma nova lista com uma nova matriz subjacente.
Maaartinus
Acho que Smalltalk cometeu um erro lá, eles não entenderam a convenção. É uma convenção difícil de entender (e até notar) porque é muito abstrata e não é particularmente impactante. Até o próprio ato de fazer esta pergunta requer alguma percepção.
usr
3
@usr Smalltalk poderiam ter sido concebido antes de se tornar uma convenção
Bergi
6

Embora já exista uma resposta aceita, parece focar em C ++, enquanto a pergunta está marcada com java . Em Java, o primeiro exemplo que vem à mente para esse tipo de coisa é Arrays.asList , que retorna, essencialmente, uma visão de uma matriz, agrupada em uma lista. A matriz subjacente e a lista ainda estão conectadas; as alterações na matriz são refletidas na lista e vice-versa. No entanto, a matriz retornada pelo método toArray da lista é independente da matriz original e da lista:

String[] wordArray = {"one", "fine", "day"};
List<String> wordList = Arrays.asList(wordArray);

// changes to the array are visible in the list
System.out.println(wordList); // prints "[one, fine, day]"
wordArray[1] = "horrible";
System.out.println(wordList); // prints "[one, horrible, day]"

// changes to the list are visible in the array
wordList.set(1, "beautiful");
System.out.println(wordArray[1]); // prints "beautiful"

// but changes to the list or array don't affect the 
// result from the list's toArray method.
String[] moreWords = wordList.toArray(new String[] {});
wordList.set(0, "the");
wordArray[1] = "best";
for (int i=0; i<3; i++) {
  System.out.println(moreWords[i]); // prints "one", "beautiful", and "day"
}

Dito isso, não há garantia de que todos os desenvolvedores de bibliotecas sigam essa convenção; portanto, você ainda precisa verificar a documentação para descobrir se esse é o comportamento que você obterá do código desconhecido.

O outro lugar que eu vi como métodos ... () usado com freqüência é nos tipos de downcast para subtipos. Por exemplo, se você tiver um conjunto enumerado de subtipos, poderá acabar com um código como:

  /**
   * Every Node is either an ANode or a BNode.
   */
  interface Node {

    /**
     * Returns this Node as an ANode.
     * 
     * @return this node
     */
    default ANode asANode() {
      if (this instanceof ANode) {
        return (ANode) this;
      }
      else {
        throw new UnsupportedOperationException();
      }
      // Or, in Java8 style, perhaps:
      // return Optional.of(this)
      //     .filter(ANode.class::isInstance)
      //     .map(ANode.class::cast)
      //     .orElseThrow(UnsupportedOperationException::new);
    }

    /**
     * Returns this Node as a BNode.
     * 
     * @return this node
     */
    default BNode asBNode() {
      if (this instanceof BNode) {
        return (BNode) this;
      }
      else {
        throw new UnsupportedOperationException();
      }
    }
  }
Joshua Taylor
fonte
1

A diferença que notei (agora pensando nisso) é

  • Para tipicamente se converter em um tipo de referência diferente (um objeto um tanto complexo)
  • Como normalmente retorna um tipo de valor simples

Então, vemos AsInteger e AsString e ToArray e ToStringList.

Implica uma conversão, que faz sentido (é um movimento, um processo). Como implica uma representação, uma maneira de expressar o objeto original.

Outra maneira de olhar para isso:

  • To é uma operação, então você a usaria para métodos.
  • Como é uma representação simples, você a usaria para propriedades.

E depois há "arte anterior" (ou legado) para lidar. Antes que os idiomas fossem totalmente OO desde o início, você teria funções de biblioteca como StrToInt () e IntToStr (). Eles realizaram conversões, eram operações, por isso fazia sentido chamá-los de SomethingToSomethingelse (). Afinal, To é mais ativo que As. Estou pensando particularmente em Delphi aqui.

Quando o C # foi projetado com a ambição de fazer OO até o fim, fazia sentido ter um método no objeto agora inteiro que converteria o número inteiro em uma string. Embora também tenhamos uma classe Convert, a conversão em string é tão comum que se tornou um método virtual no objeto. Os designers podem ter imaginado que o ToString seria mais familiar para as pessoas do antigo paradigma e talvez seja por isso que obtivemos um método virtual ToString () e não uma propriedade virtual AsString.

Martin Maat
fonte
3
Que tal toString()?
Deduplicator
Você me pegou lá. Eu acho que o AsString teria sido mais apropriado. Se tiver alguns pensamentos adicionais, atualizarei minha resposta.
Martin Maat
Eu não acho que essa resposta realmente atenda às convenções em c #. Por exemplo. ToList () e AsEnumerable () retornam objetos complexos, a diferença é que ToList () sempre retorna um novo objeto enquanto AsEnumerable () retorna uma exibição ou adaptador sobre o objeto original.
JacquesB
@JaquesB Aparentemente, idéias diferentes foram aplicadas aqui e ali. O Delphi possui as propriedades AsInteger e AsString nos objetos TField (que encapsulam os campos do banco de dados). Eu posso ter sido tendencioso por isso. Mas, novamente, a questão está marcada com Java, não com C # ou Delphi.
Martin Maat