Геттер и сеттер? - PullRequest
       53

Геттер и сеттер?

194 голосов
/ 18 декабря 2010

Я не разработчик PHP, поэтому мне интересно, если в PHP более популярно использовать явные методы получения / установки, в чистом стиле ООП, с закрытыми полями (как мне нравится):

class MyClass {
    private $firstField;
    private $secondField;

    public function getFirstField() {
        return $this->firstField;
    }
    public function setFirstField($x) {
        $this->firstField = $x;
    }
    public function getSecondField() {
        return $this->secondField;
    }
    public function setSecondField($x) {
        $this->secondField = $x;
    }
}

или просто открытые поля:

class MyClass {
    public $firstField;
    public $secondField;
}

Спасибо

Ответы [ 15 ]

210 голосов
/ 18 декабря 2010

Вы можете использовать php магические методы __get и __set.

<?php
class MyClass {
  private $firstField;
  private $secondField;

  public function __get($property) {
    if (property_exists($this, $property)) {
      return $this->$property;
    }
  }

  public function __set($property, $value) {
    if (property_exists($this, $property)) {
      $this->$property = $value;
    }

    return $this;
  }
}
?>
108 голосов
/ 21 января 2012

Зачем использовать геттеры и сеттеры?

  1. Масштабируемость : проще выполнить рефакторинг геттера, чем искать все назначения var в коде проекта.
  2. Отладка : Вы можете устанавливать точки останова на сеттеры и геттеры.
  3. Очиститель : магические функции не являются хорошим решением для меньшего количества записей, ваша IDE не будет предлагать код. Лучше использовать шаблоны для быстро пишущих геттеров.

direct assignment and getters/setters

36 голосов
/ 30 мая 2013

Google уже опубликовал руководство по оптимизации PHP и сделал вывод:

Нет геттера и сеттера Оптимизация PHP

И нет, вы не должны использовать магические методы . Магический метод для PHP - это зло. Почему?

  1. Их сложно отлаживать.
  2. Отрицательное влияние на производительность.
  3. Они требуют написания большего количества кода.

PHP не является Java, C ++ или C #. PHP отличается и играет с разными ролями.

12 голосов
/ 18 декабря 2010

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

В PHP это работает:

class Foo {
   public $bar; // should be an integer
}
$foo = new Foo;
$foo->bar = "string";

В Java это не так:

class Foo {
   public int bar;
}
Foo myFoo = new Foo();
myFoo.bar = "string"; // error

Использование магических методов (__get и __set) также работает, но только при доступе к свойству, которое имеет более низкую видимость, чем текущая область. Он может легко вызвать головную боль при попытке отладки, если он не используется должным образом.

7 голосов
/ 08 сентября 2013

В дополнение к и без того отличным и уважаемым ответам здесь я хотел бы остановиться на PHP без установщиков / получателей.

В PHP нет синтаксиса геттера и сеттера . Он предоставляет подклассные или магические методы, позволяющие «перехватывать» и переопределять процесс поиска свойств, как указано Dave .

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

Производительность Каждая ненужная функция, возникающая в результате принудительного использования PHP-архитектуры, подобной геттеру / сеттеру, при вызове использует свой собственный стековый фрейм памяти и тратит впустую циклы ЦП.

Удобочитаемость: Кодовая база вызывает вздутие живота кодовых строк, что влияет на навигацию по коду, так как больше LOC означает больше прокрутки.

Предпочтение: Лично, как правило, я принимаю провал статического анализа кода как знак, чтобы не идти по волшебной дороге, пока очевидные долгосрочные выгоды ускользают от меня в то время.

Заблуждения:

Общий аргумент - читабельность. Например, $someobject->width легче читать, чем $someobject->width(). Однако в отличие от circumference или width планеты, которые можно считать равными static, экземпляр объекта, такой как $someobject, для которого требуется функция ширины, скорее всего, измеряет ширину экземпляра объекта.
Поэтому удобочитаемость увеличивается в основном из-за утвердительных схем именования, а не из-за того, что функция скрыта, и она выводит заданное значение свойства.

__ get / __set использует:

  • предварительная проверка и предварительная санация стоимости имущества

  • строки, например

    "
    some {mathsobj1->generatelatex} multi
    line text {mathsobj1->latexoutput}
    with lots of variables for {mathsobj1->generatelatex}
     some reason
    "
    

    В этом случае generatelatex будет придерживаться схемы именования actionname + methodname

  • особые, очевидные случаи

    $dnastringobj->homeobox($one_rememberable_parameter)->gattaca->findrelated()
    $dnastringobj->homeobox($one_rememberable_parameter)->gttccaatttga->findrelated()
    

Примечание: PHP решил не реализовывать синтаксис getter / setter. Я не утверждаю, что геттеры / сеттеры вообще плохие.

7 голосов
/ 06 июня 2012

Если вы предпочитаете использовать функцию __call, вы можете использовать этот метод. Работает с

  • GET => $this->property()
  • SET => $this->property($value)
  • GET => $this->getProperty()
  • SET => $this->setProperty($value)

kalsdas

public function __call($name, $arguments) {

    //Getting and setting with $this->property($optional);

    if (property_exists(get_class($this), $name)) {


        //Always set the value if a parameter is passed
        if (count($arguments) == 1) {
            /* set */
            $this->$name = $arguments[0];
        } else if (count($arguments) > 1) {
            throw new \Exception("Setter for $name only accepts one parameter.");
        }

        //Always return the value (Even on the set)
        return $this->$name;
    }

    //If it doesn't chech if its a normal old type setter ot getter
    //Getting and setting with $this->getProperty($optional);
    //Getting and setting with $this->setProperty($optional);
    $prefix = substr($name, 0, 3);
    $property = strtolower($name[3]) . substr($name, 4);
    switch ($prefix) {
        case 'get':
            return $this->$property;
            break;
        case 'set':
            //Always set the value if a parameter is passed
            if (count($arguments) != 1) {
                throw new \Exception("Setter for $name requires exactly one parameter.");
            }
            $this->$property = $arguments[0];
            //Always return the value (Even on the set)
            return $this->$name;
        default:
            throw new \Exception("Property $name doesn't exist.");
            break;
    }
}
6 голосов
/ 06 ноября 2012
class MyClass {
    private $firstField;
    private $secondField;
    private $thirdField;

    public function __get( $name ) {
        if( method_exists( $this , $method = ( 'get' . ucfirst( $name  ) ) ) )
            return $this->$method();
        else
            throw new Exception( 'Can\'t get property ' . $name );
    }

    public function __set( $name , $value ) {
        if( method_exists( $this , $method = ( 'set' . ucfirst( $name  ) ) ) )
            return $this->$method( $value );
        else
            throw new Exception( 'Can\'t set property ' . $name );
    }

    public function __isset( $name )
    {
        return method_exists( $this , 'get' . ucfirst( $name  ) ) 
            || method_exists( $this , 'set' . ucfirst( $name  ) );
    }

    public function getFirstField() {
        return $this->firstField;
    }

    protected function setFirstField($x) {
        $this->firstField = $x;
    }

    private function getSecondField() {
        return $this->secondField;
    }
}

$obj = new MyClass();

echo $obj->firstField; // works
$obj->firstField = 'value'; // works

echo $obj->getFirstField(); // works
$obj->setFirstField( 'value' ); // not works, method is protected

echo $obj->secondField; // works
echo $obj->getSecondField(); // not works, method is private

$obj->secondField = 'value'; // not works, setter not exists

echo $obj->thirdField; // not works, property not exists

isset( $obj->firstField ); // returns true
isset( $obj->secondField ); // returns true
isset( $obj->thirdField ); // returns false

Готов!

5 голосов
/ 18 декабря 2010

Ну, PHP имеет магические методы __get, __set, __isset & __unset, что всегда является началом.Увы, правильные (понимаете?) Свойства ОО - это больше, чем магические методы.Основная проблема с реализацией PHP заключается в том, что магические методы вызываются для всех недоступных свойств.Это означает, что вы должны повторять себя (например, вызывая property_exists ()) в магических методах при определении, действительно ли name является свойством вашего объекта.И вы не можете действительно решить эту общую проблему с базовым классом, если все ваши классы не наследуются от ie.ClassWithProperties, поскольку в PHP отсутствует множественное наследование.

В отличие от Python новые классы стилей дают вам property(), что позволяет вам явно определять все ваши свойства. C # имеет специальный синтаксис.

http://en.wikipedia.org/wiki/Property_(programming)

3 голосов
/ 24 ноября 2015

Я провел эксперимент, используя магический метод __call.Не уверен, что я должен опубликовать это (из-за всех предупреждений «НЕ ИСПОЛЬЗУЙТЕ МАГИЧЕСКИЕ МЕТОДЫ» в других ответах и ​​комментариях), но я оставлю это здесь ... на всякий случай, если кто-то посчитает это полезным.

public function __call($_name, $_arguments){
    $action  = substr($_name, 0, 4);
    $varName = substr($_name, 4);

    if (isset($this->{$varName})){
        if ($action === "get_") return $this->{$varName};
        if ($action === "set_") $this->{$varName} = $_arguments[0];
    }
}

Просто добавьте этот метод выше в своем классе, теперь вы можете набрать:

class MyClass{
    private foo = "bar";
    private bom = "bim";
    // ...
    // public function __call(){ ... }
    // ...
}
$C = new MyClass();

// as getter
$C->get_foo(); // return "bar"
$C->get_bom(); // return "bim"

// as setter
$C->set_foo("abc"); // set "abc" as new value of foo
$C->set_bom("zam"); // set "zam" as new value of bom


Таким образом, вы можете получить / установить все в вашем классе, если он существует, такЕсли вам это нужно только для нескольких конкретных элементов, вы можете использовать «белый список» в качестве фильтра.

Пример:

private $callWhiteList = array(
    "foo" => "foo",
    "fee" => "fee",
    // ...
);

public function __call($_name, $_arguments){
    $action  = substr($_name, 0, 4);
    $varName = $this->callWhiteList[substr($_name, 4)];

    if (!is_null($varName) && isset($this->{$varName})){
        if ($action === "get_") return $this->{$varName};
        if ($action === "set_") $this->{$varName} = $_arguments[0];
    }
}

Теперь вы можете получать / устанавливать только "foo" и "fee".
Вы также можете использовать этот "белый список" для назначения пользовательских имен для доступа кваши переменные.
Например,

private $callWhiteList = array(
    "myfoo" => "foo",
    "zim" => "bom",
    // ...
);

С этим списком вы теперь можете набрать:

class MyClass{
    private foo = "bar";
    private bom = "bim";
    // ...
    // private $callWhiteList = array( ... )
    // public function __call(){ ... }
    // ...
}
$C = new MyClass();

// as getter
$C->get_myfoo(); // return "bar"
$C->get_zim(); // return "bim"

// as setter
$C->set_myfoo("abc"); // set "abc" as new value of foo
$C->set_zim("zam"); // set "zam" as new value of bom

.
.
.
Вот и все.


Документ: __ call () запускается при вызове недоступных методов в контексте объекта.

2 голосов
/ 27 января 2015

Прочитав другие советы, я склонен сказать, что:

Как правило GENERIC , вы не всегда будете определять сеттеры для ALL свойств,специально "внутренние" (семафоры, внутренние флаги ...). Свойства только для чтения не будут иметь сеттеров, очевидно, поэтому некоторые свойства будут иметь только геттеры;вот где __get () сокращает код:

  • определяет __get () (магические глобальные геттеры) для всех одинаковых свойств,
  • группирует их в массивы так:
    • они будут иметь общие характеристики: денежные значения будут / могут подходить надлежащим образом отформатированы, даты в определенном формате (ISO, США, международный) и т. Д.
    • сам код может проверитьчто только существующие и разрешенные свойства читаются с использованием этого магического метода.
    • всякий раз, когда вам нужно создать новое подобное свойство, просто объявите его и добавьте его имя в соответствующий массив, и все готово.Таким образом, БЫСТРО , чем определение нового метода получения, возможно, с некоторыми строками кода, ПОВТОРЯЕМЫМИ снова и снова по всему коду класса.

Да!мы могли бы написать частный метод, чтобы сделать это, но, опять же, мы будем иметь МНОГИЕ объявленные методы (++ memory), которые в конечном итоге вызовут другой, всегда один и тот же метод.Почему бы просто не написать SINGLE метод для правила их всех ...?[Ага!каламбур абсолютно предназначен!:)]

Магические сеттеры также могут реагировать ТОЛЬКО на определенные свойства, поэтому все свойства типа даты могут быть экранированы по недопустимым значениям только в одном методе.Если свойства типа даты были перечислены в массиве, их установщики могут быть легко определены.Просто пример, конечно.Есть слишком много ситуаций.

О удобочитаемость ... Ну ... Это еще один спор: я не люблю быть связанным с использованием IDE (на самом деле,Я ими не пользуюсь, они, как правило, говорят мне (и заставляют меня), как писать ... и мне нравится писать "красота").Я склонен быть последовательным в отношении именования, поэтому мне достаточно использования ctags и пары других вспомогательных средств ... В любом случае: как только все эти магические сеттеры и геттеры завершены, я пишу другие сеттеры, которые слишком специфичны или «особенные» длябыть обобщенным в методе __set ().И это охватывает все, что мне нужно для получения и установки свойств.Конечно: не всегда есть общий язык, или есть несколько таких свойств, которые не стоят проблем при кодировании магического метода, и все же есть старая, хорошая традиционная пара сеттер / геттер.

Языки программированияэто просто: человеческие искусственные языки.Итак, у каждого из них есть своя собственная интонация или акцент, синтаксис и вкус, поэтому я не буду притворяться, что пишу код на Ruby или Python, используя тот же «акцент», что и Java или C #, и я не написал бы JavaScript или PHP, чтобы они напоминалиPerl или SQL ... Используйте их так, как они предназначены.

...