Я хочу создать экземпляр класса и вызвать метод в этом экземпляре в одной строке кода.
PHP не позволит вызывать метод на регулярном конструкторе:
new Foo()->set_sth(); // Outputs an error.
Поэтому я использую, если можно так выразиться, статический конструктор:
Foo::construct()->set_sth();
Вот мой вопрос:
Является ли использование статических конструкторов такими, которые считаются хорошей практикой, и если да, как бы вы рекомендовали называть методы для этих статических конструкторов?
Я колебался в следующих вариантах:
Foo::construct(); Foo::create(); Foo::factory() Foo::Foo(); constructor::Foo();
Именование любого метода должно быть с намерением раскрывать имена . Я не могу сказать, что делает Foo :: factory. Попытайтесь создать язык более высокого уровня:
User::with100StartingPoints();
Это будет то же самое, что:
$user = new User(); $user->setPointsTo(100);
Вы также можете легко проверить, равен ли пользователь :: with100StartingPoints ().
Если вам не нужна ссылка на недавно созданный Foo
, почему бы вам просто не сделать set_sth
static
функцией (и если это необходимо, чтобы создать новый Foo
если это необходимо)?
Если вам нужно получить ссылку, как бы вы это сделали? return $this
в set_sth
? Но тогда set_sth
можно превратить в фабричную функцию.
Единственная ситуация, о которой я могу подумать, – это если вы хотите называть цепочки (как в свободном интерфейсе) цельносвязанные методы для вновь созданного экземпляра в одном выражении. Это то, что вы пытаетесь сделать?
Во всяком случае, вы можете использовать универсальную фабричную функцию для всех типов объектов, например
function create_new($type) { return new $type; } create_new('Foo')->set_sth();
Статические конструкторы (или «названные конструкторы») полезны только для доказательства намерения, как говорит @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/
Это, вероятно, не совсем лучшая практика , но вы можете использовать тот факт, что функции и классы имеют два разных пространства имен: у вас может быть функция с тем же именем, что и класс.
Это позволяет написать такой код, например:
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
Добавление к ответу Джона : для разрешения аргументов конструктора используйте следующее:
function create($type) { $args = func_get_args(); $reflect = new ReflectionClass(array_shift($args)); return $reflect->newInstanceArgs($args); } create('Foo', 'some', 'args')->bar();
Документация: ReflectionClass
->
newInstanceArgs
Они называются методами создания , и я обычно createXXX()
их createXXX()
например createById()
или createEmptyCatalog()
. Мало того, что они обеспечивают хороший способ выявления различных намерений конструкторов объекта, но они позволяют немедленную цепочку методов в свободном интерфейсе.
echo Html_Img::createStatic('/images/missing-image.jpg') ->setSize(60, 90) ->setTitle('No image for this article') ->setClass('article-thumbnail');
Propel использует статический метод «create». Я бы пошел с этим. Этот метод упрощает тестирование кода, а не просто использует статические методы для выполнения бизнес-логики.
<?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 сразу после метода create
<?php MyClass::create()->myMethod(); // or MyClass::create($param1, $param2)->myMethod();
Немного поздно, но я думаю, что это может помочь.
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.
Это мой первый пост, и я надеюсь, что у меня есть трюки