Должны ли частные функции модифицировать переменную поля или использовать возвращаемое значение? - PullRequest
1 голос
/ 19 июня 2010

Я часто сталкиваюсь с одной и той же мыслью, когда создаю приватные методы: какое приложение должно модифицировать (обычно инициализировать) существующую переменную в области действия класса. Я не могу решить, какой из следующих двух методов я предпочитаю.

Допустим, у нас есть класс Test с переменной поля x. Пусть это будет целое число . Как вы обычно модифицируете / инициализируете x?

a) Изменение поля напрямую

private void initX(){
    // Do something to determine x. Here its very simple.
    x = 60;
}


б) Использование возвращаемого значения

private int initX(){
    // Do something to determine x. Here its very simple.
    return 60;
}


А в конструкторе:

public Test(){
    // a)
    initX();
    // b)
    x = initX();
}


Мне нравится, что в b) ясно, с какой переменной мы имеем дело. Но, с другой стороны, a) кажется достаточным в большинстве случаев - название функции прекрасно отражает то, что мы делаем!

Какой из них вы предпочитаете и почему?

Спасибо за ваши ответы, ребята! Я сделаю это вики-сообществом, так как понимаю, что на это нет правильного ответа.

Ответы [ 6 ]

1 голос
/ 19 июня 2010

Нет?

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

Что более важно, что означает 60?

Если это значимое значение, сделайте его const со значимым именем: NUMBER_OF_XXXXX, MINUTES_PER_HOUR, FIVE_DOZEN_APPLES, SPEED_LIMIT, ... независимо от того, как и где вы впоследствии будете его использовать (конструктор, метод init или функция-получатель).

Если сделать его именованной константой, это значение будет использоваться повторно само по себе,И использование const гораздо более «доступно», особенно для более вездесущих значений (например, 1 или -1), чем использование фактического значения.

Только если вы хотите связать это значение const с конкретным классом, это будетимеет смысл создать класс const или var, или - если язык не поддерживает их, - функцию класса getter.

Еще одна причина сделать ее (виртуальной) функцией getter была бы, если нужны классы-потомкивозможность начать с другого начального значения.


Редактировать (в ответ на комментарии):

Для инициализаций, которые включают сложные вычисления, я бы также извлек метод для вычисления,Выбор того, чтобы сделать этот метод процедурой, которая непосредственно изменяет значение поля (a) или функцию, которая возвращает значение, которое ему следует присвоить (b), будет зависеть от вопроса, понадобятся ли вычисления в другое время, кроме"просто конструктор".

Если бы это было необходимо только при инициализации в конструкторе, я бы предпочел метод (а).

Если вычисление необходимо выполнить и в другое время, я бывыберите метод (b), поскольку он также позволяет присваивать результат некоторому другому полю или локальной переменной и может использоваться потомками или другими пользователями класса, не влияя на внутреннее состояние экземпляра.

1 голос
/ 19 июня 2010

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

С опцией а) у новых, ленивых или просто загруженных временем разработчиков возникает соблазн начать добавлять небольшие дополнительные задачи в метод initX вместо создания новой.

Кроме того, в b) вы можете удалить initX () из определения класса, чтобы потребители объекта даже не знали, что он там. Например, в C ++.

В шапке:

class Test {
private:    int X;
public:    Test();
...
}

В файле CPP:

static int initX() { return 60; }

Test::Test() {
    X = initX();
}

Удаление функций init из заголовочного файла упрощает класс для людей, которые должны его использовать.

1 голос
/ 19 июня 2010

Для инициализации я предпочитаю инициализацию конструктора, если это возможно, public Test():x(val){...}, или пишите код инициализации в теле конструктора.Конструктор - лучшее место для инициализации всех полей (на самом деле, это цель конструктора).Я бы использовал private initX() подход, только если код инициализации для X слишком длинный (только для удобства чтения) и вызвал бы эту функцию из конструктора.private int initX() по моему мнению не имеет ничего общего с инициализацией (если вы не реализуете отложенную инициализацию, но в этом случае она должна возвращать &int или const &int), это средство доступа.

1 голос
/ 19 июня 2010

Я обычно предпочитаю b) , только я выбираю другое имя, например computeX() в данном случае.Вот несколько причин, почему:

  • , если я объявлю computeX() как protected, у подкласса есть простой способ повлиять на его работу, но сам x может остаться private field;
  • Мне нравится объявлять поля final, если они есть;в этом случае a) не вариант, так как инициализация должна происходить в компиляторе (это зависит от Java, но все ваши примеры выглядят также как Java).

Это говоритУ меня нет сильных предпочтений между этими двумя методами.Например, если мне нужно инициализировать несколько связанных полей одновременно, я обычно выбираю опцию a) .Это, однако, только если я не могу или не хочу по какой-то причине инициализировать непосредственно в конструкторе.

0 голосов
/ 19 июня 2010

@ Фредерик, если вы используете опцию b) и у вас есть МНОГИЕ переменные поля, конструктор станет довольно громоздким блоком кода. Иногда вы просто не можете помочь, но в классе есть много-много переменных-членов (пример: это объект домена, и его данные поступают прямо из очень широкой таблицы в базе данных). Наиболее прагматичным подходом будет модульный код, как вам нужно.

0 голосов
/ 19 июня 2010

На самом деле только a) метод ведет себя так, как ожидалось (анализируя имя метода). Метод б) должен называться return60 в вашем примере или getXValue в более сложном.

На мой взгляд, оба варианта верны. Все зависело от вашего намерения, когда был выбран определенный дизайн. Если ваш метод должен выполнять только инициализацию, я бы предпочел a), потому что это проще. В случае, если значение x также используется для чего-то еще где-то в логике, использование опции b) может привести к более согласованному коду.

Вы также должны всегда четко писать имена методов и делать эти имена соответствующими реальной логике. (в этом случае метод b) имеет запутанное имя).

...