2008-08-28 6 views
8

Voy a intentar algo con el formato de esta pregunta y estoy abierto a sugerencias sobre una mejor manera de manejarlo.¿Es esta una forma razonable de manejar getters/setters en una clase de PHP?

No quería solo descargar un montón de código en la pregunta, así que he publicado el código de la clase en refactormycode.

base class for easy class property handling

Mi pensamiento era que la gente puede o fragmentos de correos código aquí o hacer cambios en refactormycode y colocar enlaces a sus refactorizaciones. Formularé votaciones y aceptaré una respuesta (suponiendo que haya un "ganador" claro) en función de eso.

En cualquier caso, a la propia clase:

veo un gran debate acerca de los métodos de la clase de captador/definidor y es mejor acceder simplemente variables de las propiedades simples directa o debería todas las clases tienen explícita get/set métodos definidos, bla, bla, bla. Me gusta la idea de tener métodos explícitos en caso de que tenga que agregar más lógica más adelante. Entonces no tienes que modificar ningún código que use la clase. Sin embargo, me gusta tener un millón de funciones que se ven así:

public function getFirstName() 
{ 
    return $this->firstName; 
} 
public function setFirstName($firstName) 
{ 
    return $this->firstName; 
} 

Ahora estoy seguro de que no soy la primera persona para hacer esto (estoy esperando que hay una mejor forma de hacerlo que alguien pueda sugerir a mí).

Básicamente, la clase PropertyHandler tiene un __call método mágico. Todos los métodos que vienen a través de __call que comienzan con "get" o "set" se enrutan a funciones que establecen o recuperan valores en una matriz asociativa. La clave en la matriz es el nombre del método de llamada después de obtener o establecer. Por lo tanto, si el método que entra en __call es "getFirstName", la clave del arreglo es "FirstName".

Me gustó usar __call porque se ocupará automáticamente del caso en que la subclase ya tenga definido un método "getFirstName". Mi impresión (y puedo estar equivocado) es que los __get & __conjuntos mágicos no hacen eso.

Así que aquí es un ejemplo de cómo funcionaría:

class PropTest extends PropertyHandler 
{ 
    public function __construct() 
    { 
     parent::__construct(); 
    } 
} 

$props = new PropTest(); 

$props->setFirstName("Mark"); 
echo $props->getFirstName(); 

en cuenta que PropTest no tiene realmente métodos "getFirstName" "setFirstName" o PropertyHandler y tampoco lo hace. Todo lo que hace es manipular valores de matriz.

El otro caso sería donde su subclase ya está extendiendo algo más. Como no puede tener una herencia múltiple verdadera en PHP, puede hacer que su subclase tenga una instancia de PropertyHandler como una variable privada. Tienes que agregar una función más, pero luego las cosas se comportan exactamente de la misma manera.

class PropTest2 
{ 
    private $props; 

    public function __construct() 
    { 
     $this->props = new PropertyHandler(); 
    } 

    public function __call($method, $arguments) 
    { 
     return $this->props->__call($method, $arguments); 
    } 
} 

$props2 = new PropTest2(); 

$props2->setFirstName('Mark'); 
echo $props2->getFirstName(); 

Observe cómo la subclase tiene un método __call que simplemente pasa a lo largo de todo el método __call PropertyHandler.


Otro buen argumento contra el manejo de getters y setters de esta manera es que hace que sea muy difícil de documentar.

De hecho, es básicamente imposible utilizar cualquier tipo de herramienta de generación de documentos ya que los métodos explícitos que se deben documentar no existen.

Por el momento, he abandonado este enfoque. Fue un ejercicio de aprendizaje interesante, pero creo que sacrifica demasiada claridad.

+0

Por curiosidad, ¿qué método usas ahora? – SeanJA

+0

Tener getters ** y ** setters en la misma clase es un signo de que a la clase le falta su código. Investigue el código que usa los getters y setters de la clase y muévalo dentro de la clase porque aquí es donde pertenece. – axiac

Respuesta

5

La manera de hacerlo es la siguiente:

class test { 
    protected $x=''; 
    protected $y=''; 

    function set_y ($y) { 
     print "specific function set_y\n"; 
     $this->y = $y; 
    } 

    function __call($function , $args) { 
     print "generic function $function\n"; 
     list ($name , $var) = split ('_' , $function); 
     if ($name == 'get' && isset($this->$var)) { 
      return $this->$var; 
     } 
     if ($name == 'set' && isset($this->$var)) { 
      $this->$var= $args[0]; 
      return; 
     } 
     trigger_error ("Fatal error: Call to undefined method test::$function()"); 
    } 
} 

$p = new test(); 
$p->set_x(20); 
$p->set_y(30); 
print $p->get_x(); 
print $p->get_y(); 

$p->set_z(40); 

¿Cuál es la salida (saltos de línea añadido para mayor claridad)

generic function set_x 
specific function set_y 

generic function get_x 
20 
generic function get_y 
30 

generic function set_z 
Notice: Fatal error: Call to undefined method set_z() in [...] on line 16 
+0

(moviendo el comentario de OP): @Pat Eso es interesante. Ciertamente, mucho menos código. Pero aún tiene que declarar explícitamente cada variable de propiedad subyacente, ¿verdad? –

+0

"split" ha quedado obsoleto a partir de PHP 5.3.0 –

2

sí que es cierto las variables tienen que ser declarado de forma manual, pero me parece que mejor, ya que me temo un error tipográfico en el setter

$props2->setFristName('Mark'); 

generará automáticamente una nueva propiedad (FristName en lugar de FirstName) que dificultará la depuración.

1

Me gusta tener métodos en lugar de solo usar campos públicos, pero mi problema con la implementación predeterminada de PHP (usando __get() y __set()) o su implementación personalizada es que no está estableciendo getters y setters en una por propiedad. Mi problema con esto es que agregar "más lógica más tarde" requiere que agregue lógica general que se aplica a todas las propiedades a las que se accede con el captador/definidor o que usa if o cambia las declaraciones para evaluar a qué propiedad está accediendo para que pueda aplicar lógica específica.

Me gusta su solución, y lo aplaudo por ello. No estoy satisfecho con las limitaciones que PHP tiene cuando se trata de métodos de acceso implícitos.

+0

(moviendo el comentario desde OP) @Brian Ese es un punto interesante. Tendré que reflexionar un poco sobre eso. –

3

@Brian

Mi problema con esto es que la adición de "más lógica más adelante" requiere que se agrega la lógica general que se aplica a todas las propiedades que se accede con el captador/definidor o que utilice if o cambiar declaraciones para evaluar a qué propiedad está accediendo para que pueda aplicar una lógica específica.

Eso no es del todo cierto. Tome mi primer ejemplo:

class PropTest extends PropertyHandler 
{ 
    public function __construct() 
    { 
     parent::__construct(); 
    } 
} 

$props = new PropTest(); 

$props->setFirstName("Mark"); 
echo $props->getFirstName(); 

Digamos que necesito agregar algo de lógica para validar FirstNames. Todo lo que tengo que hacer es agregar un método setFirstName a mi subclase y ese método se usa automáticamente en su lugar.

class PropTest extends PropertyHandler 
{ 
    public function __construct() 
    { 
     parent::__construct(); 
    } 

    public function setFirstName($name) 
    { 
     if($name == 'Mark') 
     { 
      echo "I love you, Mark!"; 
     } 
    } 
} 

no sólo estoy satisfecho con las limitaciones que PHP tiene cuando se trata de métodos de acceso implícitas.

estoy completamente de acuerdo. Me gusta la forma en que Python maneja esto (mi implementación es solo una tosca estafa).

1

@ Marcos

Pero incluso su método requiere una nueva declaración del método, y es un tanto quita la ventaja de ponerlo en un método para que pueda añadir más lógica, ya que para añadir más lógica requiere la declaración anticuada del método, de todos modos. En su estado predeterminado (que es donde es impresionante en lo que detecta/hace), su técnica no ofrece ninguna ventaja (en PHP) sobre los campos públicos. Está restringiendo el acceso al campo pero dando carta blanca a través de métodos de acceso que no tienen ninguna restricción propia. No estoy al tanto de que los accesos explícitos sin marcar ofrecen ninguna ventaja sobre los campos públicos en cualquier lenguaje, pero las personas pueden y deben sentirse en libertad de corregirme si me equivoco.

+0

Todas las propiedades que están bien con un getter/setter básico utilizarán las llamadas mágicas. Si necesita agregar código adicional, puede crear getters/setters solo para esas propiedades. Nuevamente, esto puede crear cierta confusión, pero es una opción. – Galen

0

Siempre he manejado este problema de manera similar con un __call que termina siendo un código de placa de caldera en muchas de mis clases. Sin embargo, es compacto y utiliza las clases de reflexión para agregar solo getters/setters a las propiedades que ya ha establecido (no agregará nuevas). Simplemente agregando el getter/setter explícitamente agregará funcionalidad más compleja. Se espera que sea

código es el siguiente:

/** 
* Handles default set and get calls 
*/ 
public function __call($method, $params) { 

    //did you call get or set 
    if (preg_match("|^[gs]et([A-Z][\w]+)|", $method, $matches)) { 

     //which var? 
     $var = strtolower($matches[1]); 

     $r = new ReflectionClass($this); 
     $properties = $r->getdefaultProperties(); 

     //if it exists 
     if (array_key_exists($var,$properties)) { 
      //set 
      if ('s' == $method[0]) { 
       $this->$var = $params[0]; 
      } 
      //get 
      elseif ('g' == $method[0]) { 
       return $this->$var; 
      } 
     } 
    } 
} 

La adición de este a una clase en la que han declarado las propiedades predeterminadas como:

class MyClass { 
    public $myvar = null; 
} 

$test = new MyClass; 
$test->setMyvar = "arapaho"; 

echo $test->getMyvar; //echos arapaho  

La clase reflexión puede añadir algo de utilidad para lo estabas proponiendo Buena solución @Mark.

0

Recientemente, también pensé en manejar getters y setters de la manera que sugeriste (el segundo enfoque fue mi favorito, es decir, el $ private props array), pero lo descarté porque no hubiera funcionado en mi aplicación .

Estoy trabajando en una aplicación bastante grande basada en SoapServer y la interfaz soap de PHP 5 inyecta los valores que se transmiten vía soap directamente en la clase asociada, sin preocuparse por las propiedades existentes o no existentes en la clase.

0

no puedo evitar poner en mis 2 centavos ...

he llevado usar __get y __set en esta casa http://gist.github.com/351387 (similar a la forma en que lo hace la doctrina), a continuación, sólo alguna vez acceder a las propiedades a través del $obj->var en un fuera de la clase. De esta manera puede anular la funcionalidad según sea necesario en lugar de hacer una gran función __get o __set, o anular __get y __set en las clases secundarias.

Cuestiones relacionadas