Способ вернуть несколько возвращаемых значений из метода: поместить метод в класс, представляющий возвращаемое значение. Это хороший дизайн?

15

Мне нужно вернуть 2 значения из метода. Мой подход заключается в следующем:

  1. создайте внутренний класс с двумя полями, которые будут использоваться для хранения этих двух значений
  2. положить метод внутри этого класса
  3. создать экземпляр класса и вызвать метод.

Единственное, что будет изменено в методе, это то, что в конце он назначит эти 2 значения полям экземпляра. Затем я могу обратиться к этим значениям, ссылаясь на поля этого объекта.

Это хороший дизайн и почему?

dhblah
источник
Другой вариант (вероятно, плохой): видите BitInteger[] java.math.BigInteger.divideAndRemainder(BitInteger val). Он возвращает 2 целых числа в качестве возвращаемых значений в массиве.
EarlNameless
Какого типа два значения вы хотите вернуть?
Тулаинс Кордова
Возможный дубликат - stackoverflow.com/questions/12668186/… .
m3th0dman

Ответы:

15

Я бы поспорил по этому поводу:

  • Почему именно ваш метод возвращает несколько значений? О каком единстве мы говорим - должны ли эти значения быть полями в одном классе, или они просто по совпадению возвращаются одним и тем же методом, но никак не связаны? Если это последнее, вы можете рассмотреть возможность разделения метода на два метода. Изменить: используйте свое мнение здесь; иногда тип «случайной» сплоченности может быть лучшим вариантом. Другой вариант - использовать пару или кортеж, хотя в ООП они обычно не видны в общедоступных API-интерфейсах (некоторые заметные исключения - это стандартные коллекции и т. Д.).
  • Если значения заслуживают формирования класса, я бы, вероятно, не советовал использовать внутренний класс. Внутренние классы обычно используются как внутренние детали реализации, которые скрыты снаружи. Есть ли какая-то причина, по которой этот результат сам по себе не должен быть «полноценным» классом?
  • Какие операции применимы к этому новому классу, кроме хранения данных? В объектно-ориентированном проектировании вы хотите, чтобы связанное поведение было близко к соответствующим данным (что, похоже, также является вашим намерением). Должен ли метод, на который вы ссылаетесь, жить не в этом классе?

Подводя итог, я хотел бы посмотреть, смогу ли я превратить этот «объект данных» в полноценный класс с данными и поведением. В качестве дополнительного комментария вы можете сделать класс неизменным, поскольку его состояние устанавливается один раз. Если сделать его неизменным, это поможет предотвратить его неправильную настройку или изменение позже (скажем, кто-то установит одно из полей в null и передаст его).

Редактировать: Как правильно отмечает Паткос Чаба, применяемый здесь принцип - это принцип единой ответственности ( SRP ) - класс, который вы пытаетесь создать, должен действительно иметь одну ответственность (определяемую как причина для изменения ). Это руководство по проектированию должно помочь вам определить, принадлежат ли ваши два поля одному классу или нет. Если придерживаться примера Википедии, ваш класс может рассматриваться как тип отчета, и в этом случае он соответствует SRP, но его сложно комментировать без дополнительной информации.

Даниэль Б
источник
Хотя я согласен с общей идеей этого ответа, существуют законные случаи, когда две тесно связанные части данных рассчитываются вместе, но нет смысла связывать их вместе где-либо еще в программе. В таком случае он может быть достаточно чистым, чтобы вернуть что-то вроде Pair<OneClass, AnotherClass>. Некоторые люди не согласны . В любом случае, Pairдолжна быть деталь реализации и никогда не появляться в публичном методе API.
9000
@ 9000 Я согласен; Я на самом деле имел в виду слово « считать» в данном случае буквально, то есть разделение метода не всегда может быть лучшим решением, это всего лишь приблизительное указание в этом направлении. Я сделаю правку в том же духе.
Даниэль Б
1
Хороший ответ. Единственное, что я хотел бы добавить, - это ссылка на принцип единой ответственности (SRP) ( en.wikipedia.org/wiki/Single_responsibility_principle ). Если этот флажок установлен, вполне возможно, что обсуждаемый метод является простым нарушением SRP, а разделение - простым решением. По моему опыту, всякий раз, когда метод хочет вернуть 2 или более значений, в 90% случаев там есть 2 метода или другой класс должен быть извлечен.
Паткос Чаба
@PatkosCsaba спасибо, внес изменения, чтобы включить его. Я обычно придерживаюсь объяснения вещей в терминах связности и сплоченности, но я полагаю, что принципы SOLID рассматриваются как основные правила, по которым следует жить в наши дни.
Даниэль Б
@DanielB: я вижу SOLID как более высокий уровень и, вероятно, легче понять концепции. Сцепление и сцепление по-прежнему являются базовой линией, но они находятся на более низком уровне. SOLID широко использует связь и сплоченность, чтобы объяснить свои принципы и представить их на более общем уровне.
Паткос Чаба
10

Есть концепция Tuple, которая представлена ​​в других языках, таких как Python.

Можно вернуть экземпляр этого обобщенного класса, который легко использовать повторно:

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
источник
2
Иногда полезно иметь общий статический метод create(), чтобы не указывать параметры типа в конструкторе. Кроме того, я бы назвал этот класс, Pairа не Tuple, учитывая, что он может представлять только 2 кортежа значений.
Авгурар
5

Кажется, что этот класс берет на себя ответственность от другого класса, и это заставляет меня думать, что этот дизайн не велик.

Для метода, который возвращает многозначные значения, я бы предпочел

  • вернуть общий контейнер (например, список или карту), содержащий возвращаемые значения

или

  • создать класс для возвращаемого значения, который содержит только необходимые поля + геттеры + конструктор со всеми полями

Пример для второго варианта:

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
источник
Публичный доступ к полям добавляет возможность изменения значений отдельно, хотя, очевидно, значения имеют отношение (в противном случае их не нужно было бы возвращать одним и тем же методом). Я бы очень не рекомендовал эту модель в этой особой ситуации. Но суть в том, что обсуждение атрибутов public и private не имеет ничего общего с вопросом OP, и я не вижу причин для последнего предложения в другом хорошем ответе.
шарфридж
Первый должен быть предпочтительным.
Хуанин
scarfridge: Точка занята, последнее предложение удалено.
user281377
2
Просто используйте открытые финальные поля, нет причин тратить время на средства доступа.
Авгурар
1

Помещение нескольких возвращаемых значений из метода в его собственный класс / структуру часто используется в системах , основанных на сообщениях, которые имеют один класс для запроса и ответа . Примером этого является простой протокол доступа к объектам (SOAP) .

Это хороший дизайн и почему?

По крайней мере, это довольно часто. Хорошо это или плохо, зависит от вашего особого варианта использования.

k3b
источник
0

Краткий ответ: вы можете вернуть массив или список с двумя значениями.

Я лично написал бы два разных метода, как

int x = obj.getX();
int y = obj.getY();
Тулаинс Кордова
источник