Являются ли геттеры и сеттеры плохими? Противоречивые советы

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

После быстрого просмотра моего кода большинство из них было Getters and Setters (60%) по сравнению с остальными, которые действительно необходимы для логики игры.

Несколько поисков Google утверждали, что Getters и Setters являются злыми, в то время как другие утверждали, что они необходимы для хорошей практики OO и отличных программ.

Так что мне делать? Что это должно быть? Должен ли я менять свои Getters и Setters для моих личных переменных, или я должен придерживаться их?

Ответ 1

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

// Game
private int score;
public void setScore(int score) { this.score = score; }
public int getScore() { return score; }
// Usage
game.setScore(game.getScore() + ENEMY_DESTROYED_SCORE);

он должен быть

// Game
private int score;
public int getScore() { return score; }
public void addScore(int delta) { score += delta; }
// Usage
game.addScore(ENEMY_DESTROYED_SCORE);

Это, возможно, немного простой пример. То, что я пытаюсь сказать, заключается в том, что обсуждение getter/setters vs public fields часто скрывает большие проблемы с объектами, которые манипулируют внутренним состоянием друг друга в интимной манере и, следовательно, слишком тесно связаны.

Идея состоит в том, чтобы делать методы, которые непосредственно делают то, что вы хотите сделать. Примером может служить то, как установить "живой" статус врага. Возможно, у вас возникнет соблазн иметь метод setAlive (boolean alive). Вместо этого вы должны иметь:

private boolean alive = true;
public boolean isAlive() { return alive; }
public void kill() { alive = false; }

Причиной этого является то, что если вы измените реализацию, что у вещей больше нет "живого" логического, а скорее значения "hit points", вы можете изменить это, не нарушая контракт двух методов, которые вы написали ранее:

private int hp; // Set in constructor.
public boolean isAlive() { return hp > 0; } // Same method signature.
public void kill() { hp = 0; } // Same method signature.
public void damage(int damage) { hp -= damage; }

Ответ 2

  • Очень зло: публичные поля.
  • Несколько зла: Getters и сеттеры, где они не требуются.
  • Хорошо: Getters и seters только там, где они действительно требуются, - делают тип открытым "более крупным" поведением, которое использует его состояние, а не просто рассматривает тип как хранилище состояния, которое должно обрабатываться другими типами.

Это действительно зависит от ситуации - иногда вам действительно нужен немой объект данных.

Ответ 3

У вас уже было много хороших ответов, поэтому я просто дам свои два цента. Геттеры и сеттеры очень, очень злые. Они, по сути, позволяют вам притворяться, что скрывают внутренние объекты вашего объекта, когда большую часть времени все, что вы сделали, бросается в избыточный код, который не скрывает внутреннего состояния. Для простого POJO нет причин, по которым getName() и setName() не могут быть заменены obj.name = "Tom".

Если вызов метода просто заменяет назначение, то все, что вы получили, предпочитая вызов метода, - это раздувание кода. К сожалению, язык закрепил использование геттеров и сеттеров в спецификации JavaBeans, поэтому программисты Java вынуждены использовать их, даже если это не имеет никакого смысла.

К счастью, Eclipse (и, возможно, другие IDE) также позволяет автоматически генерировать их. И для веселого проекта я когда-то построил для них генератор кода в XSLT. Но если есть одна вещь, которую я бы избавился от Java, ее чрезмерная зависимость от геттеров и сеттеров.

Ответ 4

Getters и seters применяют концепцию encapsulation в объектно-ориентированном программировании.

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

Есть несколько преимуществ для наличия геттеров и сеттеров:

1. Разрешение будущих изменений без изменения кода, использующего модифицированный класс.

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

Например, скажем, существует метод setValue, который устанавливает value приватную переменную в объекте:

public void setValue(int value)
{
    this.value = value;
}

Но тогда было новое требование, которое необходимо было отслеживать количество раз value. С установкой на месте, изменение довольно тривиально:

public void setValue(int value)
{
    this.value = value;
    count++;
}

Если поле value было общедоступным, нет простого способа вернуться позже и добавить счетчик, который отслеживает количество раз, когда значение было изменено. Поэтому наличие геттеров и сеттеров - один из способов "будущего доказательства" класса для изменений, которые могут произойти позже.

2. Обеспечение средств, с помощью которых можно манипулировать объектом.

Другим способом использования getters и seters является обеспечение того, как можно манипулировать объектом, поэтому объект контролирует свое собственное состояние. При наличии открытых переменных объекта, он может быть легко поврежден.

Например, объект ImmutableArray содержит массив int, называемый myArray. Если массив был открытым полем, он просто не будет неизменным:

ImmutableArray a = new ImmutableArray();
int[] b = a.myArray;
b[0] = 10;      // Oops, the ImmutableArray a contents have been changed.

Чтобы реализовать действительно неизменяемый массив, должен быть записан геттер для массива (метод getArray), чтобы он возвращал копию своего массива:

public int[] getArray()
{
    return myArray.clone();
}

И даже если происходит следующее:

ImmutableArray a = new ImmutableArray();
int[] b = a.getArray();
b[0] = 10;      // No problem, only the copy of the array is affected.

ImmutableArray действительно неизменен. Отображение переменных объекта позволит манипулировать им способами, которые не предназначены, но только разоблачение определенных способов (геттеры и сеттеры), объект можно манипулировать по-разному.

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

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

Ответ 5

Они абсолютно злы.

@coobird, к сожалению, они абсолютно не "применяют концепцию инкапсуляции", все, что они делают, заставляет вас думать, что вы инкапсулируете данные, когда на самом деле вы подвергаете данные через свойство с заблуждениями о величии метода. Все, что делает геттер/сеттер, создает публичное поле.

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

object.field = value;

вместо более интенсивного познавательного

object.setField(value);

где клиент должен теперь проверить метод getter/setter, чтобы увидеть, есть ли у него какие-либо побочные эффекты.

Во-вторых, если вам действительно нужно сделать что-то еще в методе, зачем называть его методом get/set, когда он получает больше обязанностей, чем просто получать или устанавливать? Либо следуйте за SRP, либо вызовите метод, который на самом деле скажет вам, что метод whole действительно похож на примеры Zarkonnen, о которых он упомянул, например.

public void kill(){
    isAlive = false;
    removeFromWorld(this);
}

вместо

public void setAlive(boolean isAlive){
    this.isAlive = isAlive;
    if (isAlive)
        addToWorld(this);
    else
        removeFromWorld(this);
}

где метод setAlive (boolean) сообщает клиенту, что в качестве побочного эффекта он удалит объект из мира? Почему клиент должен знать о поле isAlive? Плюс, что происходит, когда объект снова добавляется в мир, должен ли он быть повторно инициализирован? почему клиент будет заботиться об этом?

ИМХО морально назвать методы, чтобы точно сказать, что они делают, следовать за СРП и избавляться от геттеров/сеттеров. Если есть проблемы без геттеров/сеттеров, скажите объектам делать свою собственную грязную работу внутри своего собственного класса, а не пытаться делать с ними что-то в других классах.

здесь заканчивается мое разглашение, извините за это;)

Ответ 6

Это скользкий склон.

Простой объект Transfer (или объект Parameter) может иметь единственную цель - удерживать некоторые поля и предоставлять их значения по требованию. Однако даже в этом вырожденном случае можно утверждать, что объект должен быть неизменным - сконфигурирован в конструкторе и подвержен только методам get....

Также существует случай класса, который предоставляет некоторые "ручки управления"; ваш автомобильный радиоинтерфейс, вероятно, можно понимать как раскрытие чего-то вроде getVolume, setVolume, getChannel и setChannel, но его реальная функциональность - получение сигналов и испускание звука. Но эти регуляторы не раскрывают много деталей реализации; вы не знаете из этих функций интерфейса, являются ли радиостанции транзисторами, главным образом программным обеспечением или вакуумными лампами.

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

Итак... "зло"? На самом деле, нет. Но каждый раз, когда вы склонны вставлять значение и выставляете методы get... и set... для этого значения, спросите себя, почему и какова реальность этой объектности. Если единственный ответ, который вы можете дать самому себе: "Чтобы сохранить это значение для меня", возможно, что-то помимо OO происходит здесь.

Ответ 7

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

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

Ответ 8

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

Ответ 9

Наличие геттеров и сеттеров имеет тенденцию указывать ( "запах", если вы занимаетесь таким языком начальной школы), что существует проблема дизайна. Тривиальные геттеры и сеттеры едва различимы от общественных полей. Обычно код, работающий с данными, будет в другом классе - плохая инкапсуляция, и то, что вы ожидаете от программистов, несовместимых с OO.

В некоторых случаях геттеры и сеттеры в порядке. Но, как правило, тип с геттерами и сеттерами указывает на проблемы с дизайном. Getters работают для неизменности; сеттеры работают для "скажите, не спрашивайте". И неизменность, и "сказать не спрашивайте" - это хороший выбор дизайна, если они не применяются в стиле с перекрытием.

Ответ 10

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

Один пример, который я прочитал выше, был future-proofing вашего кода. Например:

public void setValue(int value)
{
    this.value = value;
}

Затем требования меняются, и вам нужно отслеживать, сколько раз было установлено значение.

Итак:

public void setValue(int value)
{
    this.value = value;
    count++;
}

Это красиво. Я понял. Однако, в Ruby, не будет ли следующая цель не использоваться?

someobject.my_value = 100

Позже вам нужно отследить количество раз my_value. Итак, не могли бы вы просто переопределить сеттер THEN и только THEN?

def my_value=(value)
    @my_value = value
    @count++
end

Я все для красивого кода, но я должен признать, что просматриваю горы классов Java, которые у нас есть, и вижу буквально тысячи и тысячи строк кода, которые НИЧЕГО, но базовые геттеры/сеттеры уродливы и раздражают.

Когда я развивался в С# на полный рабочий день, мы все время использовали общедоступные свойства и делали пользовательские геттеры/сеттеры только тогда, когда это было необходимо. Работал как шарм, и он ничего не сломал.

Ответ 11

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

Одним из преимуществ использования сеттеров является то, что проверки должны выполняться только в одном месте в вашем коде.

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

Ответ 12

Это зависит от языка программирования. Ваш вопрос сформулирован в контексте Java, где кажется, что геттеры и сеттеры обычно считаются хорошими.

Напротив, в мире Python они обычно считаются плохим стилем: они добавляют строки к коду без фактического добавления функциональности. Когда программистам Python необходимо, они могут использовать метапрограммирование, чтобы поймать получение и/или настройку атрибутов объекта.

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

(Это не делает Python обязательно лучше, чем Java, просто отличается.)

Ответ 13

Просто FYI: В дополнение ко всем отличным ответам в этой теме помните, что из всех причин, по которым вы можете придумать или против геттеров/сеттеров, производительность не одна (как некоторые могут поверить). JVM достаточно умен, чтобы встроить тривиальные геттеры/сеттеры (даже не теги final, если они фактически не переопределены).

Ответ 14

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

Ответ 15

Если вам нужен внешний доступ к отдельным значениям полей, используйте геттеры и/или сеттеры. Если нет, не делайте этого. Никогда не используйте общедоступные поля. Это так просто! (Хорошо, это никогда не бывает так просто, но это хорошее эмпирическое правило).

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

Ответ 16

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

получайте удовольствие:)