Геттеры и сеттеры плохой дизайн? Противоречивые советы [дубликат]

этот вопрос уже есть ответ здесь:

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

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

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

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

16 ответов


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

// 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);

он должен быть!--5-->

// 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; }

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

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; }

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

это действительно зависит от ситуации - иногда действительно do просто хочу тупой объект данных.


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

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

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


геттеры и сеттеры применить концепцию инкапсуляция в объектно-ориентированном программировании.

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

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

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

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

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

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

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

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

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

еще один способ геттеров и сеттеров пригодится, чтобы обеспечить способы управления объектом, поэтому объект контролирует свое собственное состояние. С открытые переменные объекта, он может быть легко поврежден.

например, an


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

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

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

object.field = value;

вместо более интенсивной когнитивно

object.setField(value);

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

во-вторых, если вам действительно нужно сделать что-то еще в методе, зачем называть его методом get/set, когда у него больше обязанностей, чем просто получение или настройка? Либо следуйте SRP, либо вызовите метод something что на самом деле говорит вам, что весь метод делает, как примеры 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? Кроме того, что происходит, когда объект повторно добавляется в мир, должен ли он быть повторно инициализирован? зачем заботу клиента о это?

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

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


это скользкий путь.

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

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

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

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


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

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


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


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

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


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

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

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

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

так:

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

это красиво. Я понял. Однако, в Руби, не служат той же цель?

someobject.my_value = 100

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

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

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

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


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

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

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


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

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

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

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


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


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


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

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


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

удачи :)