Неужели это неправильно, если не использовать сеттеров и геттеров?

Я как-то новичок в PHP. По какой-то причине в других типах языков программирования, таких как JAVA, у меня нет проблем с использованием сеттеров и геттеров для каждой отдельной переменной, но когда я программирую на PHP, вероятно, потому, что он настолько гибкий, он чувствует себя как пустая трата времени. Намного проще просто устанавливать атрибуты класса как публичные большую часть времени и манипулировать ими таким образом. Дело в том, что, когда я это делаю, я чувствую, что делаю что-то неправильно и иду против принципов OO.

Неужели это неправильно, если не использовать сеттеры и геттеры? Почему или почему нет? Как вы, ребята, делаете это большую часть времени?

Ответ 1

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

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

Обратите внимание, что некоторые языки программирования имеют функции для синтеза поля по умолчанию + getter + setter - Ruby делает это с помощью метапрограммирования, С# имеет автоматически реализованные свойства. И Python полностью обходит эту проблему, позволяя вам переопределить доступ к атрибутам, позволяя вам инкапсулировать атрибут в подклассе, который ему нужен, вместо того, чтобы беспокоиться об этом. (Это тот подход, который мне больше всего нравится.)

Ответ 2

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

Ответ 3

Если мы говорим строго о PHP здесь, а не о С#, Java и т.д. (где компилятор будет оптимизировать эти вещи), я считаю, что получатели и сеттеры являются пустой тратой ресурсов, где вам просто нужно проксировать значение частное поле и ничего не делать.

В моей настройке я сделал два дерьмовых класса, один из которых состоял из пяти частных полей, инкапсулированных пятью парами getter/setter, проксимирующих поле (которое выглядело почти точно как java-код, достаточно забавно), а другое с пятью общедоступными полями и называлось memory_get_usage() в конце после создания экземпляра. В script с геттерами/сеттерами использовалось 59708 байт памяти и script с общедоступными полями, использующими 49244 байт.

В контексте библиотеки классов любого значительного размера, такого как структура веб-сайта, эти бесполезные геттеры и сеттеры могут добавить к ОГРОМНОЙ черной дыре для памяти. Я разрабатываю рамки для своего работодателя на PHP (их выбор, а не мой. Я бы не использовал его для этого, если бы у меня был выбор, но, сказав это, PHP не налагает никаких непреодолимых ограничений на нас), и когда я реорганизую библиотека классов для использования публичных полей вместо геттеров/сеттеров, весь shebang в конечном итоге использовал на 25% меньше памяти на запрос по крайней мере.

Методы __get(), __set() и __call() 'magic' действительно светят для обработки изменений интерфейса. Когда вам нужно перенести поле в getter/setter (или getter/setter в поле), они могут сделать процесс прозрачным для любого зависимого кода. С интерпретированным языком немного сложнее найти все способы использования поля или метода даже при достаточно хорошей поддержке чувствительности кода, предоставляемой Eclipse PDT или Netbeans, поэтому магические методы полезны для обеспечения того, чтобы старый интерфейс все еще делегировал новый функциональность.

Скажем, у нас есть объект, который был разработан с использованием полей вместо геттеров/сеттеров, и мы хотим переименовать поле с именем 'field' в поле fieldWithBetterName, потому что поле было неуместным или больше не описывало использование точно, или просто неправильно. И скажем, мы хотели изменить поле под названием "field2", чтобы ленить загрузить его значение из базы данных, потому что изначально неизвестно, используя геттер...

class Test extends Object {
    public $field;
    public $field2;
}

становится

class Test extends Object {
    public $fieldWithBetterName = "LA DI DA";
    private $_field2;

    public function getField2() {
        if ($this->_field2 == null) {
            $this->_field2 = CrapDbLayer::getSomething($this->fieldWithBetterName);
        }
        return $this->_field2;
    }

    public function __get($name) {
        if ($name == 'field')) {
            Logger::log("use of deprecated property... blah blah blah\n".DebugUtils::printBacktrace());
            return $this->fieldWithBetterName;
        }
        elseif ($name == 'field2') {
            Logger::log("use of deprecated property... blah blah blah\n".DebugUtils::printBacktrace());
            return $this->getField2();
        }
        else return parent::__get($name);
    }
}
$t = new Test;
echo $t->field;
echo $t->field2;

(В качестве побочного примечания, что бит "extends Object" - это только базовый класс, который я использую практически для всего, у которого есть объявление __get() и __set(), которое генерирует исключение, когда к незанятым полям обращаются)

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

class Test extends Object {
    public $field2;

    public function __call($name, $args) {
        if (strpos($name, 'get')===0) {
            $field = lcfirst($name); // cheating, i know. php 5.3 or greater. not hard to do without it though.
            return $this->$field;
        }
        parent::__call($name, $args);
    }
}

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

Ответ 4

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

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

Ответ 5

Вы считали, что используете магические функции __set/__ get? Используя их, вы можете легко объединить все функции getter/setter только в 2 функциях!

Ответ 6

Существует способ эмуляции get/set без фактического использования класса функций get/set, поэтому ваш код остается опрятным:

$person->name = 'bob';
echo $person->name;

Взгляните на этот класс Я закодировал.

Как правило, при использовании этого класса вы объявляете все свои свойства защищенными (или частными). В случае, если вы хотите добавить поведение свойства, скажите strtolower() + ucfirst() в свойстве "name", все, что вам нужно сделать, это объявить защищенную функцию set_name() в вашем классе и поведение должно автоматически подбираться. То же самое можно сделать с помощью get_name().

// Somewhere in your class (that extends my class).
protected function set_name($value) { $this->name = ucfirst(strtolower($value)); }
//

// Now it would store ucfirst(strtolower('bob')) automatically.
$person->name = 'bob';

P.S. Еще одна интересная вещь: вы можете создавать несуществующие поля, такие как

echo $person->full_name;

не имея таких полей (пока существует функция get_full_name()).

Ответ 7

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

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