Uma maneira de retornar vários valores de retorno de um método: coloque o método dentro da classe que representa o valor de retorno. É um bom design?

15

Eu preciso retornar 2 valores de um método. Minha abordagem é a seguinte:

  1. crie uma classe interna com 2 campos que serão usados ​​para manter esses 2 valores
  2. coloque o método dentro dessa classe
  3. instanciar a classe e chamar o método

A única coisa que será alterada no método é que, no final, ele atribuirá esses 2 valores aos campos da instância. Então eu posso abordar esses valores fazendo referência aos campos desse objeto.

É um bom design e por quê?

dhblah
fonte
Outra opção (provavelmente ruim): veja BitInteger[] java.math.BigInteger.divideAndRemainder(BitInteger val). Retorna 2 números inteiros como seus valores de retorno em uma matriz.
earlNameless
Que tipo são os dois valores que você deseja retornar?
Tulains Córdova 01/10/12
Possível duplicada - stackoverflow.com/questions/12668186/… .
M3th0dman 1/10/12

Respostas:

15

Eu argumentaria isso da seguinte maneira:

  • Por que exatamente seu método retorna vários valores? De que tipo de coesão estamos falando - esses valores devem realmente ser campos em uma única classe ou são retornados apenas por coincidência pelo mesmo método, mas não relacionados? Se for o último, convém dividir o método em dois métodos. Editar: use seu julgamento aqui; Às vezes, um tipo de coesão "coincidente" pode ser a melhor opção. Outra opção é usar uma construção de par ou de tupla, embora em OOP, elas geralmente não sejam vistas em APIs públicas (algumas exceções notáveis ​​são coleções padrão, etc.).
  • Se os valores merecem formar uma classe, eu provavelmente desaconselharia o uso de uma classe interna. Classes internas são normalmente usadas como detalhes de implementação interna, ocultos do exterior. Existe alguma razão para que esse resultado não deva ser uma classe "completa", por si só?
  • Além de manter dados, quais operações são aplicáveis ​​a essa nova classe? No design orientado a objetos, você deseja ter o comportamento relacionado próximo aos dados relevantes (que também parecem ser suas intenções). O método ao qual você está se referindo não deve residir nessa classe?

Para resumir, eu verificaria se posso transformar esse "objeto de dados" em uma classe avançada com dados e comportamento. Como um comentário adicional, convém tornar a classe imutável, pois seu estado é definido uma vez. Torná-lo imutável ajudará a evitar que seja definido incorretamente ou modificado posteriormente (digamos, alguém configurando um dos campos para nulo e passando adiante).

Edit: Como Patkos Csaba salienta corretamente, o princípio a ser aplicado aqui é o Single Responsability Principle ( SRP ) - a classe que você está tentando criar deve realmente ter uma responsabilidade (definida como uma razão para mudar ). Esta diretriz de design deve ajudá-lo a descobrir se seus dois campos pertencem a uma única classe ou não. Para ficar com o exemplo da Wikipedia, sua classe pode ser vista como um tipo de relatório; nesse caso, está em conformidade com o SRP, mas é difícil comentar sem mais informações.

Daniel B
fonte
Embora eu concorde com a idéia geral desta resposta, há casos legítimos em que dois dados intimamente relacionados são calculados juntos, mas não faz sentido associá-los de outra forma em qualquer outro lugar do programa. Nesse caso, pode estar limpo o suficiente para retornar algo parecido Pair<OneClass, AnotherClass>. Algumas pessoas discordariam . Em qualquer caso, Pairdeve ser um detalhe de implementação e nunca aparecer em um método público de API.
9000
@ 9000 Eu concordo; Na verdade, eu quis dizer que a palavra considerar deve ser considerada literalmente neste caso, ou seja, dividir o método nem sempre pode ser a melhor solução, é apenas uma indicação aproximada nessa direção. Vou fazer uma edição nesse sentido.
Daniel B
1
Boa resposta. A única coisa que gostaria de acrescentar é uma referência ao Princípio da Responsabilidade Única (SRP) ( en.wikipedia.org/wiki/Single_responsibility_principle ). Se marcado, é muito possível que o método em discussão seja apenas uma simples violação do SRP e uma divisão seja uma solução simples. Na minha experiência, sempre que um método deseja retornar 2 ou mais valores, em 90% dos casos existem 2 métodos lá ou outra classe deve ser extraída.
Patkos Csaba
Obrigado @PatkosCsaba, fez uma edição para incluí-lo. Normalmente, explico as coisas em termos de acoplamento e coesão, mas acho que os princípios do SOLID são vistos como as regras básicas a serem cumpridas atualmente.
Daniel B
@ DanielB: Eu vejo o SOLID como um nível mais alto e provavelmente mais fácil de entender conceitos. O acoplamento e a coesão ainda são a linha de base, mas são mais baixos. O SOLID utiliza muito o acoplamento e a coesão para explicar seus princípios e apresentá-los em um nível mais genérico.
Patkos Csaba
10

Existe o conceito de uma tupla que é destaque em outros idiomas, como Python.

Pode-se retornar uma instância dessa classe genérica que é facilmente reutilizável:

public class TypedTuple<L, R> implements Serializable {
private static final long serialVersionUID = 1L;

  protected L left;
  protected R right;

  protected TypedTuple() {
    // Default constructor for serialization
  }

  public TypedTuple(L inLeft, R inRight) {
    left = inLeft;
    right = inRight;
  }

  public L getLeft() {
    return left;
  }

  public R getRight() {
    return right;
  }
}
Cuga
fonte
2
Às vezes, é bom ter um método estático genérico create(), para evitar precisar especificar os parâmetros de tipo no construtor. Além disso, eu nomearia essa classe em Pairvez de Tuple, pois ela pode representar apenas duas tuplas de valores.
Augurar 11/07
5

Parece que esta classe está tirando a responsabilidade de outra classe, e isso me faz pensar que esse design não é ótimo.

Para um método que retorna valores multible, prefiro

  • retornar um contêiner genérico (por exemplo, uma lista ou mapa) contendo os valores de retorno

ou

  • crie uma classe para o valor de retorno, que contém apenas os campos necessários + getters + um construtor com todos os campos

Exemplo para a segunda opção:

public Class FooBarRetval {
   private String foo;
   private int bar;

   public FooBarRetval (String foo, int bar) {
      this.foo = foo;
      this.bar = bar;
   }

   public String getFoo() {
      return foo;
   }

   public int getBar() {
      return bar;
   }
}
user281377
fonte
Tornar os campos públicos adiciona a opção de alterar os valores separadamente, embora claramente os valores tenham uma relação (caso contrário, eles não precisariam ser retornados pelo mesmo método). Eu desencorajaria altamente esse padrão nessa situação especial. Mas o ponto principal é que a discussão de atributos público versus privado não tem nada a ver com a pergunta dos OPs e não vejo razão para a última frase em uma resposta boa.
scarfridge
O primeiro deve ser preferido.
Juanin
scarfridge: ponto apontado, última frase removida.
precisa saber é o seguinte
2
Basta usar campos finais públicos, sem motivo para perder tempo com os acessadores.
Augurar 11/07
0

Resposta curta: você pode retornar uma matriz ou uma lista com os dois valores.

Eu pessoalmente escreveria dois métodos distintos, como

int x = obj.getX();
int y = obj.getY();
Tulains Córdova
fonte