Подтвердить что ты не робот

Является ли "пропуск по ссылке" плохим дизайном?

Итак, я только начал изучать Java, и я обнаружил, что нет такой вещи, как "пройти по ссылке". Я переношу приложение с С# на Java, а исходное приложение имеет int и double, которые являются параметрами "ref" или "out".

Сначала мне показалось, что я могу передать "Integer" или "Double", и поскольку это был тип Reference, значение было бы изменено. Но потом я узнал, что эти ссылочные типы неизменяемы.

Итак, тогда я создал класс "MutableInteger" и класс "MutableDouble", и я передал их в свои функции. Это работает, но я предполагаю, что я должен идти против первоначальных дизайнерских намерений языка.

Является ли "pass by reference" вообще плохим дизайном? Как мне изменить свой образ мышления?

Кажется разумным иметь такую ​​функцию:

bool MyClass::changeMyAandB(ref int a, ref int b)
{
    // perform some computation on a and b here

    if (success)
        return true;
    else return false;
}

Это плохой дизайн?

4b9b3361

Ответ 1

Это неплохой дизайн на языке с надлежащей поддержкой для него (*), но когда вам нужно определить класс MutableInt только для связи между двумя методами, что-то, безусловно, неверно.

Решение для примера, которое вы отправили, состоит в том, чтобы вернуть массив из двух целых чисел и сбой сигнала с помощью либо возврата null, либо исключения. Это не всегда будет работать, поэтому иногда вам нужно...

  • установить атрибуты для текущего объекта (когда используются два метода внутри класса);
  • передать массив целых чисел, который метод может изменить (когда много целых чисел передаются несколько раз);
  • создайте вспомогательный класс, скажем Result, который инкапсулирует результат вычисления (когда вы имеете дело с int и float вместо двух ints) и, возможно, имеет фактическое вычисление как метод или конструктор;
  • используйте предложенную вами идиому, но затем подумайте об использовании поддержки в Apache Commons или другой хорошей библиотеке.

(*) Просто плохой дизайн языка. В Python или Go вы возвращаете несколько значений и перестаете беспокоиться.

Ответ 2

Объектно-ориентированное программирование выполняется лучше всего, если вы структурируете свой код в чистые, понятные абстракции.

Числа, как абстракция, неизменяемы и имеют идентификатор (т.е. "пятерка" всегда "пятая", и нет такой вещи, как "multiple экземпляры пяти" ).

То, что вы пытаетесь изобрести, - это "изменяемое число", которое является изменяемым и имеет личность. Эта концепция немного громоздка, и вам, вероятно, будет лучше с моделированием вашей проблемы с более значимыми абстракциями (объектами), чем это.

Подумайте об объектах, которые представляют что-то и имеют определенный интерфейс, а не в отдельных кусках значений.

Ответ 3

Передача объектов значения по ссылке в общем случае плохой дизайн.

Существуют определенные сценарии, для которых он действителен, например, для замены позиций массива для операций сортировки с высокой производительностью.

Существует очень мало причин, по которым вам потребуется эта функциональность. В С# использование ключевого слова OUT обычно является недостатком и само по себе. Есть несколько приемлемых способов использования ключевого слова out, например DateTime.TryParse(datetext, out DateValue), но стандартное использование выходных параметров - плохой дизайн программного обеспечения, который хочет в целом подражать плохой практике использования флагов для статуса для всего.

Ответ 4

Плохой дизайн, в котором вы участвуете, заключается в использовании класса MutableInteger. 2 всегда будет 2. Завтра будет 2.

Стандартный шаблон Java/OO обычно позволяет таким элементам быть экземпляром класса и позволить этому классу работать и управлять им.

Далее вверх AtomicInteger. Опять же, у меня никогда не было ситуации, в которой мне нужно было пройти ее, но если вы не захотите реорганизовать много кода (ваш вопрос был "хорошей практикой", поэтому мне пришлось тяжело на вас), это лучше вариант. Причина в том, что если вы позволяете целому числу бежать к другой функции, с точки зрения инкапсуляции, вы не знаете, что другая функция будет работать в одном потоке. Как таковой concurrency, и вам, вероятно, понадобится concurrency, предоставляемый атомными ссылочными классами. (Также см. AtomicReference.)

Ответ 5

Метод, изменяющий var в стеке вызывающего, может быть довольно запутанным.

В идеале язык должен поддерживать возврат нескольких значений, что позволит решить такие проблемы.

Но перед этим, если вам нужно использовать параметр "out", вам нужно.

Ответ 6

Конечно, это зависит от конкретной проблемы, которую вы обрабатываете, но я бы сказал, что в большинстве случаев, если вам нужна такая функция, ваш дизайн не очень объектно-ориентированный.
Что вы пытаетесь достичь? Если эти числа a и b должны работать вместе, возможно, они принадлежат классу MyClass, и вам нужен метод экземпляра. Что-то вроде:

class MyClass{
     private int a;
     private int b;
     //getters/setters/constructors
     public boolean dothings(){
     // perform some computation on a and b here
        if (success)
             return true;
        else return false;
        //why not just return success?
     } 

}

Ответ 7

Является ли "пропуск по ссылке" плохим дизайном?

Не в общем. Вам нужно понять ваш конкретный сценарий и спросить себя, что делает функция. И вам нужно правильно определить свой стиль кодирования, особенно если вы кодируете другие (распространяете библиотеки).

Передача по ссылке обычно выполняется, когда ваша функция возвращает несколько выходов. Часто рекомендуется возвращать объект ResultContainer, который содержит всю информацию, возвращаемую вашей функцией. Возьмите следующий пример С#:

bool isTokenValid(string token, out string username)

VS

AuthenticationResult isTokenValid(string token)

class AuthenticationResult {
    public bool AuthenticationResult;
    public string Username;
}

Разница заключается в том, что параметр с ссылкой (в данном случае out put) четко указывает, что он может использоваться только для проверки токена или, при необходимости, для извлечения информации о пользователе. Поэтому, даже если вы обязаны передать параметр, вы можете уничтожить его, если он вам не нужен. Второй пример - более подробный код.

Конечно, второй дизайн предпочтительнее, если у вас есть такой метод

bool doSomething(object a, ref object b, ref object c, ref object d, ... ref object z);

Потому что вы все их переносите в контейнер.

Позвольте мне уточнить: в Java и С# не-примитивные типы всегда передаются как клонированные ссылки. Это означает, что object не клонируются, но только ссылка на них клонируется в стек, а затем вы не можете ожидать, что будете указывать на совершенно другой объект после возврата. Вместо этого вы всегда ожидаете, что состояние объекта будет изменено с помощью метода. В противном случае вы просто clone() объект и voilà.

Итак, вот трюк: MutableInteger, или лучше шаблон Holder, является решением для передачи примитивных значений по ссылке.

В настоящее время он используется компиляторами CORBA idl2java, когда ваш IDL имеет ссылочный параметр.

В вашем конкретном случае я не могу ответить на ваш вопрос о хорошем или плохом дизайне, потому что метод, который вы показали, является слишком общим. Поэтому подумайте об этом. Так же, как вход, если бы у меня была какая-то функция пост-обработки, примененная к мультимедийной информации, даже как шифрование, я бы использовал ссылку. Для меня следующее выглядит неплохой дизайн

encrypt(x);

VS

x = encrypt(x);

Ответ 8

Является ли "pass by reference" вообще плохим дизайном? Как мне изменить свой образ мышления? Просто не нужно делать POJO bean для хранения ваших значений, отправить эту функцию bean в функцию и вернуть новые значения. Конечно, для некоторых случаев может быть просто функция, которую ваш вызов возвращает значение (если его всегда только одна вещь, которую вы хотите вернуть, но вы говорите об этом, поэтому я думаю, что ее более одного).

Традиционно создайте bean, у которого есть свойства, которые нуждаются в изменении примера:

class MyProps{
int val1;
int val2;//similarly can have strings etc here
public int getVal1(){
return val1;
}
public void setVal1(int p){ 
val1 = p;
}// and so on other getters & setters

}

Или может сделать класс с дженериками для хранения любого объекта

class TVal<E>{
E val;
public E getValue(){
return val;
}
public void setValue(E p){
val = p;
}
}

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

public class UseIt{
    void a (){
      TVal<Integer> v1 = new TVal<Integer>();
      v1.setValue(1);//auto boxed to Integer from int
      TVal<Integer> v2 = new TVal<Integer>();
      v2.setValue(3);
      process(v1,v2);
      System.out.println("v1 " + v1 + " v2 " + v2);
    }

    void process(TVal<Integer> v,TVal<Integer> cc){
        v.setValue(v.getValue() +1);
        cc.setValue(cc.getValue() * 2);
    }
}