Лучшие практики для статических конструкторов - PullRequest
21 голосов
/ 20 марта 2011

Я хочу создать экземпляр класса и вызвать метод для этого экземпляра в одной строке кода.

PHP не позволяет вызывать метод обычного конструктора:

new Foo()->set_sth(); // Outputs an error.

Итак, я использую, если можно так сказать, статический конструктор:

Foo::construct()->set_sth();

Вот мой вопрос:

Является ли использование статических конструкторов подобным образом хорошей практикой, и если да, как бы вы рекомендовали называть методы для этих статических конструкторов?

Я колебался по поводу следующих опций:

Foo::construct();
Foo::create();
Foo::factory()
Foo::Foo();
constructor::Foo();

Ответы [ 8 ]

11 голосов
/ 20 марта 2011

Имена любого метода должны быть с намерением раскрыть имена .Я не могу сказать, что делает Foo :: factory.Попробуйте построить язык более высокого уровня:

User::with100StartingPoints();

Это было бы так же, как:

$user = new User();
$user->setPointsTo(100);

Вы также можете легко проверить, равен ли User :: with100StartingPoints () этому.

9 голосов
/ 15 сентября 2014

Статические конструкторы (или «именованные конструкторы») полезны только для доказательства намерения, как говорит @koen.

Однако, начиная с 5.4, появилось нечто, называемое разыменованием, что позволяет вам непосредственно создавать экземпляры классов с помощью вызова метода.

(new MyClass($arg1))->doSomething(); // works with newer versions of php

Итак, статические конструкторы полезны, только если у вас есть несколько способов создания экземпляров ваших объектов. Если у вас есть только один (всегда один и тот же тип аргументов и количество аргументов), статические конструкторы не нужны.

Но если у вас есть несколько способов создания экземпляров, статические конструкторы очень полезны, так как они не загрязняют ваш главный конструктор бесполезной проверкой аргументов, ослабляя языковые ограничения.

Пример:

<?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;
}

}

Как вы можете видеть в oneDayStartingToday, статический метод может получить доступ к закрытым полям экземпляра! Сумасшедший не так ли? :)

Более подробное объяснение см. http://verraes.net/2014/06/named-constructors-in-php/

8 голосов
/ 20 марта 2011

Если вам не нужна ссылка на недавно созданный Foo, почему бы вам просто не сделать set_sth static функцией (и заставить ее создать новый Foo внутри, если требуется)?

Если вам нужно нужно достать ссылку, как бы вы это сделали?return $this в set_sth?Но тогда set_sth в любом случае можно превратить в фабричную функцию.

Единственная ситуация, о которой я могу подумать, - это если вы хотите вызывать цепочечные методы (например, в свободном интерфейсе) для вновь созданного экземпляра, все в одномвыражение.Это то, что вы пытаетесь сделать?

В любом случае, вы можете использовать фабричную функцию общего назначения для всех типов объектов, например

function create_new($type) {
    return new $type;
}

create_new('Foo')->set_sth();
6 голосов
/ 20 марта 2011

Вероятно, это не совсем лучшая практика , но вы могли бы использовать тот факт, что функции и классы имеют два разных пространства имен: у вас может быть функция, имя которой совпадает с именем класса.

Это позволяет написать такой код, например:

function MyClass() {
    return new MyClass();
}

class MyClass {
    public function __construct() {
        $this->a = "plop";
    }
    public function test() {
        echo $this->a;
    }
    protected $a;
}

Обратите внимание, что я определил функцию с именем MyClass и класс с тем же именем.


Затем вы можете написать это:

MyClass()->test();

Что будет работать отлично, и не даст вам никакой ошибки - здесь вы получите следующий вывод:

plop
4 голосов
/ 23 марта 2011

Это так называемые методы создания , и я обычно называю их createXXX(), например createById() или createEmptyCatalog().Они не только предоставляют хороший способ раскрыть различные намерения конструкторов объекта, но и обеспечивают немедленную цепочку методов в свободном интерфейсе.

echo Html_Img::createStatic('/images/missing-image.jpg')
        ->setSize(60, 90)
        ->setTitle('No image for this article')
        ->setClass('article-thumbnail');
4 голосов
/ 20 марта 2011

Добавление к Ответ Джона : чтобы разрешить аргументы конструктора, используйте следующее:

function create($type) {
    $args = func_get_args();
    $reflect = new ReflectionClass(array_shift($args));
    return $reflect->newInstanceArgs($args);
}
create('Foo', 'some', 'args')->bar();

Документация: ReflectionClass->newInstanceArgs

1 голос
/ 23 ноября 2011

Propel использует статический метод «создать».Я бы пошел с этим.Этот метод облегчает тестирование кода, а не просто использование статических методов для выполнения бизнес-логики.

<?php 
class MyClass
{
  public static function create()
  {
    return new MyClass();
  }
  public function myMethod()
  {
  }
}

Кроме того, вы также можете передавать параметры в конструктор.Например:

<?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()
  {
  }
}

В любом случае вы сможете вызывать myMethod сразу после создания метода

<?php
MyClass::create()->myMethod();
// or
MyClass::create($param1, $param2)->myMethod();
0 голосов
/ 24 сентября 2015

Немного опоздал на вечеринку, но я думаю, что это может помочь.

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
       }
    }
}

это может быть использовано как

$result = MyClass::myMethod();

необязательные параметры могут быть переданы через __constructor или myMethod.
Это мой первый пост, и я надеюсь, что уловил правильно

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...