Объекты Value vs ассоциативные массивы в PHP

(Этот вопрос использует 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 в равной степени приведет к ошибке, которая верна. Однако формальный класс просто чувствует себя более жестким для меня, но я не могу его оправдать.

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

Ответ 1

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

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

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

абсолютно корректен и может оказаться сложной ошибкой.

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

$class->setNaem('John');

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

Ответ 2

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

class PersonalData {
    protected $firstname;
    protected $lastname;

    // Getters/setters here
}

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

  • Нет возможности сделать некоторые опечатки. $data['firtsname'] = 'Chris'; будет работать, а $data->setFirtsname('Chris'); будет вызывать ошибку.
  • Тип hinting: массивы 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());
    }
    
  • Мы можем добавить дополнительный код перед операциями set/get, например, валидацией или протоколированием:

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

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

Ответ 3

Подумав об этом в течение некоторого времени, вот мой собственный ответ.

Главное в предпочтении объектов значений над массивами - Ясность.

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

// 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)
{
}

Ответ 4

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

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

Ответ 5

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

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

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

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

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

$class = new stdClass();
$class->param = 'value';
$class->param2 = 'value2';
return $class;

Ответ 6

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

Ответ 7

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

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

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

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

Ответ 8

Преимущество правильного объекта Value заключается в том, что нет способа сделать недействительным и не изменить существующий (целостность и "неизменность" ). Имея только параметры getter и type hinting, там нет возможности вставлять его в компилируемый код, который вы, очевидно, легко можете сделать с податливыми массивами.

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

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;
}

Ответ 9

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

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

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

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

Ответ 10

Я предпочитаю иметь жестко закодированные свойства, как в вашем втором примере. Я чувствую, что он более четко определяет ожидаемую структуру класса (и все возможные свойства в классе). В отличие от первого примера, который сводится к тому, чтобы всегда помнить о том, чтобы использовать одни и те же ключевые имена. Со вторым вы всегда можете вернуться назад и посмотреть на класс, чтобы получить представление о свойствах, просто взглянув на верхнюю часть файла.

Вам лучше знать, что вы делаете что-то не так со вторым - если вы попытаетесь echo $this->doesntExist, вы получите сообщение об ошибке, тогда как если вы попытаетесь echo array['doesntExist'], вы не будете.

Ответ 11

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