php - standard - Mejores prácticas para constructores estáticos
psr 3 php (8)
Además de la respuesta de Jon : Para permitir argumentos de constructor, use lo siguiente:
function create($type) {
$args = func_get_args();
$reflect = new ReflectionClass(array_shift($args));
return $reflect->newInstanceArgs($args);
}
create(''Foo'', ''some'', ''args'')->bar();
Documentación: ReflectionClass
->
newInstanceArgs
Quiero crear una instancia de una clase y llamar a un método en esa instancia, en una sola línea de código.
PHP no permitirá llamar a un método en un constructor regular:
new Foo()->set_sth(); // Outputs an error.
Entonces estoy usando, si puedo llamarlo así, un constructor estático:
Foo::construct()->set_sth();
Aquí está mi pregunta:
¿Es una buena práctica usar constructores estáticos como ese y, en caso afirmativo, cómo recomendaría que se nombren los métodos para estos constructores estáticos?
He estado dudando sobre las siguientes opciones:
Foo::construct();
Foo::create();
Foo::factory()
Foo::Foo();
constructor::Foo();
El nombramiento de cualquier método debe ser con la intención de revelar nombres . No puedo decir lo que hace ''Foo :: factory''. Trate de construir a un lenguaje de nivel superior:
User::with100StartingPoints();
Esto sería lo mismo que:
$user = new User();
$user->setPointsTo(100);
También puede probar fácilmente si User :: with100StartingPoints () es igual a esto.
Estos se denominan métodos de creación , y normalmente los createXXX()
como createById()
o createEmptyCatalog()
. No solo proporcionan una buena forma de revelar las diferentes intenciones de los constructores de un objeto, sino que también permiten el encadenamiento inmediato de métodos en una interfaz fluida.
echo Html_Img::createStatic(''/images/missing-image.jpg'')
->setSize(60, 90)
->setTitle(''No image for this article'')
->setClass(''article-thumbnail'');
Los constructores estáticos (o "constructores nombrados") solo son beneficiosos para demostrar una intención, como dice @koen.
Sin embargo, desde la versión 5.4, apareció algo llamado "desreferenciación", lo que le permite crear una instancia de clase en línea directamente con una llamada de método.
(new MyClass($arg1))->doSomething(); // works with newer versions of php
Por lo tanto, los constructores estáticos solo son útiles si tiene varias formas de crear una instancia de sus objetos. Si solo tiene uno (siempre el mismo tipo de argumentos y número de argumentos), no hay necesidad de constructores estáticos.
Pero si tiene múltiples formas de crear instancias, entonces los constructores estáticos son muy útiles, ya que evita contaminar su constructor principal con una inútil verificación de argumentos, lo que debilita las restricciones de idiomas.
Ejemplo:
<?php
class Duration
{
private $start;
private $end;
// or public depending if you still want to allow direct instantiation
private function __construct($startTimeStamp = null, $endTimestamp = null)
{
$this->start = $startTimestamp;
$this->end = $endTimestamp;
}
public static function fromDateTime(/DateTime $start, /DateTime $end)
{
return new self($start->format(''U''), $end->format(''U''));
}
public static function oneDayStartingToday()
{
$day = new self;
$day->start = time();
$day->end = (new /DateTimeImmutable)->modify(''+1 day'')->format(''U'');
return $day;
}
}
¡Como puede ver en oneDayStartingToday
, el método estático puede acceder a los campos privados de la instancia! ¿Loco no es así? :)
Para una mejor explicación, vea http://verraes.net/2014/06/named-constructors-in-php/
Probablemente no sea una buena práctica , pero podría usar el hecho de que las funciones y las clases tienen dos espacios de nombres diferentes: puede tener una función que tenga el mismo nombre que una clase.
Esto permite escribir este tipo de código, por ejemplo:
function MyClass() {
return new MyClass();
}
class MyClass {
public function __construct() {
$this->a = "plop";
}
public function test() {
echo $this->a;
}
protected $a;
}
Tenga en cuenta que he definido una función llamada MyClass
y una clase con el mismo nombre.
Entonces, puedes escribir esto:
MyClass()->test();
Lo que funcionará a la perfección, y no obtendrá ningún error: aquí obtendrá el siguiente resultado:
plop
Propel utiliza un método estático "crear". Yo iría con eso. Este método hace que el código sea más fácil de probar en lugar de usar métodos estáticos para realizar la lógica de negocios.
<?php
class MyClass
{
public static function create()
{
return new MyClass();
}
public function myMethod()
{
}
}
Además, también puedes pasar parámetros al constructor. Por ejemplo:
<?php
class MyClass
{
public function __construct($param1, $param2)
{
//initialization using params
}
public static function create($param1, $param2)
{
return new MyClass($param1, $param2); // return new self($param1, $param2); alternative ;)
}
public function myMethod()
{
}
}
En cualquier caso, podrá invocar myMethod justo después del método de creación.
<?php
MyClass::create()->myMethod();
// or
MyClass::create($param1, $param2)->myMethod();
Si no necesita una referencia al Foo
recién construido, ¿por qué no hace simplemente que set_sth
una función static
(y haga que cree un nuevo Foo
internamente si es necesario)?
Si necesita obtener la referencia, ¿cómo lo haría? return $this
en set_sth
? Pero entonces set_sth
puede convertirse en una función de fábrica de todos modos.
La única situación en la que puedo pensar es si desea llamar a métodos chainable (como en una interfaz fluida) en una instancia recién construida, todo en una expresión. ¿Es eso lo que estás tratando de hacer?
De todos modos, puede utilizar una función de fábrica de propósito general para todos los tipos de objetos, por ejemplo,
function create_new($type) {
return new $type;
}
create_new(''Foo'')->set_sth();
Un poco tarde para la fiesta pero creo que esto podría ayudar.
class MyClass
{
function __construct() {
// constructor initializations here
}
public static myMethod($set = null) {
// if myclass is not instantiated
if (is_null($set)) {
// return new instance
$d = new MyClass();
return $d->Up(''s'');
} else {
// myclass is instantiated
// my method code goes here
}
}
}
esto puede ser utilizado como
$result = MyClass::myMethod();
los parámetros opcionales se pueden pasar a través de __constructor o myMethod.
Este es mi primer post y espero tener los trucos correctos.