(Этот вопрос использует PHP как контекст, но не ограничивается только PHP. Например, подходит любой язык со встроенным хешем)
Давайте посмотрим на этот пример (PHP):
function makeAFredUsingAssoc() { return array( 'id'=>1337, 'height'=>137, 'name'=>"Green Fred"); }
Против:
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
равной степени приведет к ошибке, что верно. Однако, если формальный класс просто чувствует себя более жестким для меня, но я не могу его оправдать.
Какими будут плюсы и минусы использования каждого подхода и когда люди должны использовать какой подход?
Под поверхностью оба подхода эквивалентны. Тем не менее, вы получаете большую часть стандартных преимуществ OO при использовании класса: инкапсуляция, наследование и т. Д.
Кроме того, рассмотрите следующие примеры:
$arr['naem'] = 'John';
совершенно верен и может быть сложной ошибкой для поиска.
С другой стороны,
$class->setNaem('John');
никогда не будет работать.
Простой класс, подобный этому:
class PersonalData { protected $firstname; protected $lastname; // Getters/setters here }
Имеет несколько преимуществ перед массивом.
$data['firtsname'] = 'Chris';
будет работать в то время как $data->setFirtsname('Chris');
будет вызывать ошибку en. Тип подсказки: массивы 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
петли и т. Д. Единственный недостаток – скорость. Создание массива и работа на нем происходит быстрее. Однако мы все знаем, что во многих случаях время процессора намного дешевле, чем время программиста. 😉
Подумав об этом в течение некоторого времени, вот мой собственный ответ.
Главное в предпочтении объектов ценности над массивами – ясность .
Рассмотрим эту функцию:
// 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) { }
В зависимости от UseCase я могу использовать либо или. Преимущество класса заключается в том, что я могу использовать его как Тип и использовать подсказки типа для методов или любых методов интроспекции. Если я просто хочу передать некоторый случайный набор данных из запроса или что-то еще, я, скорее всего, буду использовать массив. Поэтому я предполагаю, что до тех пор, пока Фред имеет особое значение в моей модели, я бы использовал класс.
На стороне:
ValueObjects должны быть неизменными. По крайней мере, если вы ссылаетесь на определение Эрика Эвана в Domain Driven Design. В PoEA от Fowler, ValueObjects необязательно должны быть неизменными (хотя это предлагается), но они не должны иметь идентичности, что явно имеет место с Фредом .
Позвольте мне задать вам этот вопрос:
Что отличает опечатка, как $myFred['naem']
и делает опечатку вроде $myFred->naem
? Такая же проблема все еще существует в обоих случаях, и они оба ошибочны.
Когда я программирую, мне нравится использовать KISS (держать его простым, глупым).
Fred
, то есть public function acceptsClass(Fred $fredObj)
Вы могли бы так же легко создать стандартный класс, а не массив, если он будет использоваться как возвращаемое значение. В этом случае вы можете заботиться о строгой печати.
$class = new stdClass(); $class->param = 'value'; $class->param2 = 'value2'; return $class;
Когда возвращаемое значение представляет собой объект в вашем приложении, вы должны использовать объект, поскольку это цель ООП. Если вы просто хотите вернуть группу несвязанных значений, тогда это не так ясно. Однако, если это часть публичного API, объявленный класс по-прежнему остается лучшим способом.
Честно говоря, я люблю их обоих.
Но то, что действительно отстой, – это думать о том, какой из них использовать слишком много. Как я уже сказал, JSON не любит хэши. К сожалению, я использовал массив. Теперь мне нужно изменить несколько тысяч строк кода.
Мне это не нравится, но кажется, что классы – это более безопасный способ.
Преимущество надлежащего объекта значения заключается в том, что нет возможности фактически сделать недопустимым и не изменить существующий (целостность и «неизменность»). С помощью только параметров getter и type hinting нет возможности использовать его в компилируемом коде, который, очевидно, легко можно сделать с податливыми массивами.
В качестве альтернативы вы можете проверить в публичном конструкторе и выбросить исключение, но это обеспечивает более мягкий заводский метод.
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; }
Я работаю с языками ООП более 10 лет. Если вы понимаете, как работают объекты, вам это понравится. Наследование, полиморфизм, инкапсуляция, перегрузка являются ключевым преимуществом ООП. С другой стороны, когда мы говорим о PHP, мы должны учитывать, что PHP не является полнофункциональным объектно-ориентированным языком. Например, мы не можем использовать перегрузку метода или перегрузку конструктора (прямолинейно).
Ассоциативные массивы на PHP – это очень хорошая функция, но я считаю, что вредит корпоративным приложениям php. Когда вы пишете код, вы хотите получить чистое и поддерживаемое приложение.
Другое мнение, что вы теряете ассоциативные массивы, заключается в том, что вы не можете использовать intellisense.
Поэтому я думаю, что если вы хотите написать код и более удобный код, вам нужно использовать функции OOP, когда они предоставляются.
Я предпочитаю иметь жестко закодированные свойства, как в вашем втором примере. Я чувствую, что он более четко определяет ожидаемую структуру класса (и все возможные свойства в классе). В отличие от первого примера, который сводится к тому, чтобы всегда помнить о том, чтобы использовать одни и те же ключевые имена. Со вторым вы всегда можете вернуться и посмотреть на класс, чтобы получить представление о свойствах, просто взглянув на верхнюю часть файла.
Вам лучше понять, что вы делаете что-то не так со вторым – если вы попытаетесь выполнить echo $this->doesntExist
вы получите сообщение об ошибке, тогда как если вы попытаетесь выполнить echo array['doesntExist']
вы этого не сделаете.
Про хэш: он способен обрабатывать комбинации значений имен, которые неизвестны во время разработки.