Должны ли методы класса вызывать свои собственные методы получения и установки?

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
Использование getters / setters во внутреннем коде позволяет вам создать точку останова в вашем коде, если вы проходите через отладчик. Это также позволяет вам убедиться, что если что-то не так с настройкой установки / получения значения, вы знаете, что это из-за этого метода, а не из-за какого-то недопустимого доступа. В целом, это обеспечивает большую согласованность кода.
Callyalater

Ответы:

46

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

Например, что произойдет, если вы добавите «грязный» флаг к некоторым полям установщика позже? Вызывая ваши сеттеры во внутреннем коде, вы устанавливаете грязный флаг, не меняя никакого другого кода. Во многих ситуациях это было бы хорошо.

Мэтт С
источник
Но что делать, когда вы инициализируете данные в бэкэнде и не хотите, чтобы это интерпретировалось как грязное. Если вы звонили setField()с самого начала, вы только что ввели ошибку.
Даниэль Каплан
6
Правильно, поэтому я говорю, что это частично зависит от ситуации. Возможно, ваша инициализация данных должна пропускать установщики, но другой логический код не должен. Выберите подходящие правила и придерживайтесь их.
Мэтт С
3
@DanielKaplan: дело в том, что вызывать геттеры и сеттеры в большинстве случаев правильно, и только в определенных ситуациях нет. Однако в реальном коде, когда вы изменяете существующую реализацию getter / setter позже, чтобы ввести некоторые намеренные побочные эффекты, вам, скорее всего, придется проверять каждый вызов getter или setter внутри класса, а также каждый прямой доступ к поле. Вот почему вы должны стараться, чтобы ваши классы были как можно меньше.
Док Браун
33

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

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

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

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

прецизионный самописец
источник
1
Я полностью согласен / практикую эту линию мышления. Я также думаю, что вы затронули вопрос, который на самом деле важнее моего вопроса. Тем не менее, я не чувствую, что это прямо отвечает на мой первоначальный вопрос. Если вы не говорите, «в общем плане ваш вопрос не имеет значения». Что также может быть правдой :)
Даниэль Каплан
@DanielKaplan: Я говорю именно это. :) Я разрывался между ответом и комментарием с этим, но я искренне не чувствую, что есть правильный ответ, который не задает более важный вопрос.
pdr
9

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

Майкл
источник
1
Я не вижу связи между вашим первым предложением и остальной частью вашего абзаца. Первое предложение говорит, что класс должен вызывать свои собственные методы получения и установки. В оставшейся части параграфа объясняется, почему клиентский код (т. Е. Код, использующий класс) должен использовать методы получения и установки вместо прямого доступа к полям. Но я не понимаю, как по этой причине класс не должен напрямую обращаться к своим собственным полям.
Даниэль Каплан
1
@DanielKaplan Моя точка зрения заключается в том, что причины одни и те же. Что если вы добавите установочную логику позже, это повлияет на внутренний код так же (потенциально), как и на внешний.
Майкл
2
@ Майкл: У класса могут быть веские причины не использовать свои собственные методы получения / установки. Один из распространенных сценариев состоит в том, что существует множество установщиков формы. someField=newValue; refresh(); Если метод позволяет задавать несколько полей, вызов сеттеров для записи этих полей вызовет избыточные операции «обновления». Запись всех полей и последующий вызов refresh()может привести к более эффективной и гладкой работе.
суперкат
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;
    }
}

Вызов сеттеров для year и 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();
    }
}
ConditionRacer
источник
Можете ли вы дать обоснование для этого?
Даниэль Каплан
1

Да. Геттеры и сеттеры представляют состояние, поэтому переверните этот вопрос - хотите ли вы отслеживать несколько способов изменения состояния объектов одним и тем же способом, если нет необходимости?

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

Подумайте, ничто не мешает вам иметь как публичное поле, так и публичный метод получения / установки - но что бы вы получили?

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

jmoreno
источник
Суть моего вопроса в том, что я спрашиваю только о прямом доступе к полям в одном классе. Я уже понимаю назначение геттеров и сеттеров для клиентского кода.
Даниэль Каплан
@DanielKaplan: Я понимаю это, и я говорю, что, насколько это практически возможно, вы должны относиться к ним одинаково.
Jmoreno
@DanielKaplan: У вас может быть один частный сеттер и один публичный сеттер, которые будут иметь одинаковый результат (все побочные эффекты одинаковы), по крайней мере, для момента, но что это даст вам, кроме возможности расхождения вещей? Разница между этим сценарием и то , что вы описываете, что вы не можете избежать в состоянии получить доступ к государственным вне геттеров / сеттеров, но вы можете избежать на самом деле сделать это. Не усложняйте свой код, если вам не нужно.
Jmoreno
1

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

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

Артур
источник
1

Я бы сказал нет ..

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

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

Надеюсь это поможет..

Правин Сонавейн
источник