静态构造函数的最佳实践

时间:2011-03-20 11:31:10

标签: php coding-style

我想创建一个类的实例,并在一行代码中调用该实例上的方法。

PHP不允许在常规构造函数上调用方法:

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

所以我正在使用,如果我可以称之为静态构造函数:

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

这是我的问题:

使用像这样的静态构造函数是一种很好的做法,如果是的话,你会如何为这些静态构造函数命名方法?

我一直在犹豫以下选项:

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

8 个答案:

答案 0 :(得分:11)

任何方法的命名都应该是意图揭示名称。我无法告诉Foo :: factory'确实。尝试构建更高级别的语言:

User::with100StartingPoints();

这与:

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

您还可以轻松测试User :: with100StartingPoints()是否与此相等。

答案 1 :(得分:9)

静态构造函数(或#34;命名构造函数")仅有助于证明其意图,正如@koen所说。

从5.4开始,有人称之为"解除引用"出现了,它允许您使用方法调用直接内联类实例化。

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

因此,只有在有多种方法实例化对象时,静态构造函数才有用。 如果你只有一个(总是相同类型的参数和args数),则不需要静态构造函数。

但是如果你有多种实例化方法,那么静态构造函数非常有用,因为它可以避免使用无用的参数检查来污染你的主构造函数,从而削弱语言约束。

示例:

<?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/

答案 2 :(得分:8)

如果您不需要对新构建的Foo的引用,为什么不简单地使set_sth成为static函数(并让它创建一个新的Foo内部如果需要)?

如果你需要掌握参考资料,你会怎么做? return $this中的set_sth?但是,无论如何set_sth都可以成为工厂职能。

我能想到的唯一情况是,如果你想在一个表达式中的新构造的实例上调用可链接的方法(比如在流畅的界面中)。那是你想要做的吗?

无论如何,您可以为所有类型的对象使用通用工厂函数,例如

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

create_new('Foo')->set_sth();

答案 3 :(得分:5)

它可能不是一个最佳实践,但您可以使用函数和类具有两个不同命名空间的事实:您可以拥有一个与类同名的函数。

这允许人们编写这种代码,例如:

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 :(得分:4)

添加到Jon's answer:要允许构造函数参数,请使用以下命令:

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

文档:ReflectionClass -> newInstanceArgs

答案 5 :(得分:4)

这些名为creation methods,我通常将其命名为createXXX(),例如createById()createEmptyCatalog()。它们不仅提供了一种很好的方式来揭示对象构造函数的不同意图,而且它们可以在流畅的界面中实现即时方法链接。

echo Html_Img::createStatic('/images/missing-image.jpg')
        ->setSize(60, 90)
        ->setTitle('No image for this article')
        ->setClass('article-thumbnail');

答案 6 :(得分:1)

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();

答案 7 :(得分:-2)

派对有点晚,但我认为这可能会有所帮助。

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传递 这是我的第一篇文章,我希望我能得到正确的噱头