mejor práctica para inicializar miembros de clase en php

10

Tengo muchos códigos como este en mis constructores:

function __construct($params) {

    $this->property = isset($params['property']) ? $params['property'] : default_val;

}

¿Es mejor hacer esto en lugar de especificar el valor predeterminado en la definición de propiedad? es decir public $property = default_val? A veces hay lógica para el valor predeterminado, y algunos valores predeterminados se toman de otras propiedades, por lo que estaba haciendo esto en el constructor.

¿Debo usar setters para que toda la lógica de los valores predeterminados esté separada?

rgvcorley
fuente

Respuestas:

8

He tenido este tipo de debate filosófico conmigo mismo antes. Aquí es donde estoy la mayor parte del tiempo, aunque me doy cuenta de que esta es una respuesta basada en la opinión:

Una cosa que veo que podría ayudar a responder la pregunta es el paso de $ params que pueden tener o no atributos / miembros de la matriz configurados.

Con los años he llegado a esta conclusión:

Evite el paso de matrices.

¿Por qué? Bueno, no hay forma de establecer o tener valores centinela definidos para argumentos pasados ​​opcionales.

En otras palabras, con el código que especificó, no puede hacer algo como esto:

function __construct($arg1 = NULL, $arg2 = DEFAULT_VAL) {

    $this->arg1 = $arg1;

    $this->arg2 = $arg2;

}

$ arg1 y $ arg2 son argumentos opcionales; si no se pasan, tienen NULL y DEFAULT_VAL respectivamente, no es necesario verificarlo explícitamente.

Quizás esto parezca algo arbitrario.

Creo que entiendo lo que estás tratando de lograr: la aprobación de una sola referencia en lugar de toneladas de argumentos. Esto me lleva a mi próxima conclusión:

Si no pasa variables "atómicas" (cadenas, enteros, literales), pase los objetos.

Aquí hay beneficios de rendimiento ya que el paso de objetos se realiza por referencia (aunque creo que las matrices son más o menos las mismas dentro de PHP).

Entonces podrías hacer algo como:

function __construct(MyAwesomeObject $oArg) {

        $this->oArg = $oArg;

    }

Se garantizaría que el argumento objeto pasado tenga "propiedad1", "propiedad2" aunque posiblemente con los valores predeterminados.

Además, aquí puede escribir una sugerencia y un buen IDE también completará automáticamente correctamente el código.

Pero rápidamente nos damos cuenta de que tenemos una cosa de gallina y huevo: estás construyendo un objeto con argumentos de objetos pasados ​​que ellos mismos necesitan construir en algún momento.

¿A dónde nos lleva esto? Bueno, he llegado a la conclusión de que eventualmente todas las clases se reducen a, por falta de un mejor término, variables "atómicas" (cadenas, flotantes, dobles, ints, recursos, entiendes mi punto), y que tiendo a intentar para construir todas las clases con esos tipos u objetos variables, pero no matrices.

Entonces, ¿respondí tu pregunta? Probablemente no exactamente. Pero espero haber ilustrado algo útil, aunque algo estilístico. Creo que el código es un poco más limpio, más legible y menos costoso.

Ahora, esto no quiere decir que no debe desinfectar su aporte. Esa es otra discusión completamente.

Espero que esto ayude.

ekeyser
fuente
1
Es un buen punto y muy útil para las solicitudes de IDE, pero hay demasiadas propiedades de objeto para pasarlas todas como parámetros. Si tiene 7 argumentos, digamos 5 de los cuales son opcionales, terminará con cosas como new object($param1,-some default value so I can specify the next parameter-, $param3);y, por lo tanto, tiene sus valores predeterminados codificados en varios lugares diferentes
rgvcorley
3

Aunque tiendo a evitar pasar matrices a un constructor, a veces me resulta necesario procesar un valor de matriz entrante (como para una clase que lee valores de un archivo de configuración).

En un caso como este, aprovecharé las funciones de matriz de PHP para asegurarme de que estoy trabajando exactamente con lo que creo que estoy trabajando:

public function import( array $incoming )
{
  $defaults = array(
      'foo' => DEFAULT_FOO
    , 'bar' => DEFAULT_BAR
    ...
  );

  $values = array_merge($defaults, array_intersect_key($incoming, $defaults));

  ...
}

Esa última línea utiliza array_merge()para sobrescribir cualquier valor $defaultscon sus valores correspondientes $incoming. También lo uso array_intersect_key()para asegurarme de que la matriz resultante no contenga claves adicionales que la clase / método no sabría cómo procesar.


fuente
Hola, ¿cuál es el comportamiento si $ por defecto contiene una matriz en lugar de variables simples?
Pol Dellaiera
@PolDellaiera Mira array_merge_recursive.