Какие есть альтернативы публичным полям? - PullRequest
13 голосов
/ 01 января 2011

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

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

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

Итак, мой вопрос: каковы альтернативы? или мне действительно нужно использовать приватные поля с геттерами и сеттерами?

Для справки приведу один из моих классов и некоторые его методы.

Я уточню больше, если потребуется.

public double health;
//The player's fields.
public String name;
public double goldCount;
public double maxWeight;
public double currentWeight;
public double maxBackPckSlts;
public double usedBackPckSlts; // The current back pack slots in use
public double maxHealth; // Maximum amount of health
public ArrayList<String> backPack = new ArrayList<String>();

//This method happens when ever the player dynamically takes damage(i.e. when it is not scripted for the player to take damage.
//Parameters will be added to make it dynamic so the player can take any spread of damage.
public void beDamaged(double damage)
{
    this.health -= damage;
    if (this.health < 0)
    {
        this.health = 0;
    }
}

РЕДАКТИРОВАТЬ: Для проверки вот как выглядит мой класс Weapon: (Пример кода по какой-то причине не работает, поэтому он выглядит неправильно.)

private final double DAMAGE;
private final double SPEED;

public Weapon(double initialDmg,double initialSpd,String startName,double initialWg)
{
    DAMAGE = initialDmg;
    SPEED = initialSpd;
    setItemName(startName);
    setItemWeight(initialWg);
}

public double getSpeed() 
{
    return SPEED;
}


public double getDamage()
{
    return DAMAGE;
}

Как видите, поскольку Weapon's DAMAGE и SPEED не нужно менять, они могут быть окончательными на данный момент. (если позже в игре я решил, что эти значения можно «обновить», я могу добавить сеттеры затем с проверкой или просто создать новое оружие с улучшенными значениями) Они устанавливаются в конструкторе Weapon's .

Вывод: геттеры и сеттеры в порядке, если они используются грамотно и используются только при необходимости. (Однако)

Ответы [ 12 ]

21 голосов
/ 01 января 2011

Распространено использовать геттеры и сеттеры вместо того, чтобы давать другим объектам разрешение на непосредственное изменение ваших полей.Это может не иметь никакого смысла, если вы видите, что 99,99% ваших методов получения и установки не делают ничего, кроме того, что вы могли бы сделать с прямым доступом к полям.Но что происходит, когда вы решаете, что, когда игроку нанесен урон, превышающий очко, он бросает половину своего инвентаря?Или вы хотите ограничить количество мест для рюкзака, которые могут использовать магические предметы?Вы должны либо выследить все места в вашем коде, где вы изменяете поля, либо, если вы использовали методы получения и установки, вы вносите изменения полностью в классе.В этом суть объектно-ориентированного программирования - то, что вы инкапсулировали «знание» о том, что объект делает внутри самого объекта, а не распространили его среди всех объектов, которые взаимодействуют с этим объектом.

18 голосов
/ 01 января 2011

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

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

Я думаю, что ваш код уже начинает использовать инкапсуляцию.

Давайте посмотрим на код

Давайте посмотрим на метод beDamaged.

public void beDamaged(double damage)
{
    this.health -= damage;

    if (this.health < 0)
    {
        this.health = 0;
    }
}

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

Давайте сделаем вывод об игроке

Теперь, исходя из вышесказанного, я думаю, что могу сделать вывод об объекте игрока:

* Игрок health не может быть отрицательным числом.

Всегда ли то, что мы сделали, верно?

Давайте посмотрим, может ли это быть верным из предоставленного вами кода.

Ага!У нас есть небольшая проблема:

public double health;

С полем health, равным public, внешний мир может напрямую манипулировать полем, чтобы поместить состояние объекта игрока в то, что, вероятно, нежелательнос помощью некоторого кода, подобного следующему:

Player player = new Player();
player.health = -100

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

Что мы можем с этим сделать?

Как этого можно было избежать?- при наличии поля health private.

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

Что также означает это - когда вы делаете поле private, это не означает автоматически, что вы должны делать геттеры и сеттеры для поля.

Приватные поля не требуют геттеров и сеттеров

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

8 голосов
/ 01 января 2011

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

В противном случае оставьте их как приватные поля и просто не предоставьте метод получения / установки.

Существуют различные причины, по которым это лучший метод:

  1. Если клиенты используют ваше поле напрямую, а потом что-то должно измениться, вы застряли. С помощью геттера вы можете сделать многое, прежде чем получите доступ к полю.
  2. Существует нечто, называемое спецификацией JavaBeans, которое требует использования getter / setters. Без них ваш класс (тогда называемый bean) не будет взаимодействовать с этим. EL JSP и JSF - один из примеров того, что ваш класс должен соответствовать стандартам JavaBeans.

(p.s. не имеет отношения к вашему вопросу, но вам лучше не объявлять backPack как ArrayList. Объявить как List; код для интерфейса, а не для реализации)

4 голосов
/ 01 января 2011

Если у вас есть приватное поле с методом get() и методом set(), которые не делают ничего, кроме получения и присвоения значения, вы должны просто сделать поле общедоступным, так как поле на самом деле не является частный, а геттеры и сеттеры только ухудшают производительность. Если получатели и установщики проверяют устанавливаемое значение или если разрешено извлекать значение, тогда продолжайте и используйте получатели и установщики. например Если у вас есть переменная private int width;, и кто-то пытается вставить -1 с помощью установщика, и установщик следит за тем, чтобы он не был отрицательным, тогда это хорошее применение. Например:

private int width;
public int get(){
    return width;
}
public void set(int w){
    if (w < 0) throw new RuntimeException();
    else width = w;
}

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

Короче говоря, коротко:

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

т.е.

BAD:

private int width;
public int get(){
    return width;
}
public void set(int w){
    width = w;
}

GOOD

private int width;
public int get(){
    return width;
}
public void set(int w){
    if (w < 0) throw new RuntimeException();
    else width = w;
}

ХОРОШО, если вы не хотите ничего, кроме получения или установки:

public int width;
3 голосов
/ 01 января 2011

Об этом:

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

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

Для каждого поля, которое у вас есть, вы должны проверить:

а) нужен ли ему Геттер (другим классам нужно знать значение этого поля)
б) нужен ли ему сеттер (нужно ли другим классам изменять значение этого поля)
c) или поле должно быть неизменным (окончательным), если это так, оно должно быть инициализировано во время определения или в конструкторе (и, очевидно, оно может не иметь установщика)

Но вы вряд ли когда-либо (исключение: объекты-значения) предполагаете, что все закрытые поля будут иметь геттеры и сеттеры, и пусть ваша IDE сгенерирует их все.

2 голосов
/ 01 января 2011

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

1 голос
/ 01 января 2011

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

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

1 голос
/ 01 января 2011

Укороченная версия ваших методов ...

public void beDamaged(double damage) {
    health = Math.max(0, health-damage);
}

public void gainHealth(double gainedHp) {
    health = Math.min(maxHealth, health + gainedHp);
}

или даже следующие, которые можно вызвать с +1 к усилению, -1 к потере 1 л. С.

public void adjustHealth(double adjustHp) {
    health = Math.max(0, Math.min(maxHealth, health + adjustHp));
}
1 голос
/ 01 января 2011

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

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

Вот пример.Рассмотрим класс с именем Point.Если вы решите, что у точки есть открытые атрибуты x и y, вы никогда не сможете изменить это.Напротив, если у вас есть методы get / set X / Y, в последующих версиях класса могут использоваться различные внутренние представления: прямоугольные координаты (x, y), но также полярные (r, theta) и т. Д. Все это без изменения открытыхинтерфейс.

1 голос
/ 01 января 2011

приватных полей, сеттеров и геттеров - это действительно ваш лучший путь.

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

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

...