Sposób na zwrócenie wielu zwracanych wartości z metody: umieść metodę w klasie reprezentującej zwracaną wartość. Czy to dobry projekt?

15

Muszę zwrócić 2 wartości z metody. Moje podejście jest następujące:

  1. utwórz wewnętrzną klasę z 2 polami, które będą używane do zachowania tych 2 wartości
  2. umieść metodę w tej klasie
  3. utwórz instancję klasy i wywołaj metodę.

Jedyną rzeczą, która zostanie zmieniona w metodzie jest to, że ostatecznie przypisze te 2 wartości do pól instancji. Następnie mogę odnieść się do tych wartości, odwołując się do pól tego obiektu.

Czy to dobry projekt i dlaczego?

dhblah
źródło
Inna opcja (prawdopodobnie zła): patrz BitInteger[] java.math.BigInteger.divideAndRemainder(BitInteger val). Zwraca 2 liczby całkowite jako wartości zwracane w tablicy.
EarNameless
Jakiego typu są dwie wartości, które chcesz zwrócić?
Tulains Córdova
Możliwy duplikat - stackoverflow.com/questions/12668186/… .
m3th0dman

Odpowiedzi:

15

Argumentowałbym to w następujący sposób:

  • Dlaczego dokładnie twoja metoda zwraca wiele wartości? O jakim rodzaju spójności mówimy - czy te wartości powinny faktycznie być polami w jednej klasie, czy też są przypadkowo zwracane tą samą metodą, ale poza tym niezwiązane? Jeśli jest to drugie, możesz rozważyć podzielenie metody na dwie metody. Edytuj: użyj tutaj oceny; czasami najlepszym rozwiązaniem jest rodzaj „przypadkowej” spójności. Inną opcją jest użycie konstrukcji parowej lub krotkowej, chociaż w OOP zwykle nie są one widoczne w publicznych interfejsach API (niektóre znaczące wyjątki to standardowe kolekcje itp.).
  • Jeśli wartości zasługują na utworzenie klasy, prawdopodobnie odradzałbym używanie klasy wewnętrznej. Klasy wewnętrzne są zwykle używane jako wewnętrzne szczegóły implementacji, które są ukryte z zewnątrz. Czy jest jakiś powód, dla którego wynik ten nie powinien być klasą „w pełnym wymiarze”?
  • Jakie operacje oprócz przechowywania danych mają zastosowanie do tej nowej klasy? W projektowaniu obiektowym chcesz, aby powiązane zachowanie było zbliżone do odpowiednich danych (które również wydają się być twoimi intencjami). Czy metoda, o której mówisz, nie powinna raczej żyć w tej klasie?

Podsumowując, chciałbym przekonać się, czy mogę przekształcić ten „obiekt danych” w rozwiniętą klasę z danymi i zachowaniem. Jako dodatkowy komentarz, możesz sprawić, by klasa była niezmienna, ponieważ jej stan jest ustawiany raz. Uczynienie go niezmiennym pomoże zapobiec niepoprawnemu ustawieniu lub modyfikacji w późniejszym czasie (powiedzmy, że ktoś ustawi jedno z pól na zero i przekaże je dalej).

Edycja: Jak słusznie zauważa Patkos Csaba, stosowana tutaj zasada to Zasada Jednej Odpowiedzialności ( SRP ) - klasa, którą próbujesz stworzyć, powinna mieć naprawdę jedną odpowiedzialność (zdefiniowaną jako powód zmiany ). Te wytyczne projektowe powinny pomóc ci dowiedzieć się, czy twoje dwa pola należą do jednej klasy, czy nie. Aby trzymać się przykładu z Wikipedii, twoja klasa może być postrzegana jako rodzaj raportu, w którym to przypadku jest zgodny z SRP, ale trudno jest skomentować bez dalszych informacji.

Daniel B.
źródło
Chociaż zgadzam się z ogólną ideą tej odpowiedzi, istnieją uzasadnione przypadki, w których dwa ściśle powiązane dane są obliczane razem, ale nie ma sensu wiązać ich w żaden inny sposób w programie. W takim przypadku może być wystarczająco czysty, aby zwrócić coś takiego Pair<OneClass, AnotherClass>. Niektórzy się nie zgodzą . W każdym razie Pairpowinien być szczegółem implementacji i nigdy nie powinien pojawiać się w publicznej metodzie API.
9000
@ 9000 Zgadzam się; I faktycznie oznaczało słowo pod uwagę należy brać dosłownie w tym przypadku, czyli dzielenie metoda nie zawsze jest najlepsze rozwiązanie, to tylko szorstki wskazanie w tym kierunku. Dokonam edycji zgodnie z tymi liniami.
Daniel B
1
Dobra odpowiedź. Jedyne, co chciałbym dodać, to odniesienie do zasady pojedynczej odpowiedzialności (SRP) ( en.wikipedia.org/wiki/Single_responsibility_principle ). Jeśli zaznaczone, bardzo możliwe jest, że omawiana metoda jest po prostu naruszeniem SRP, a podział jest prostym rozwiązaniem. Z mojego doświadczenia wynika, że ​​ilekroć metoda chce zwrócić 2 lub więcej wartości, w 90% przypadków są tam 2 metody lub należy wyodrębnić inną klasę.
Patkos Csaba
@PatkosCsaba dzięki, dokonałem edycji, aby go uwzględnić. Zwykle staram się wyjaśniać rzeczy w kategoriach sprzężenia i spójności, ale myślę, że zasady SOLID są postrzegane jako podstawowe zasady życia w tych dniach.
Daniel B
@DanielB: Uważam SOLID za wyższy poziom i prawdopodobnie łatwiejsze do zrozumienia pojęcia. Łączenie i spójność są nadal podstawą, ale są one na niższym poziomie. SOLID doskonale wykorzystuje sprzężenie i spójność, aby wyjaśnić swoje zasady i przedstawić je na bardziej ogólnym poziomie.
Patkos Csaba
10

Istnieje koncepcja krotki, która występuje w innych językach, takich jak Python.

Można zwrócić instancję tej uogólnionej klasy, którą można łatwo ponownie wykorzystać:

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
źródło
2
Czasami miło jest mieć ogólną metodę statyczną create(), aby uniknąć konieczności określania parametrów typu w konstruktorze. Również chciałbym wymienić tę klasę Pairzamiast Tuple, ponieważ może reprezentować tylko 2-krotki wartości.
sierpień
5

Wygląda na to, że ta klasa odbiera odpowiedzialność innej klasie, a to sprawia, że ​​myślę, że ten projekt nie jest świetny.

W przypadku metody zwracającej wartości uniwersalne wolałbym

  • zwraca ogólny kontener (np. Listę lub Mapę) zawierający zwracane wartości

lub

  • utwórz klasę dla wartości zwracanej, która zawiera tylko niezbędne pola + gettery + konstruktor ze wszystkimi polami

Przykład dla drugiej opcji:

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;
   }
}
użytkownik 281377
źródło
Upublicznienie pól dodaje opcję oddzielnej zmiany wartości, chociaż wyraźnie wartości mają relację (w przeciwnym razie nie musiałyby być zwracane tą samą metodą). Bardzo odradzałbym ten wzór w tej szczególnej sytuacji. Ale najważniejsze jest to, że dyskusja atrybutów publicznych i prywatnych nie ma nic wspólnego z pytaniem dotyczącym PO i nie widzę powodu dla ostatniego zdania w innej dobrej odpowiedzi.
scarfridge
Pierwszy powinien być preferowany.
Juanin
scarfridge: Punkt zajęty, ostatnie zdanie usunięte.
user281377
2
Po prostu użyj publicznych pól końcowych, bez powodu, aby tracić czas na akcesoria.
sierpień
0

Krótka odpowiedź: możesz zwrócić tablicę lub listę z dwiema wartościami.

Osobiście napisałbym dwie różne metody

int x = obj.getX();
int y = obj.getY();
Tulains Córdova
źródło