recorrer - Valor de objetos frente a matrices asociativas en PHP
obtener el valor de un array php (11)
(Esta pregunta usa PHP como contexto pero no está restringido solo a PHP. Por ejemplo, cualquier lenguaje con hash incorporado también es relevante)
Veamos este ejemplo (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");
}
El método n. ° 1 es, por supuesto, más terser, pero puede conducir fácilmente a errores tales como
$myFred = makeAFredUsingAssoc();
return $myFred[''naem'']; // notice teh typo here
Por supuesto, uno podría argumentar que $myFred->naem
conducirá igualmente al error, lo cual es cierto. Sin embargo, tener una clase formal me parece más rígido, pero realmente no puedo justificarlo.
¿Cuáles serían las ventajas y desventajas de usar cada enfoque y cuándo debería la gente usar qué enfoque?
Cuando el valor de retorno representa una entidad en su aplicación, debe usar un objeto, ya que este es el propósito de OOP. Si solo desea devolver un grupo de valores no relacionados, entonces no es tan claro. Sin embargo, si es parte de una API pública, una clase declarada sigue siendo la mejor opción.
Déjame hacerte esta pregunta:
¿Qué tiene de diferente hacer un error tipográfico como $myFred[''naem'']
y crear un error tipográfico como $myFred->naem
? El mismo problema todavía existe en ambos casos y ambos errores.
Me gusta usar KISS (mantenerlo simple, estúpido) cuando programo.
- Si simplemente está devolviendo un subconjunto de una consulta de un método, simplemente devuelva una matriz.
- Si está almacenando los datos como una variable pública / privada / estática / protegida en una de sus clases, sería mejor almacenarla como stdClass.
- Si luego va a pasar esto a otro método de clase, puede preferir la tipificación estricta de la clase
Fred
, es decir,public function acceptsClass(Fred $fredObj)
Podría haber creado una clase estándar con la misma facilidad que una matriz si se va a utilizar como valor de retorno. En este caso, puede que le importe menos la tipificación estricta.
$class = new stdClass();
$class->param = ''value'';
$class->param2 = ''value2'';
return $class;
Debajo de la superficie, los dos enfoques son equivalentes. Sin embargo, obtienes la mayoría de los beneficios de OO estándar cuando utilizas una clase: encapsulación, herencia, etc.
Además, mira los siguientes ejemplos:
$arr[''naem''] = ''John'';
es perfectamente válido y podría ser un error difícil de encontrar.
Por otra parte,
$class->setNaem(''John'');
nunca funcionará
Dependiendo de UseCase, podría usar cualquiera o. La ventaja de la clase es que puedo usarlo como un tipo y usar sugerencias de tipo en los métodos o cualquier método de introspección. Si solo quiero pasar un conjunto de datos aleatorios de una consulta o algo, probablemente usaría la matriz. Así que supongo que mientras Fred tenga un significado especial en mi modelo, utilizaría una clase.
En otros comentarios:
Se supone que los ValueObjects son inmutables. Al menos si se refiere a la definición de Eric Evan en Domain Driven Design. En el PoEA de Fowler, los ValueObjects no tienen necesariamente que ser inmutables (aunque se sugiere), pero no deberían tener identidad, lo cual es claramente el caso de Fred .
Después de pensarlo por un tiempo, aquí está mi propia respuesta.
Lo principal de preferir los objetos de valor sobre las matrices es la claridad .
Considera esta función:
// Yes, you can specify parameter types in PHP
function MagicFunction(Fred $fred)
{
// ...
}
versus
function MagicFunction(array $fred)
{
}
La intención es más clara. El autor de la función puede hacer cumplir su requisito.
Más importante aún, como usuario, puedo buscar fácilmente lo que constituye un Fred válido . Solo necesito abrir Fred.php
y descubrir su funcionamiento interno.
Hay un contrato entre el que llama y el que llama. Usando objetos de valor, este contrato se puede escribir como código de verificación sintáctica:
class Fred
{
public $name;
// ...
}
Si utilicé una matriz, solo puedo esperar que mi usuario lea los comentarios o la documentación:
// IMPORTANT! You need to specify ''name'' and ''age''
function MagicFunction(array $fred)
{
}
El beneficio de un objeto de valor adecuado es que no hay forma de convertirlo en uno no válido y no hay forma de cambiar uno que exista (integridad e "inmutabilidad"). Con solo captadores y parámetros de sugerencias tipo, no hay forma de arruinarlo en código compilable, lo que obviamente puede hacer fácilmente con arreglos maleables.
Alternativamente, puede validar en un constructor público y lanzar una excepción, pero esto proporciona un método de fábrica más suave.
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;
}
He trabajado con OOP Languages más de 10 años. Si entiendes cómo funcionan los objetos, te encantará. La herencia, el polimorfismo, la encapsulación y la sobrecarga son la principal ventaja de OOP. Por otro lado, cuando hablamos de PHP tenemos que considerar que PHP no es un lenguaje orientado a objetos con todas las funciones. Por ejemplo, no podemos usar la sobrecarga de métodos o la sobrecarga de constructores (directa).
Las matrices asociativas en PHP son MUY buenas características, pero creo que perjudican las aplicaciones empresariales de php. Cuando escribe código, quiere obtener una aplicación limpia y mantenible.
Otro cree que se pierde con arreglos asociativos es que no se puede usar intellisense.
Así que creo que si desea escribir un código de limpieza y más fácil de mantener, debe usar las características de OOP cuando se proporciona.
Honestamente, me gustan los dos.
- Los arreglos hash son mucho más rápidos que crear objetos, ¡y el tiempo es dinero!
- Pero a JSON no le gustan las matrices de hash (que se parece un poco al OOP OCD).
- Tal vez para proyectos con varias personas, una clase bien definida sería mejor.
- Las matrices Hash pueden requerir más tiempo de CPU y memoria (un objeto tiene una cantidad predefinida), aunque es difícil de asegurar para cada escenario.
Pero lo que realmente apesta es pensar en cuál usar demasiado. Como dije, a JSON no le gustan los hashes. Vaya, utilicé una matriz. Tengo que cambiar unas miles de líneas de código ahora.
No me gusta, pero parece que las clases son la forma más segura de ir.
Prefiero tener propiedades codificadas como en tu segundo ejemplo. Siento que define más claramente la estructura de clase esperada (y todas las propiedades posibles en la clase). A diferencia del primer ejemplo que se reduce a recordar siempre usar los mismos nombres de clave. Con el segundo, siempre puedes volver atrás y mirar la clase para tener una idea de las propiedades simplemente mirando la parte superior del archivo.
Será mejor que sepa que está haciendo algo mal con el segundo: si intenta hacer echo $this->doesntExist
, obtendrá un error, mientras que si intenta hacer un echo array[''doesntExist'']
no lo hará.
Un profesional para el hash: es capaz de manejar combinaciones de nombre-valor que son desconocidas en el momento del diseño.
Una clase simple como esta:
class PersonalData {
protected $firstname;
protected $lastname;
// Getters/setters here
}
Tiene pocas ventajas sobre una matriz.
- No hay posibilidad de hacer algunos errores tipográficos.
$data[''firtsname''] = ''Chris'';
funcionará mientras$data->setFirtsname(''Chris'');
arrojará un error. Indicación tipo: las matrices PHP pueden contener todo (incluso nada) mientras que la clase bien definida contiene solo datos especificados.
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()); }
Podemos agregar un código adicional antes de las operaciones set / get, como validación o registro:
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;
}- Podemos usar todos los beneficios de OOP como herencia, polimorfismo, insinuación tipo, encapsulación, etc.
- Como se mencionó anteriormente, todas nuestras "estructuras" pueden heredar de alguna clase base que proporciona implementación para las interfaces
Countable
,Serializable
oIterator
, por lo que nuestras estructuras podrían usar buclesforeach
etc. - Soporte IDE.
La única desventaja parece ser la velocidad. La creación de una matriz y operar en ella es más rápida. Sin embargo, todos sabemos que en muchos casos el tiempo de CPU es mucho más barato que el tiempo del programador. ;)