PHP ООП много сеттеров, геттеров

Мне нужно создать ок. 5-7 классов, в каждом классе будет много участников (скажем, в каждом классе будет 20 участников). Я мог бы создать их, используя публичный доступ, например:

class A {
    public $myPropertyOne = '';
    public $myPropertyTwo = '';
    ...
}

Мой предпочтительный способ, конечно, сделать эти члены закрытыми и создать методы get/set для каждого свойства. т.е.

class A {
    private $myPropertyOne = '';
    private $myPropertyTwo = '';

    public function getMyPropertyOne() {
            return $this->myPropertyOne;
    }

    public function setMyPropertyOne($myPropertyOne) {
            $this->myPropertyOne = $myPropertyOne;
    }

    public function getMyPropertyTwo() {
            return $this->myPropertyTwo;
    }

    public function setMyPropertyTwo($myPropertyTwo) {
            $this->myPropertyTwo = $myPropertyTwo;
    }
}

Но учитывая, что у класса будет 20 свойств, мне придется добавить к этому еще 40 методов. И меня беспокоит то, как это замедлит работу скрипта и потребует гораздо больше памяти (помните, у меня будет несколько таких классов).

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

Если бы это был скомпилированный язык (например, C++), у меня не было бы вопросов, и я бы использовал решение с геттерами, сеттерами, но, поскольку PHP является интерпретируемым языком, я заинтересован в том, чтобы мои скрипты использовали меньше оперативной памяти и были как можно быстрее.

Заранее спасибо, будем очень признательны за любые мысли по этому вопросу!


Мое мнение

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

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

Итак... я считаю, что лучший способ здесь - использовать публичный доступ для всех членов и избегать геттеров/сеттеров для всех свойств и использовать частный доступ с методами get/set для свойств, которые требуют проверки или инициализации данных, прежде чем значение будет быть установлен.

Например:

class B {
    public $myPropertyOne = '';
    public $myPropertyTwo = '';
    private $myPropertyThree = array();


    public function getMyPropertyThree() {
        return $this->myPropertyThree;
    }

    public function setMyPropertyThree($val) {
        if(!is_array($val)) {
            return;
        }

        $this->myPropertyThree = $val;
    }
}

Спасибо, что уделили время моему вопросу!


person user1476490    schedule 23.06.2012    source источник
comment
это микрооптимизация, и вам не следует никогда беспокоиться об этом.   -  person Karoly Horvath    schedule 23.06.2012
comment
Ну да, но перспектива добавить 280 методов (для 7 классов, 20 приватных членов) только для этого меня немного пугает с точки зрения памяти/скорости. И скажем, система растет, и я расширю эти классы до 15... тогда я получу 600 методов. С другой стороны, я понимаю, что эти методы get/set будут выполняться только тогда, когда это необходимо, но в то же время я предполагаю, что когда PHP будет анализировать исходный код, он будет строить таблицу методов в памяти, выполнять синтаксический анализ и все это может сильно замедлить работу скрипта, верно ли мое предположение?   -  person user1476490    schedule 23.06.2012
comment
Память относительно дешева, а накладные расходы метода относятся только к классу (а не к экземпляру). PHP также выполняет трюки, такие как обработка нескольких запросов в одном процессе (через mod_php и т. д.), поэтому начальное время загрузки (каким бы тривиальным оно ни было) можно полностью игнорировать... в любом случае сначала сравните (в реальных условиях), чтобы увидеть, какие части системы медленные. (FWIW, предположим, что существует 600 методов, и каждый метод тратит впустую 1 КБ. Целых 600 КБ тратится впустую. Не так уж и много в схеме вещей... особенно учитывая, сколько каждой переменной в PHP уже отходы ;-)   -  person    schedule 23.06.2012
comment
Вы не должны беспокоиться о времени синтаксического анализа вообще. Это можно легко устранить с помощью APC (или другого кэша кода операции).   -  person linepogl    schedule 23.06.2012
comment
Спасибо за ваши ответы, однако я не могу согласиться с pst. 600 КБ на одну загрузку скрипта - это не так уж много, но, скажем, тысяча пользователей загружает скрипт, это приведет к использованию ~ 586 МБ всего. А если 10-20к пользователей? :)   -  person user1476490    schedule 23.06.2012
comment
@ user1476490 тысяч пользователей, загружающих скрипт, будут означать 1000 одновременных запросов (т. е. первый запрос все еще выполняется, когда запускается последний). Это маловероятный сценарий. Высокая нагрузка еще не означает, что 10-20 тысяч пользователей обслуживаются одновременно.   -  person lanzz    schedule 23.06.2012
comment
Мааан, просто не делай этого. если у вас столько одновременных запросов в секунду, у вас уже есть кластер серверов, обрабатывающий трафик, с memcached, APC, возможно, хип-хоп и со всеми видами других оптимизаций. И эти дурацкие микрооптимизации были бы последней вещью, о которой вы могли бы беспокоиться.   -  person Karoly Horvath    schedule 23.06.2012
comment
Извините, я знаю, что это давняя мертвая дискуссия, и простите меня за некропостинг, но если бы вы действительно были озабочены сохранением байтов данных, разве вы не сократили бы setMyPropertyThree до чего-то вроде sPropThree? ;)   -  person DevlshOne    schedule 25.10.2012


Ответы (6)


Простой тест показывает, что экземпляры занимают одинаковый объем памяти, на который не влияет количество методов в классе:

Класс без методов:

class Test1 { }

Класс с 20 методами:

class Test2 {

    function test1() { return true; }
    function test2() { return true; }
    function test3() { return true; }
    function test4() { return true; }
    function test5() { return true; }
    function test6() { return true; }
    function test7() { return true; }
    function test8() { return true; }
    function test9() { return true; }
    function test10() { return true; }
    function test11() { return true; }
    function test12() { return true; }
    function test13() { return true; }
    function test14() { return true; }
    function test15() { return true; }
    function test16() { return true; }
    function test17() { return true; }
    function test18() { return true; }
    function test19() { return true; }
    function test20() { return true; }

}

Тестовый цикл, одинаковый для обоих тестов:

$test = array();
$base = memory_get_usage();
for ($i = 0; $i < 10000; $i++) {
    $test[] = new ClassToBeTested();
}
$used = memory_get_usage() - $base;
print("used: $used\n");

Результат для класса Test1 (без методов):

used: 3157408

Результат для класса Test2 (20 методов):

used: 3157408

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

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

person lanzz    schedule 23.06.2012
comment
К сожалению, я не могу вставить сюда много кода, но вот что я сделал. Я создал test1.php с одним классом, затем test2.php с классом и одним методом, а затем test3.php с классом, 20 приватными свойствами и затем 40 общедоступных методов get/set (со всеми возвратами и т. д.). ). И в конце выведите общее количество используемой оперативной памяти, вот что я получил: test1.php - 653.97 Kbytes test2.php - 654.73 Kbytes test3.php - 718.16 Kbytes Это грубо, но в основном я полагаю, что каждый метод/переменная, не считая данных, которые он содержит, съедает ~ 1 КБ. - person user1476490; 23.06.2012

Но учитывая, что класс будет иметь 20 свойств

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

Придется вдобавок к этому добавить еще 40 методов.

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

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

Это не проблема.

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

Современные IDE могут автодополнять магические методы.

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

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

Кроме того, магические методы неочевидны и затрудняют чтение API.

person Gordon    schedule 23.06.2012
comment
Спасибо за ваш подробный ответ. - person user1476490; 23.06.2012

Чтобы свойства вашего класса, реализованные магическими методами, были выделены IDE, просто используйте тег @property PHPDoc @property, например:

<?php
/**
* @property int id Blog post ID
* @property string title Blog post Title
*/
class Post {

}

Подробнее о @property PHPDoc здесь: http://manual.phpdoc.org/HTMLSmartyConverter/PHP/phpDocumentor/tutorial_tags.property.pkg.html

Что касается других вопросов, то комментарий Кароли Хорват полностью охватывает те множество сеттеров, геттеров.

person НЛО    schedule 23.06.2012

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

Теперь, конечно, это будет больше потреблять ресурсов, правда. Что касается автозаполнения, используйте константы: вы просто наберете что-то вроде:

 $my_class->getFromHashMap($parameter)

И при вводе параметра вы будете использовать константу в том виде, в каком она хранится в классе: здесь автозаполнение должно вам помочь.

person Raveline    schedule 23.06.2012
comment
Да, в настоящее время я храню все свойства в ассоциативном массиве, но для конечного пользователя (разработчика) не совсем понятно, как их все использовать, поэтому мне приходится использовать завершение кода и комментарии к документам, плюс я хочу улучшить код, проверяя данные, когда пользователь устанавливает переменную. Хеш-карта — хорошее решение, но это приведет к другим возможным проблемам (разработчик может неправильно ввести имя параметра, я не смогу использовать рефакторинг, если захочу переименовать параметр и т. д.). - person user1476490; 23.06.2012
comment
За исключением проверки данных, использование констант в качестве ключей для вашей хэш-карты должно решить большую часть проблемы, которую вы описываете (и вы даже можете предотвратить в своих сеттерах использование ключа, который не определен в ваших константах). - person Raveline; 23.06.2012

Имейте в виду, что мой код считал, что имя свойства было объявлено в нижнем регистре...

  <?php

    class Modelo {
        var $attr1 = "default";
        var $attr2 = 0;


        public function __call($name, $arguments)
        {
            if (method_exists($this, ($method = $name))){
                return $this->$method();
            }
            else{       
                $attribute = split("get",$name);
                if(count($attribute)==2){
                    $attribute = strtolower($attribute[1]);
                    if(isset($this->$attribute)){
                        return ($this->$attribute);
                    }
                }else{
                    $attribute = split("set",$name);
                    if(count($attribute)==2){
                        $attribute = strtolower($attribute[1]);
                        if(isset($this->$attribute) && count($arguments)==1){
                            $this->$attribute=$arguments[0];
                        }else{
                            die("$name number of arguments error: ".join($arguments,","));
                        }
                    }else{
                        die("$name doesn't exist!");
                    }               
                }           
            }
        }


    }

    echo "<pre>";
    $m = new Modelo();
    print_r(
        array(
            "objetct"=>$m
            ,"getAttr1"=>$m->getAttr1()
            ,"getAttr2"=>$m->getAttr2()
        )
    );
    echo "setAttr1\n";
    $m->setAttr1("by set method");
    print_r(
        array(
            "objetct"=>$m
            ,"getAttr1"=>$m->getAttr1()
            ,"getAttr2"=>$m->getAttr2()
        )
    );

    ?>
person ivandcl    schedule 23.06.2012
comment
Спасибо, а как в этом случае будет работать завершение кода, скажем, в eclipse или netbeans? - person user1476490; 23.06.2012

Вы можете попробовать это:

черта get_set {

public function set($what, $value)
{
    $this->{$what} = $value;
}

public function get($what)
{
    return $this->{$what};
}

}

Он будет работать с общедоступными и защищенными переменными. Вы можете добавить if(!isset($this->{$what})error()

person JG Estiot    schedule 25.05.2015