我想创建一个类的实例,并在该实例上用一行代码调用一个方法。
PHP不允许对常规构造函数调用方法:
new Foo()->set_sth(); // Outputs an error.
因此,如果我可以称之为静态构造函数:
Foo::construct()->set_sth();
我的问题是:
使用这样的静态构造函数是一个好的实践吗?如果是,您将如何建议为这些静态构造函数命名方法?
我一直在犹豫以下选择:
Foo::construct();
Foo::create();
Foo::factory()
Foo::Foo();
constructor::Foo();
但是,如果不存储实例,那么创建实例的意义是什么呢?你可以直接调用一个静态函数,对吗?否则,也许你会对单子模式感兴趣?
我不是说我没有存储实例。
如果理论上您想执行new Foo()->bar();,则不会存储实例。所以我才这么想。不过,我认为单件模式就在你的胡同里。
这是一个真实的代码行:echo ORM::factory('article')->find(1)->title;——它不存储实例,它不是单例的,而且它仍然有意义。你不同意吗?
任何方法的命名都应具有显示名称的意图。我不知道"工厂"是干什么的。尝试建立更高级别的语言:
User::with100StartingPoints();
这将与以下内容相同:
$user = new User();
$user->setPointsTo(100);
您还可以很容易地测试user::with100startingpoints()是否等于这个值。
正如@koen所说,静态构造器(或"命名构造器")只对证明意图有益。
不过,从5.4开始,出现了一个名为"取消引用"的方法,它允许您直接用方法调用内联类实例化。
(new MyClass($arg1))->doSomething(); // works with newer versions of 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/
型绝对是对原始问题最有信息性和相关性的回答。提供了何时/为什么使用静态构造函数的解释,以及在不必静态(尽管是5.4+)的情况下对一行程序的方法。
如果您不需要参考新构建的Foo,为什么不简单地将set_sth设置为static函数(如果需要,让它在内部创建一个新的Foo)?
如果你真的需要得到参考资料,你会怎么做?return $this在set_sth中?但这样一来,set_sth就可以变成工厂功能了。
我能想到的唯一情况是,如果您想在一个新构建的实例上调用一个表达式中所有的可链接方法(比如在一个流畅的接口中)。这就是你想做的吗?
无论如何,您可以对所有类型的对象使用通用的工厂函数,例如
function create_new($type) {
return new $type;
}
create_new('Foo')->set_sth();
型这是一个有趣的方法。但对于接受参数的构造函数来说,它不起作用。
型另一个小缺点是,NetBeans无法为新创建的实例提供代码提示。
型@伊曼纽尔:看看我的答案。
型@NIKIC:+1,有用的添加。
型@伊曼纽尔:我想如果你把返回类型提示添加到set_sth中,它会在那之后工作。
这可能不是一个很好的实践,但是您可以使用这样一个事实:函数和类有两个不同的名称空间:您可以有一个与类同名的函数。
这样就可以编写这种代码,例如:
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
型很优雅。你知道有没有使用这种风格的开源项目?我很想看到它在一个真实的项目中实现。
型不,事实上,我从来没有在我从事过的任何项目中大规模使用过它:我只是知道这是可能的,可能使用过一次或两次,但不经常使用。
型使用这种风格有什么好处?我能看到的唯一好处是-不需要键入"new"关键字
型不知道,我会投赞成票还是反对票。我肯定会说,这是一个糟糕的实践,因为它使代码不干净,但另一方面,这是一个非常方便的事情,我不能拒绝尝试使用它至少一次。我可以想象,这可能对查询生成器有用。
这些被称为创建方法,我通常将它们命名为createXXX(),例如createById()或createEmptyCatalog()。它们不仅提供了一种很好的方法来揭示对象构造函数的不同意图,而且还支持在一个流畅的接口中立即链接方法。
echo Html_Img::createStatic('/images/missing-image.jpg')
->setSize(60, 90)
->setTitle('No image for this article')
->setClass('article-thumbnail');
除了jon的答案:要允许构造函数参数,请使用以下内容:
function create($type) {
$args = func_get_args();
$reflect = new ReflectionClass(array_shift($args));
return $reflect->newInstanceArgs($args);
}
create('Foo', 'some', 'args')->bar();
文件:ReflectionClass->newInstanceArgs。
型被否决了,因为IDE无法提供任何有意义的代码完成。
推进使用静态方法"创建"。我会同意的。这种方法使代码更容易测试,而不仅仅是使用静态方法来执行业务逻辑。
class MyClass
{
public static function create()
{
return new MyClass();
}
public function myMethod()
{
}
}
此外,还可以将参数传递给构造函数。例如:
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()
{
}
}
无论哪种情况,您都可以在create方法之后立即调用mymethod
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();
可选参数可以通过uu构造函数或mymethod传递。这是我的第一个帖子,我希望我的花招是对的。
型这样,您就需要向每个静态方法添加粘合代码。