Действительно ли это неправильно, если не использовать сеттеры и геттеры? - PullRequest
15 голосов
/ 30 апреля 2009

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

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

Ответы [ 7 ]

21 голосов
/ 30 апреля 2009

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

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

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

17 голосов
/ 30 апреля 2009

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

15 голосов
/ 02 мая 2009

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

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

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

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

Скажем, у нас есть объект, который был разработан с использованием полей вместо геттеров / сеттеров, и мы хотим переименовать поле с именем 'field' в 'fieldWithBetterName', потому что 'field' было неуместным или больше не описывал использование точно, или было просто неправильно. И скажем, мы хотели изменить поле с именем '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);
    }
}

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

7 голосов
/ 30 апреля 2009

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

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

3 голосов
/ 30 апреля 2009

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

2 голосов
/ 01 мая 2009

Существует способ эмулировать 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 ()).

1 голос
/ 30 апреля 2009

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

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

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