Я собираюсь попробовать что-то с форматом этого вопроса, и я очень открыт для предложений о лучшем способе справиться с этим.
Я не хотел просто сбрасывать кучу кода в вопросе, поэтому я разместил код для класса на refactormycode.
базовый класс для простой обработки свойств класса
Я думал, что люди могут либо публиковать фрагменты кода здесь, либо вносить изменения в refactormycode и отправлять ссылки на их рефакторинг. Я сделаю upvotes и соглашусь ответить (при условии, что есть четкий «победитель») на основе этого.
Во всяком случае, к самому классу:
Я вижу много споров о методах класса getter / setter и лучше ли просто обращаться к простым переменным свойств напрямую или должен быть определен каждый класс с явными методами get / set, бла-бла-бла. Мне нравится идея иметь явные методы в случае, если вам придется добавить больше логики позже. Тогда вам не нужно изменять какой-либо код, который использует класс. Однако я ненавижу иметь миллион функций, которые выглядят так:
public function getFirstName() { return $this->firstName; } public function setFirstName($firstName) { return $this->firstName; }
Теперь я уверен, что я не первый человек, который это сделал (я надеюсь, что есть лучший способ сделать это, что кто-то может мне предложить).
В принципе, класс PropertyHandler имеет метод __call magic. Любые методы, которые проходят через __call, которые начинаются с «get» или «set», затем направляются к функциям, которые устанавливают или извлекают значения в ассоциативный массив. Ключ в массив – это имя вызывающего метода после get или set. Итак, если метод, входящий в __call, является «getFirstName», ключ массива – «FirstName».
Мне понравилось использовать __call, потому что он автоматически позаботится о случае, когда подкласс уже имеет метод getFirstName. Мое впечатление (и я могу ошибаться) в том, что методы магии __get & __set не делают этого.
Итак, вот пример того, как он будет работать:
class PropTest extends PropertyHandler { public function __construct() { parent::__construct(); } } $props = new PropTest(); $props->setFirstName("Mark"); echo $props->getFirstName();
Обратите внимание, что PropTest на самом деле не имеет методов «setFirstName» или «getFirstName», а также свойство PropertyHandler. Все, что происходит, это манипулировать значениями массива.
В другом случае ваш подкласс уже расширяет что-то еще. Поскольку вы не можете иметь истинное множественное наследование в PHP, вы можете сделать свой подкласс экземпляром PropertyHandler как приватную переменную. Вы должны добавить еще одну функцию, но тогда все будет вести себя точно так же.
class PropTest2 { private $props; public function __construct() { $this->props = new PropertyHandler(); } public function __call($method, $arguments) { return $this->props->__call($method, $arguments); } } $props2 = new PropTest2(); $props2->setFirstName('Mark'); echo $props2->getFirstName();
Обратите внимание, как в подклассе есть метод __call, который просто передает все вместе с методом PropertyHandler __call.
Другим хорошим аргументом против обращения с геттерами и сеттерами является то, что это очень сложно документировать.
Фактически, практически невозможно использовать какой-либо инструмент для создания документов, поскольку явные методы, которые не документируются, не существуют.
На данный момент я почти отказался от этого подхода. Это было интересное учебное упражнение, но я думаю, что он жертвует слишком большой ясностью.
Как я это делаю:
class test { protected $x=''; protected $y=''; function set_y ($y) { print "specific function set_y\n"; $this->y = $y; } function __call($function , $args) { print "generic function $function\n"; list ($name , $var ) = split ('_' , $function ); if ($name == 'get' && isset($this->$var)) { return $this->$var; } if ($name == 'set' && isset($this->$var)) { $this->$var= $args[0]; return; } trigger_error ("Fatal error: Call to undefined method test::$function()"); } } $p = new test(); $p->set_x(20); $p->set_y(30); print $p->get_x(); print $p->get_y(); $p->set_z(40);
Который будет выводить (добавление строк для ясности)
generic function set_x specific function set_y generic function get_x 20 generic function get_y 30 generic function set_z Notice: Fatal error: Call to undefined method set_z() in [...] on line 16
@Брайан
Моя проблема заключается в том, что добавление «дополнительной логики позже» требует добавления общей логики, которая применяется ко всем свойствам, к которым обращаются с помощью getter / setter, или к которым вы используете if или switch для оценки того, какое свойство вы обращаетесь, чтобы вы могли применить специфической логики.
Это не совсем так. Примите мой первый пример:
class PropTest extends PropertyHandler { public function __construct() { parent::__construct(); } } $props = new PropTest(); $props->setFirstName("Mark"); echo $props->getFirstName();
Предположим, что мне нужно добавить логику для проверки FirstNames. Все, что мне нужно сделать, это добавить метод setFirstName в мой подкласс, и этот метод будет автоматически использоваться вместо этого.
class PropTest extends PropertyHandler { public function __construct() { parent::__construct(); } public function setFirstName($name) { if($name == 'Mark') { echo "I love you, Mark!"; } } }
Я просто не удовлетворен ограничениями, которые имеет PHP, когда дело доходит до неявных методов доступа.
Я полностью согласен. Мне нравится способ Python справиться с этим (моя реализация – это просто неуклюжая копия).
Да, правильно, переменные должны быть объявлены вручную, но я нахожу это лучше, так как опасаюсь опечатки в сеттере
$props2->setFristName('Mark');
будет автоматически генерировать новое свойство (FristName вместо FirstName), что усложнит процесс отладки.
Мне нравится иметь методы вместо того, чтобы просто использовать публичные поля, но моя проблема с реализацией по умолчанию PHP (с использованием __get () и __set ()) или вашей пользовательской реализации заключается в том, что вы не устанавливаете геттеры и сеттеры для каждого свойства основа. Моя проблема заключается в том, что добавление «дополнительной логики позже» требует добавления общей логики, которая применяется ко всем свойствам, к которым обращаются с помощью getter / setter, или к которым вы используете if или switch для оценки того, какое свойство вы обращаетесь, чтобы вы могли применить специфической логики.
Мне нравится ваше решение, и я приветствую вас за это – я просто не удовлетворен ограничениями, которые имеет PHP, когда дело доходит до неявных методов доступа.
@Отметка
Но даже ваш метод требует нового объявления метода, и он несколько убирает преимущество поместить его в метод, чтобы вы могли добавить больше логики, потому что для добавления большей логики требуется старомодное объявление метода. В своем состоянии по умолчанию (которое там, где оно впечатляет в том, что оно обнаруживает / делает), ваш метод не предлагает преимуществ (в PHP) по публичным полям. Вы ограничиваете доступ к полю, но предоставляете карт-бланш через методы доступа, которые не имеют каких-либо ограничений. Я не знаю, что непроверенные явные аксессоры предлагают какое-либо преимущество перед публичными полями на любом языке, но люди могут и должны быть свободны исправить меня, если я ошибаюсь.
Я всегда рассматривал эту проблему в аналогичном с __call, который заканчивается в значительной степени как кодовый табличный код во многих моих классах. Тем не менее, он компактный и использует классы отражения, чтобы добавлять только геттеры / сеттеры для свойств, которые вы уже установили (не будут добавлять новые). Простое добавление getter / setter явно добавит более сложные функции. Ожидается, что
Код выглядит следующим образом:
/** * Handles default set and get calls */ public function __call($method, $params) { //did you call get or set if ( preg_match( "|^[gs]et([AZ][\w]+)|", $method, $matches ) ) { //which var? $var = strtolower($matches[1]); $r = new ReflectionClass($this); $properties = $r->getdefaultProperties(); //if it exists if ( array_key_exists($var,$properties) ) { //set if ( 's' == $method[0] ) { $this->$var = $params[0]; } //get elseif ( 'g' == $method[0] ) { return $this->$var; } } } }
Добавьте это в класс, где вы объявили свойства по умолчанию:
class MyClass { public $myvar = null; } $test = new MyClass; $test->setMyvar = "arapaho"; echo $test->getMyvar; //echos arapaho
Класс отражения может добавить что-то полезное к тому, что вы предлагаете. Аккуратное решение @Mark.
Совсем недавно я также подумал о том, как обращаться с геттерами и сеттерами, как вы предложили (второй подход был моим фаворитом, то есть частным массивом $ props), но я отбросил его, потому что это не получилось бы в моем приложении.
Я работаю над довольно большим SoapServer-приложением, и мыльный интерфейс PHP 5 вводит значения, которые передаются через мыло непосредственно в ассоциированный класс, не беспокоясь о существующих или несуществующих свойствах в классе.
Я не могу не помещать свои 2 цента …
Я взял использование __get
и __set
в этой усадьбе http://gist.github.com/351387 (аналогично тому, как это делает доктрина), а затем только доступ к свойствам через $obj->var
за пределами класс. Таким образом, вы можете переопределить функциональность по мере необходимости, вместо создания огромной функции __get
или __set
или переопределения __get
и __set
в дочерних классах.