Это разумный способ обработки геттеров / сеттеров в классе PHP? - PullRequest
8 голосов
/ 28 августа 2008

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

Я не хотел просто выкидывать кучу кода в вопросе, поэтому я разместил код для класса на refactormycode.

базовый класс для простой обработки свойств класса

Я думал, что люди могут либо размещать здесь фрагменты кода, либо вносить изменения в refactormycode и публиковать ссылки на свои рефакторинги. На этом основании я проголосую и приму ответ (при условии, что есть явный «победитель»).

Во всяком случае, для самого класса:

Я вижу много споров о методах класса getter / setter, и лучше ли просто обращаться к простым переменным свойств напрямую, или если каждый класс имеет явные методы get / set, бла-бла-бла. Мне нравится идея иметь явные методы на случай, если вам придется добавить больше логики позже. Тогда вам не нужно изменять код, который использует класс. Однако я ненавижу иметь миллион функций, которые выглядят так:

public function getFirstName()
{
   return $this->firstName;
}
public function setFirstName($firstName)
{
   return $this->firstName;
}

Теперь я уверен, что я не первый, кто делает это (я надеюсь, что есть лучший способ сделать это, который кто-то может мне предложить).

По сути, класс PropertyHandler имеет магический метод __call. Любые методы, которые проходят через __call, которые начинаются с «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.


Другим хорошим аргументом против обработки методов получения и установки таким образом является то, что это действительно затрудняет документирование.

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

Пока я почти отказался от этого подхода. Это было интересное учебное упражнение, но я думаю, что оно приносит слишком много ясности.

Ответы [ 8 ]

5 голосов
/ 28 августа 2008

То, как я это делаю, таково:

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
3 голосов
/ 28 августа 2008

@ Brian

Моя проблема с этим заключается в том, что добавление «дополнительной логики позже» требует, чтобы вы добавили общую логику, которая применяется ко всем свойствам, доступным через 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 (моя реализация - просто неуклюжий плагиат).

2 голосов
/ 28 августа 2008

Да, верно, переменные должны быть объявлены вручную, но я считаю, что это лучше, так как я боюсь опечатки в установщике

$props2->setFristName('Mark');

автоматически сгенерирует новое свойство (FristName вместо FirstName), что усложнит отладку.

1 голос
/ 28 августа 2008

@ Mark

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

1 голос
/ 28 августа 2008

Мне также нравится иметь методы вместо использования просто открытых полей, но моя проблема с реализацией PHP по умолчанию (с использованием __get () и __set ()) или с вашей пользовательской реализацией состоит в том, что вы не устанавливаете методы получения и установки на на основе собственности. Моя проблема с этим заключается в том, что добавление «дополнительной логики позже» требует, чтобы вы добавили общую логику, которая применяется ко всем свойствам, доступным через getter / setter, или чтобы вы использовали операторы if или switch, чтобы оценить, к какому свойству вы обращаетесь, чтобы вы могли применить специфическая логика.

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

0 голосов
/ 17 апреля 2010

Я не могу не положить свои 2 цента ...

Я использовал __get и __set в этом поместье http://gist.github.com/351387 (аналогично тому, как это делает учение), а затем только когда-либо обращался к свойствам через $obj->var снаружи класс. Таким образом, вы можете переопределить функциональность по мере необходимости вместо создания огромной функции __get или __set или переопределения __get и __set в дочерних классах.

0 голосов
/ 06 января 2009

Совсем недавно я также думал о том, как обращаться с геттерами и сеттерами так, как вы предлагали (второй подход был моим любимым, то есть с массивом private $ props), но я отказался от него, потому что он не сработал бы в моем приложении.

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

0 голосов
/ 28 августа 2008

Я всегда обращался с этой проблемой аналогично __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([A-Z][\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    

Класс отражения может добавить что-то полезное к тому, что вы предлагаете. Чистое решение @ Марк.

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