Pergunta

A base de código onde eu trabalho tem um objeto chamado Par onde A e B são os tipos de primeiro e segundo valores do par. I encontrar este objeto a ser ofensivo, porque ele é usado em vez de um objeto com membros claramente nomeados. Então, eu acho o seguinte:

List<Pair<Integer, Integer>> productIds = blah();
// snip many lines and method calls

void doSomething(Pair<Integer, Integer> id) {
  Integer productId = id.first();
  Integer quantity = id.second();
}

Em vez de

class ProductsOrdered {
  int productId;
  int quantityOrdered;
  // accessor methods, etc
}

List<ProductsOrderded> productsOrdered = blah();

Muitos outros usos do par na base de código são igualmente mau-cheiro.

tuplas Eu pesquisei e eles parecem ser muitas vezes incompreendido ou usado de forma duvidosa. Existe um argumento convincente a favor ou contra o seu uso? Posso compreender não querendo criar enormes hierarquias de classe, mas existem bases de código realistas, onde a hierarquia de classe iria explodir, caso tuplas não foram utilizados?

Foi útil?

Solução

Em primeiro lugar, uma tupla é rápido e fácil: em vez de escrever uma classe para cada vez que você quiser colocar 2 coisas juntos, não há um modelo que faz isso por você

.

Em segundo lugar, eles são genéricos. Por exemplo, em C ++ std :: mapa utiliza uma std :: par de chave e um valor. Assim, qualquer par pode ser usado, em vez de ter que fazer algum tipo de classe de mensagens publicitárias com métodos de acesso para todas as permutações de dois tipos.

Finalmente, eles são úteis para retornar vários valores. Não há realmente nenhuma razão para fazer uma classe especificamente para vários valores de retorno de uma função, e não deve ser tratado como um objeto se eles são independentes.

Para ser justo, o código colado é um mau uso de um par.

Outras dicas

Tuples são usados ??o tempo todo em Python onde eles estão integrados na linguagem e muito útil (eles permitem que vários valores de retorno para começar).

Às vezes, você realmente só precisa coisas par e criando um verdadeiro, honesto ao deus, classe é um exagero. Um outro lado, usando tuplas quando você realmente deve estar usando uma classe é tão ruim uma idéia de como o inverso.

O exemplo de código tem alguns diferentes cheiros:

  • Reinventar a roda

Já existe uma tupla disponíveis no quadro; a estrutura KeyValuePair. Isto é usado pela classe Dictionary para armazenar pares, mas você pode usá-lo em qualquer lugar ele se encaixa. (Não estou dizendo que ele se encaixa neste caso ...)

  • Fazer uma roda quadrada

Se você tem uma lista de pares é melhor usar a estrutura KeyValuePair do que uma classe com o mesmo propósito, uma vez que resulta em menos alocações de memória.

  • Escondendo a intenção

Uma classe com propriedades mostra claramente que os valores médios, enquanto uma classe Pair<int,int> não lhe diz nada sobre o que os valores representam (só que eles estão provavelmente relacionadas de alguma forma). Para tornar o código razoavelmente auto-explicativo com uma lista como essa, você teria que dar a lista um nome muito desciptive, como productIdAndQuantityPairs ...

Para que o seu valor, o código no OP é uma bagunça não porque ele usa tuplas, mas porque os valores na tupla são muito fracamente tipado. Compare o seguinte:

List<Pair<Integer, Integer>> products_weak = blah1();
List<Pair<Product, Integer>> products_strong = blah2();

eu ficar chateado muito se a minha equipe de desenvolvimento foram passando em torno de IDs em vez de instâncias de classe ao redor, porque um inteiro pode representar qualquer .


Com isso dito, tuplas são extremamente útil quando você usá-los direita:

  • Tuples existem para grupo ad hoc valores. Eles são certamente melhor do que criar um número excessivo de classes de mensagens publicitárias.
  • alternativa útil para out / ref parâmetros quando você precisar retornar mais de um valor de uma função.

No entanto, tuplas em C # fazer o meu olhos água. Muitas línguas, como OCaml, Python, Haskell, F #, e assim por diante tem uma sintaxe especial, concisa para definir tuplas. Por exemplo, em F #, a Mapa módulo define um construtor da seguinte forma:

val of_list : ('key * 'a) list -> Map<'key,'a>

Eu posso criar uma instância de um mapa usando:

(* val values : (int * string) list *)
let values = 
    [1, "US";
     2, "Canada";
     3, "UK";
     4, "Australia";
     5, "Slovenia"]

(* val dict : Map<int, string> *)
let dict = Map.of_list values

O código equilvalent em C # é ridicuous:

var values = new Tuple<int, string>[] {
     new Tuple<int, string>(1, "US"),
     new Tuple<int, string>(2, "Canada"),
     new Tuple<int, string>(3, "UK"),
     new Tuple<int, string>(4, "Australia"),
     new Tuple<int, string>(5, "Slovenia")
     }

 var dict = new Dictionary<int, string>(values);

Eu não acredito que haja algo de errado com tuplas , em princípio, , mas C # 's sintaxe é demasiado pesado para obter a maioria de uso fora deles.

É de reutilização de código. Ao invés de escrever Yet Another classe com exatamente a mesma estrutura como a última 5 Tuple-como classes fizemos, você faz ... uma classe Tuple, e o uso que sempre que você precisar de uma tupla.

Se o único significado da classe é "para armazenar um par de valores", então eu diria que usando tuplas é uma idéia óbvia. Eu diria que foi um cheiro de código (tanto quanto eu odeio o termo) se você começou a implementar várias classes idênticas apenas para que você pode renomear os dois membros.

Este é apenas código do protótipo qualidade que provavelmente foi esmagado juntos e nunca foi reformulado. Não corrigi-lo é apenas preguiça.

O uso real para tuplas é para funções genéricas que realmente não ligo para o que as partes componentes são, mas apenas opera no nível tupla.

Scala tem tipos de valor tupla, todo o caminho a partir de 2-tuplas (es) de até tuplas com mais de 20 elementos. Consulte primeiros passos para Scala (passo 9):

val pair = (99, "Luftballons")
println(pair._1)
println(pair._2)

Tuples são úteis se você precisar agrupar em conjunto valores para alguns relativamente ad hoc propósito. Por exemplo, se você tem uma função que precisa retornar dois objetos bastante alheios, ao invés de criar uma nova classe para manter os dois objetos, você retorna um par da função.

Eu concordo completamente com outros cartazes que tuplas pode ser mal utilizado. Se uma tupla tem qualquer tipo de semântica importantes para sua aplicação, você deve usar uma classe adequada em seu lugar.

O exemplo óbvio é um par de coordenadas (ou triplo). Os rótulos são irrelevantes; utilizando X e Y (e Z) é apenas uma convenção. Tornando-uniforme deixa claro que eles podem ser tratados da mesma forma.

Muitas coisas já foram mencionados, mas acho que deve-se também mencionar que há alguns estilos de programação, que diferem de OOP e para aquelas tuplas são bastante úteis.

linguagens de programação funcionais como Haskell, por exemplo, não têm aulas em tudo.

Se você está fazendo um Schwartziana transformar para ordenar por uma chave particular (que é caro para calcular repetidamente), ou algo parecido, uma classe parece ser um pouco exagerado:

val transformed = data map {x => (x.expensiveOperation, x)}
val sortedTransformed = transformed sort {(x, y) => x._1 < y._1}
val sorted = sortedTransformed map {case (_, x) => x}

Ter um class DataAndKey ou o que parece um pouco supérfluo aqui.

O seu exemplo não foi um bom exemplo de uma tupla, porém, eu concordo.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top