Объекты значений против ассоциативных массивов в PHP - PullRequest
37 голосов
/ 13 января 2010

(Этот вопрос использует PHP в качестве контекста, но не ограничивается только PHP. Например, любой язык со встроенным хешем также имеет значение)

Давайте посмотрим на этот пример (PHP):

function makeAFredUsingAssoc()
{
    return array(
        'id'=>1337,
        'height'=>137,
        'name'=>"Green Fred");
}

Versus:

class Fred
{
    public $id;
    public $height;
    public $name;

    public function __construct($id, $height, $name)
    {
        $this->id = $id;
        $this->height = $height;
        $this->name = $name;
    }
}

function makeAFredUsingValueObject()
{
    return new Fred(1337, 137, "Green Fred");
}

Метод № 1, конечно, более краткий, однако он может легко привести к ошибке, такой как

$myFred = makeAFredUsingAssoc();
return $myFred['naem']; // notice teh typo here

Конечно, можно утверждать, что $myFred->naem в равной степени приведет к ошибке, и это правда. Однако, формальный класс мне кажется более жестким, но я не могу это оправдать.

Каковы были бы плюсы / минусы использования каждого подхода и когда люди должны использовать какой подход?

Ответы [ 11 ]

28 голосов
/ 05 октября 2010

Простой класс, подобный этому:

class PersonalData {
    protected $firstname;
    protected $lastname;

    // Getters/setters here
}

Имеет несколько преимуществ перед массивом.

  1. Нет возможности сделать опечатки. $data['firtsname'] = 'Chris'; будет работать, в то время как $data->setFirtsname('Chris'); выдаст ошибку.
  2. Подсказка типа: массивы PHP могут содержать все (включая ничего), в то время как четко определенный класс содержит только указанные данные.

    public function doSth(array $personalData) {
        $this->doSthElse($personalData['firstname']); // What if "firstname" index doesn't exist?
    }
    
    
    public function doSth(PersonalData $personalData) {
        // I am guaranteed that following method exists. 
        // In worst case it will return NULL or some default value
        $this->doSthElse($personalData->getFirstname());
    }
    
  3. Мы можем добавить дополнительный код перед операциями set / get, такими как проверка или ведение журнала:

    public function setFirstname($firstname) {
        if (/* doesn't match "firstname" regular expression */) {
            throw new InvalidArgumentException('blah blah blah');
        }
    
    
    <pre><code>if (/* in debbug mode */) {
        log('Firstname set to: ' . $firstname);
    }
    
    
    $this-&gt;firstname = $firstname;
    
    }
  4. Мы можем использовать все преимущества ООП, такие как наследование, полиморфизм, подсказки типов, инкапсуляция и так далее ...
  5. Как упоминалось ранее, все наши "структуры" могут наследоваться от некоторого базового класса, который обеспечивает реализацию для интерфейсов Countable, Serializable или Iterator, поэтому наши структуры могут использовать циклы foreach и т. Д.
  6. Поддержка IDE.

Единственный недостаток - скорость. Создание массива и работа с ним быстрее. Однако все мы знаем, что во многих случаях процессорное время намного дешевле, чем время программиста. ;)

27 голосов
/ 13 января 2010

Под поверхностью два подхода эквивалентны. Однако при использовании класса вы получаете большинство стандартных преимуществ ОО: инкапсуляция, наследование и т. Д.

Также посмотрите на следующие примеры:

$arr['naem'] = 'John';

совершенно корректно и может быть трудной ошибкой для поиска.

С другой стороны,

$class->setNaem('John');

никогда не сработает.

8 голосов
/ 05 октября 2010

Подумав некоторое время, вот мой собственный ответ.

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

Рассмотрим эту функцию:

// Yes, you can specify parameter types in PHP
function MagicFunction(Fred $fred)
{
    // ...
}

против

function MagicFunction(array $fred)
{
}

Цель более ясна.Автор функции может выполнить свое требование.

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

Между вызывающим абонентом и вызываемым абонентом существует договор.Используя объекты-значения, этот контракт можно записать в виде проверенного синтаксиса кода:

class Fred
{
    public $name;
    // ...
}

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

// IMPORTANT! You need to specify 'name' and 'age'
function MagicFunction(array $fred)
{
}
6 голосов
/ 13 января 2010

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

В записке:
ValueObjects должны быть неизменными. По крайней мере, если вы ссылаетесь на определение Эрика Эвана в Domain Driven Design. В PoEA Фаулера ValueObjects не обязательно должны быть неизменяемыми (хотя это и предлагается), но они не должны иметь идентичность, что явно имеет место для Fred .

3 голосов
/ 13 января 2010

Позвольте мне задать вам этот вопрос:

Чем отличается опечатка типа $myFred['naem'] от опечатки типа $myFred->naem? Одна и та же проблема все еще существует в обоих случаях, и они оба ошибки.

Мне нравится использовать KISS (будь проще, глупее), когда я программирую.

  • Если вы просто возвращаете подмножество запроса из метода, просто верните массив.
  • Если вы храните данные как переменную public / private / static / protected в одном из ваших классов, было бы лучше сохранить их как stdClass.
  • Если вы собираетесь позже передать это другому методу класса, вы можете предпочесть строгую типизацию класса Fred, т.е. public function acceptsClass(Fred $fredObj)

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

$class = new stdClass();
$class->param = 'value';
$class->param2 = 'value2';
return $class;
2 голосов
/ 13 января 2010

Pro для хэша: он способен обрабатывать комбинации имя-значение, которые неизвестны во время разработки.

1 голос
/ 29 января 2012

Я работаю с ООП Языки более 10 лет. Если вы понимаете, как работают объекты, вам это понравится. Наследование, полиморфизм, инкапсуляция, перегрузка являются ключевыми преимуществами ООП. С другой стороны, когда мы говорим о PHP, мы должны учитывать, что PHP не является полнофункциональным объектно-ориентированным языком. Например, мы не можем использовать перегрузку метода или конструктора (просто).

Ассоциативные массивы в PHP - ОЧЕНЬ хорошая функция, но я думаю, что это вредит корпоративным приложениям php. Когда вы пишете код, вы хотите получить чистое и поддерживаемое приложение.

Другое мнение, что вы проигрываете с ассоциативными массивами, - это то, что вы не можете использовать intellisense.

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

1 голос
/ 16 апреля 2011

Преимущество правильного Объекта Значения состоит в том, что нет никакого способа фактически сделать недопустимый объект и нет способа изменить существующий объект (целостность и «неизменность»). Имея только геттеры и параметры хинтинга типов, НЕТ СПОСОБА запутать его в компилируемом коде, что, очевидно, легко сделать с податливыми массивами.

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

class Color
{
    public static function create($name, $rgb) {
        // validate both
        if ($bothValid) {
            return new self($name, $rgb);
        } else {
            return false;
        }
    }
    public function getName() { return $this->_name; }
    public function getRgb() { return $this->_rgb; }

    protected function __construct($name, $rgb)
    {
        $this->_name = $name;
        $this->_rgb = $rgb;
    }
    protected $_name;
    protected $_rgb;
}
1 голос
/ 05 октября 2010

Честно говоря, я люблю их обоих.

  • Хэш-массивы работают намного быстрее, чем создание объектов, а время - деньги!
  • Но JSON не нравятся хэш-массивы (которые немного похожи на OOP OCD).
  • Может быть, для проектов с несколькими людьми хорошо определенный класс был бы лучше.
  • Хеш-массивы могут занимать больше процессорного времени и памяти (объект имеет заранее определенное количество), хотя в каждом сценарии это трудно гарантировать.

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

Мне это не нравится, но кажется, что занятия безопаснее.

1 голос
/ 13 января 2010

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

...