Как избежать геттеров и сеттеров - PullRequest
25 голосов
/ 23 февраля 2012

Я читал во многих местах, что "добытчики и сеттеры - зло".И я понял, почему так.Но я не знаю, как их полностью избежать.Say Item - это класс, который содержит информацию об имени элемента, количестве, цене и т. Д., А ItemList - это класс, в котором есть список элементов.Чтобы найти общий итог:

int grandTotal()
{
int total = 0;

for (Item item: itemList)
       total += item.getPrice();

return total;
}

В вышеприведенном случае, как можно избежать getPrice ()?Класс Item предоставляет getName, setName и т. Д.

1005 * Как их избежать?

Ответы [ 15 ]

40 голосов
/ 23 февраля 2012

Когда следует использовать геттеры и сеттеры?

Методы получения и установки отлично подходят для настройки или определения конфигурации класса или извлечения данных из модели

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

Вы также можете предоставить геттеры без сеттеров. Они не должны приходить в пары.

Когда не следует использовать геттеры и сеттеры?

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

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

Резюме

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

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

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

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

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

Также обратите внимание:

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

Редактировать: Добавлено резюме
Редактировать 2: правописание

19 голосов
/ 01 марта 2012

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

  • Геттеры и сеттеры (аксессоры) не являются злыми
  • Они "злые" (ненужные) большую часть времени однако
  • Инкапсуляция - это не просто добавление методов доступа вокруг приватных полей для управления изменениями, в конце концов, нет преимущества для добавленных методов get / set, которые просто изменяют приватное поле
  • Вы должны написать как можно больше кода по принципу " Скажи, не спрашивай "
  • Вам нужно , чтобы использовать средства доступа для кода платформы, DTO, сериализации и так далее. Не пытайтесь бороться с этим.
  • Однако вы хотите, чтобы логика вашего основного домена (бизнес-объекты) была максимально свободной от собственности. Вы должны указывать объектам делать вещи, а не проверять их внутреннее состояние по желанию.

Если у вас есть множество аксессоров, вы существенно нарушаете инкапсуляцию. Например:

class Employee
{
   public decimal Salary { get; set; }

   // Methods with behaviour...
}

Это объект домена дерьма, потому что я могу сделать это:

me.Salary = 100000000.00;

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

Лучший объект будет:

class Employee
{
   private decimal salary;

   public void GivePayRise()
   {
        // Should this employee get a pay rise.
        // Apply business logic - get value etc...
        // Give raise
   }

   // More methods with behaviour
}

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

13 голосов
/ 23 февраля 2012

Следующий пример - блестящий пример установщиков и сборщиков шаблонов.

class Item{
  private double price;

  public void setPrice(final double price){
    this.price = price;
  }
  public double getPrice(){
    return this.price;
  }
}

Некоторые кодеры считают, что это называется инкапсуляцией, но на самом деле этот код является точным эквивалентом

class Item{
  public double price;
}

В обоих классах price не защищен и не инкапсулирован, но второй класс читается легче.

 class Item{
    private double price;

    public void setPrice(final double price){
      if(isValidPrice(price))
        this.price = price;
      else throw new IllegalArgumentException(price+" is not valid!");
    }

    public double getPrice(){
      return this.price;
    }
  }

Это реальная инкапсуляция, инвариант класса защищен setPrice. Мой совет - не пишите фиктивные геттеры и сеттеры, используйте геттеры и сеттеры, только если они охраняют инвариант вашего класса

6 голосов
/ 23 февраля 2012

Я читал во многих местах, что "добытчики и сеттеры - зло".

В самом деле? Это звучит безумно для меня. Много? Покажите нам один. Мы разорвем его на куски.

И я понял, почему так.

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

Но я не знаю, как их полностью избежать.

Вы не должны.

как избежать getPrice?

Видите, почему вы хотите этого избежать? Как еще вы предполагаете получать данные из ваших объектов?

как их избежать ???

Не. Хватит читать сумасшедшие разговоры.

4 голосов
/ 15 декабря 2015

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

Добытчики

Они злые?В коде нет такого понятия, как зло .Код есть код, и он не является ни хорошим, ни плохим.Это просто вопрос того, насколько сложно читать и отлаживать.

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

«Злой»

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

Иногда люди используют геттеры так:

if(item.getPrice() <= my_balance) {
   myBank.buyItem(item);
}

В этом коде нет ничего плохого, но он не так прост, как мог бы быть.Посмотрите на это (более прагматичный подход):

myBank.buyItem(item); //throws NotEnoughBalanceException

Проверять цену товара при покупке чего-либо - не работа покупателей или кассиров.Это на самом деле работа банка.Представьте, что у клиента A есть SimpleBank.java

public class SimpleBank implements Transaction {

  public void buyItem(Item item){
     if(getCustomer().getBalance() >= item.getPrice()){
        transactionId = doTransaction(item.getPrice());
        sendTransactionOK(transactionId);
     }
  }
}

Первый подход здесь, кажется, хорошо.Но что, если у клиента B есть NewAndImprovedBank.java?

public class NewAndImprovedBank implements Transaction {

  public void buyItem(Item item){
     int difference = getCustomer().getBalance() - item.getPrice();
     if (difference >= 0) {
        transactionId = doTransaction(item.getPrice());
        sendTransactionOK(transactionId);
     } else if (difference <= getCustomer().getCreditLimit()){
        transactionId = doTransactionWithCredit(item.getPrice());
        sendTransactionOK(transactionId);
     }
  }
}

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

Заключение

Не спрашивайте разрешения ака item.getPrice(), вместо этого попросите прощения ака NotEnoughBalanceException.

3 голосов
/ 23 февраля 2012

getPrice () обращается к закрытой переменной, которую я предполагаю.

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

total += item.price;

Однако это обычно считается плохим стилем . Переменные класса должны обычно оставаться закрытыми.

Пожалуйста, смотрите мой комментарий к вопросу.

2 голосов
/ 03 октября 2014

'Зло' как .getAttention ()

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

На самом деле все, что я прочитал, несет в себе эту тему.

Как эта тема относится к классу Item ?

Взгляд в будущееof Item

Вот класс предметов фикций:

class Item{
    private double price;

    public void setPrice(final double price){
      if(isValidPrice(price))
        this.price = price;
      else throw new IllegalArgumentException(price+" is not valid!");
    }

    public double getPrice(){
      return this.price;
    }
  }

Это все хорошо и хорошо, но это все еще «Зло» в том смысле, что это может причинить вам много горя вбудущее.

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

Это намного лучше (дажеХорошо, возможно) передать объект Price вместо double.Тем самым вы можете легко вносить изменения в то, что вы подразумеваете под «ценой», не нарушая существующие интерфейсы.

Вывод на получение и установщик

Если вы обнаружите, что используете средства получения и установки на простыетипы, убедитесь, что вы учитываете возможные будущие изменения интерфейса.Есть очень хороший шанс, что вы не должны быть.Вы используете setName(String name)?Вы должны учитывать setName(IdentityObject id) или даже setIdentity(IdentityObject id) на случай, если появятся другие идентификационные модели (аватары, ключи, что угодно).Конечно, вы всегда можете обойтись и setAvatar и setKey во всем, но, используя объект в сигнатуре вашего метода, вы упростите в будущем расширение до объектов, которые могут использовать новые свойства идентификатора и не разрушать устаревшееобъекты.

2 голосов
/ 09 марта 2013

Как избежать геттеров и сеттеров? Классы проектирования, которые фактически воздействуют на данные, которые они содержат.

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

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

2 голосов
/ 24 февраля 2012

Как избежать геттеров и сеттеров в Java?Используйте Project Lombok

1 голос
/ 24 сентября 2014

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

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

OrderLine будет иметь Item и qty в качестве полей.

После этого, кодируйте что-то вроде calcTotal (int qty) в Item, которое возвращает цену * qty. Создайте метод в OrderLine, который вызывает Calculate (qtyOrdered)

Передать возвращаемое значение в itemList.

Таким образом, вы избегаете добытчиков. ItemList будет знать только общую стоимость. Ваш код должен соответствовать вашим данным. Попросите Объект, у которого есть данные, рассчитать totalPrice вместо того, чтобы запрашивать у этого объекта необработанные данные для расчета вашей totalPrice.

...