为什么Traits不能直接实例化?

问题描述 投票:0回答:4

在 PHP 中测试特征时,我有点困惑为什么要引入特征。我做了一些小实验。首先我直接在类中调用特征方法

<?php

trait HelloWorld {
    public function sayHello() {
        echo 'Hello World!';
    }
}

class TheWorldIsNotEnough {
    use HelloWorld;
    public function sayHellos() {
        $o = new HelloWorld();
        $o->sayHello();
    }
}

$o = new TheWorldIsNotEnough();
$o->sayHellos();

?>

我收到一个错误

Fatal error: Cannot instantiate trait HelloWorld in C:\xampp\htdocs\test.php on line 35

但是当我这样做的时候

<?php

trait HelloWorld {
    public function sayHello() {
        echo 'Hello World!';
    }
}
class MyHelloWorld {
    use HelloWorld;
}
class TheWorldIsNotEnough {
    use HelloWorld;
    public function sayHellos() {
        $o = new MyHelloWorld();
        $o->sayHello();
    }
}

$o = new TheWorldIsNotEnough();
$o->sayHellos();

?>

我能够调用特征方法,结果显示“Hello World!”。 那么使用 Traits 的优点是什么?它与抽象类有何不同?请帮助我理解用法。谢谢。

php class abstract traits php-5.4
4个回答
11
投票

Traits
不应被实例化。它们只是代码部分,您可以通过
use
在类中重用它们。您可以想象,
trait
代码会扩展并成为您的类的一部分。甚至令人悲伤的是:

特征本质上是语言辅助的复制和粘贴。

所以你的例子应该像这样工作:

<?php

trait HelloWorld {
    public function sayHello() {
        echo 'Hello World!';
    }
}

class TheWorldIsNotEnough {
    use HelloWorld;

    public function sayHellos() {
        // your trait defines this method, so now you can    
        // think that this method is defined in your class directly
        $this->sayHello(); 
    }
}

$o = new TheWorldIsNotEnough();
$o->sayHellos();

//or simply
$o->sayHello();
?>

3
投票

感谢所有发布答案的人,但经过大量研究后得到了我真正寻找的答案。我的问题是是什么使 Traits 与抽象类、继承等现有方法不同。在类内部调用时实例化它是可以的,但最大的区别是我们可以通过这种方式在类中包含多个特征

use class1, class2; 

如果两个类中存在相同的方法并且我们想使用 class2 中的方法时发生冲突,我们会这样做

use class1, class2 {
  class2::method1 insteadof class1;
}

甚至特征也可以有多个这样定义的特征:

trait Class1 {
    use trait1, trait2;
}

与继承不同;如果特征具有静态属性,则使用该特征的每个类都具有这些属性的独立实例。 检查此链接http://php.net/manual/en/language.oop5.traits.php#107965

特征与继承的另一个区别是特征中定义的方法可以访问它们所使用的类的方法和属性,包括私有的。 http://php.net/manual/en/language.oop5.traits.php#109508.

与接口实现不同的是,所有特征方法都可以访问而无需再次定义。


1
投票

好吧,这可能不是正确的方法,但我想出了一种如何使用 Traits 的方法,以及为什么它在某些情况下对我的项目更好。它们是类的一种扩展。如果您熟悉 CakePHP,这些 Traits 让我想起模型的行为或控制器的组件。只要查一下就可以了:-)

抽象类略有不同,因为您可以像这样使用它进行继承:

abstract class HelloWorld {
    public function sayHello() {
        echo "Hello World!";
    }

    abstract public function doFunnyStuff();
    abstract public function doMoreFunnyStuff();
}

class ConcreteHelloWorld extends HelloWorld {
    public function doFunnyStuff() {
        echo "Funny Hello!";
    }

    public function doMoreFunnyStuff() {
        echo "More Funny Hello!";
    }
}

$o = new ConcreteHelloWorld();
$o->sayHello(); // common property
$o->doFunnyStuff(); // specialy implemented property
$o->doMoreFunnyStuff(); // specialy impelemented property

特质更像是一个类的扩展。我正在 MVC 框架中使用 Traits 以这种方式扩展带有日志记录的类:

trait Logger
{
    public function saveLog($kindOf, $messasge, $serverity)
    {
        some_connect_to_DB_pseudo_code();
        $sqlQuery = "INSERT INTO log (kindof, message, serverity)
                        VALUES (".$kindof.", ".$message.", ".$serverity.")";
        mysql_query($sqlQuery); // deprecated :-)
    }
}

class Controller extends AppController
{
    use Logger;

    public function someAction($params)
    {
        $this->saveLog("CALL", __METHOD__." - started some Action with params: ".$params, 0);

        ...
        ...
    }
}

它非常方便,因为我在每个类中都使用它,而且我不必在必须连接到数据库并生成 SQL 查询的地方再次编写所有这些行。由于我在整个 MVC 框架中拥有大量继承,因此我不必将 Logger 作为某些父类包含在内。只需将其与“use”关键字一起放入任何应该能够将日志信息发送到数据库的类中即可。

同样的事情对我来说也适用于调试消息,我只需写这样的东西:

$this->debug("WARNING", $message);

我的调试特征正在制作一条格式良好的警告消息:-)希望它有助于理解。


0
投票

Abstract
Trait
类之间唯一的共同点是无法单独实例化
a Trait/an Abstract

但他们的目的不同。

Trait
仅旨在以细粒度且一致的方式对功能进行分组。它是通过使开发人员能够在生活在不同类层次结构中的多个独立类中减少单继承的一些限制,其中类只是提供一种继承模板并强制继承类实现抽象方法。
    

© www.soinside.com 2019 - 2024. All rights reserved.