Чи повинні методи класу називати власні геттери та сетери?


53

Де я працюю, я бачу багато класів, які роблять такі речі:

public class ClassThatCallsItsOwnGettersAndSetters {

    private String field;

    public String getField() {
        return field;
    }

    public void setField(String field) {
        this.field = field;
    }

    public void methodWithLogic() {
        setField("value");
        //do stuff
        String localField = getField();
        //do stuff with "localField"
    }
}

Якби я написав це з нуля, я б написав methodWithLogic()так:

public class ClassThatUsesItsOwnFields {

    private String field;

    public String getField() {
        return field;
    }

    public void setField(String field) {
        this.field = field;
    }

    public void methodWithLogic() {
        field = "value";
        //do stuff            
        //do stuff with "field"
    }
}

Я відчуваю, що коли клас називає власні геттери та сетери, це робить код важче читати. Для мене це майже означає, що в цьому методі виклику відбувається складна логіка, хоча в нашому випадку це майже ніколи не буває. Коли я налагоджую незнайомий код, хто скаже, що помилка не є побічним ефектом у цьому методі? Іншими словами, це змушує мене багато подорожувати в дорозі розуміння коду.

Чи є переваги першому методу? Чи справді перший метод краще?


Коли я налагоджую незнайомий код, хто скаже, що помилка не є побічним ефектом у цьому методі? Ваші одиничні тести. :)
Джошуа Тейлор

1
Використання геттерів / сеттерів у вашому внутрішньому коді дозволяє створити точку перерви у вашому коді, якщо ви проходите через нього налагоджувачем. Це також дозволяє вам переконатися, що якщо щось не вдається встановити налаштування / отримати значення, ви знаєте, що це через цей метод, а не через якийсь недійсний доступ. Загалом, він передбачає більше коду послідовності.
callyalater

Відповіді:


46

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

Наприклад, що станеться, якщо пізніше ви додасте "брудний" прапор до деяких полів сетера? Зателефонувавши своїм сетерам у свій внутрішній код, ви встановите брудний прапор, не змінюючи іншого коду. У багатьох ситуаціях це було б добре.


А як бути, коли ви ініціалізуєте дані в бекенді, і ви не хочете, щоб це трактувалося як брудне. Якщо ви зателефонували setField()з самого початку, ви просто ввели помилку.
Даніель Каплан

6
Правильно, тому я кажу, що це частково залежить від ситуації. Можливо, ваша ініціалізація даних повинна пропустити сетери, але інший логічний код не повинен. Виберіть правильні правила та дотримуйтесь їх.
Matt S

3
@DanielKaplan: справа в тому, що викликати геттерів та сетерів у більшості випадків - це правильно, і лише в конкретних ситуаціях. Однак у реальному коді, коли ви змінюєте існуючу програму getter / setter-впровадження пізніше, щоб ввести деякі навмисні побічні ефекти, вам, швидше за все, доведеться перевіряти кожен дзвінок до геттера або сеттера всередині класу, а також кожен прямий доступ до поле. Ось чому ви повинні намагатися якомога менше зберігати заняття.
Док Браун

33

Виклик директора безпосередньо сам по собі не є проблемою. Питання справді повинно бути таким: чому наш код скрізь має сетери?

Класи, що змінюються, - це небезпечна гра, особливо там, коли сам клас керує власним станом. Поміркуйте, скільки цих сетерів справді потрібно для існування. Скільки можна встановити в конструкторі, а потім бути повністю інкапсульованим самим класом?

Якщо поле має бути встановлено зовні, то запитайте себе, чи не існує методу з логікою. Чи сам ваш клас насправді лише клас даних / стану? Чи несе цей клас більше ніж одна відповідальність?

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


1
Я повністю погоджуюсь / практикую цю лінію мислення. Я також думаю, що ви довели питання, яке насправді важливіше мого питання. Однак, я не відчуваю, що це прямо відповідає на моє первісне запитання. Якщо ви не говорите, "у великій схемі речей ваше питання не має значення". Що також може бути правдою :)
Даніель Каплан

@DanielKaplan: Я кажу саме це. :) Мене розірвало між відповіддю та коментарем з цим, але я щиро не відчуваю, що є правильна відповідь, яка не задає більшого питання.
пдр

9

Так, методи вашого класу повинні викликати геттерів та сетерів. Вся справа в написанні геттерів і сетерів - це підтвердження в майбутньому. Ви можете зробити кожну властивість полем і безпосередньо викрити дані користувачам класу. Причина, по якій ви будуєте геттери та сетери, не обов'язково полягає в тому, що зараз існує складна логіка, але щоб інтерфейс не зламався в майбутньому, якщо вам доведеться його додати.


1
Я не бачу зв'язку між вашим першим реченням та рештою вашого абзацу. У першому реченні сказано, що клас повинен називати власні геттери та сетери. У решті параграфа пояснюється, чому клієнтський код (тобто код за допомогою класу) повинен використовувати getters та setters замість прямого доступу до полів. Але я не бачу, як це є причиною, щоб клас не мав прямого доступу до власних полів.
Даніель Каплан

1
@DanielKaplan Моя думка, що причини одні і ті ж. Якщо ви додасте логіку сеттера пізніше, це вплине на внутрішній код так само (потенційно), як і зовнішній.
Майкл

2
@Michael: Часто для класу можуть бути вагомі причини не використовувати власні геттери / сетери. Один загальний сценарій - це те, коли існує багато сеттерів форми. someField=newValue; refresh(); Якщо метод дозволяє встановити кілька полів, виклик сетерів записати ці поля спричинить зайві операції "оновлення". Введення всіх полів та refresh()один раз виклик може призвести до більш ефективної та плавної роботи.
supercat

6

Щоб відповісти на ваші запитання одним словом, так.

Наявність класового виклику власних геттерів та сетерів додає розширюваність та забезпечує кращу базу для майбутнього коду.

Скажіть, у вас є щось подібне:

public class Vehicle
{
    private int year;
    private String make;

    public Vehicle(int year, String make)
    {
        setYear(year);
        setMake(make);
    }

    public void setYear(int year)
    {
        this.year = year;
    }

    public void setMake(String make)
    {
        this.make = make;
    }
}

Зателефонувавши сетерам за рік і зробити в даний час, можливо, не буде додано жодної функціональності, але що робити, якщо ви хочете додати налаштування щось на зразок перевірки вводу?

public class Vehicle
{
    private int year;
    private String make;

    public Vehicle(int year, String make)
    {
        setYear(year);
        setMake(make);
    }

    public void setYear(int year)
    {
        if(year > 0)
        {
            this.year = year;
        }
        else
        {
            System.out.println(year + " is not a valid year!");
        }
    }

    public void setMake(String make)
    {
        this.make = make;
    }
}

5

Одне, що не згадувалося, - це те, що геттер і сетери (як і всі методи) є віртуальними на Java. Це додає ще одну можливість завжди використовувати їх у своєму коді. Інший користувач може розширити ваш клас, замінивши ваші геттери та сетери. Тоді ваш базовий клас використовує дані підкласу замість власних. Мова, де ви чітко позначаєте віртуальні функції, це набагато зручніше, оскільки ви можете передбачити та оголосити, з якими функціями це можна зробити. У Java це завжди те, що ви завжди повинні знати. Якщо це бажана поведінка, то використовувати їх у своєму коді - це добре, інакше не так вже й багато.


3

Якщо ви намагаєтеся виконати щось, що надається загальнодоступним інтерфейсом, тоді використовуйте геттери / сеттери.

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

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

public class MyClass
{
    private int i;
    private List<string> list;
    public string getNextString()
    {
        i++;
        return list[i];
    }

    private void getString()
    {
        // Do not increment
        string currentString = list[i];

        // Increment
        string nextString = getNextString();
    }
}

Чи можете ви надати міркування для цього?
Даніель Каплан

1

Так. Геттери та сетери представляють стан, тому розгорніть це запитання - чи хочете ви відслідковувати різні способи зміни стану об'єктів однаково, якщо не потрібно?

Чим менше речей ви повинні відстежувати, тим краще - ось чому незмінні предмети легше мати справу.

Поміркуйте, ніщо не заважає вам мати як публічне поле, так і громадський геттер / сетер - але що б ви могли отримати?

Іноді може бути бажаним або навіть необхідним безпосередній доступ до поля з однієї або декількох причин, не слід ухилятися від цього, якщо це станеться. Але ви дійсно повинні робити це лише у тому випадку, якщо для цього є певна вигода.


Суть мого питання полягає в тому, що я запитую лише про прямий доступ до полів того ж класу. Я вже розумію призначення геттерів та сеттерів для коду клієнта.
Даніель Каплан

@DanielKaplan: Я це розумію, і те, що я говорю, це те, що на практиці ви повинні ставитися до них однаково.
jmoreno

@DanielKaplan: У вас може бути один приватний сеттер і один публічний сетер, які мають точно такий же результат (усі побічні ефекти однакові) хоча б на момент спостереження, але що б ви могли отримати від вас, крім потенціалу, щоб речі розходилися? Різниця між цим сценарієм і то , що ви описуєте, що ви не можете уникнути в стані отримати доступ до держави за межами геттеров / сеттерів, але ви можете уникнути на насправді зробити це. Не ускладнюйте свій код, якщо вам не доведеться.
jmoreno

1

Обидва методи мають свої випадки використання. Оскільки громадський сеттер підтримує значення поля (та / або зв'язані значення), ви повинні використовувати сеттер, коли ваша логіка методу не заважає цій логіці узгодженості. Якщо ви просто встановили свою "власність", використовуйте сетер. З іншого боку, є ситуації, коли вам потрібен прямий доступ до деяких полів, наприклад, масові операції з важким сеттером або операції, де концепція сеттера занадто проста.

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


1

Я б сказав, що ні.

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

Спільний спільний сервіс буде здійснювати часті дзвінки до одержувачів / сетерів. Таким чином, його ніколи не виникає питання. Вигода від використання змінних - це читабельність та IMHO, я б хотів за це.

Сподіваюся, це допомагає ..

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.