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

Why Traits cannot be instantiated directly?

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

中调用了 trait 方法
<?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();

?>

我能够调用 trait 方法并显示结果 "Hello World! "。 那么使用 Traits 的优势是什么?它与抽象 classes 有何不同?请帮助我了解用法。谢谢

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

但他们的目的不同。 Trait 仅旨在以细粒度和一致的方式对功能进行分组。它通过使开发人员能够 reuse sets of methods freely 生活在不同 class 层次结构中的几个独立 class 中来减少单一继承的一些限制,其中 Abstract class 是只是为了提供一种模板来继承并强制继承class实现抽象方法。

Traits 不应该被实例化。它们只是代码部分,您可以通过 useing 在 classes 中重用它们。您可以想象,trait 代码扩展并成为您的 class 的一部分。更让人难过的是:

Traits are essentially language assisted copy and paste.

所以你的例子应该是这样的:

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

好的,这可能不是解决问题的方法,但我想出了一种方法来说明如何使用 Traits 以及为什么它在某些情况下对我的项目更好。它们是 classes 的一种扩展。如果您对 CakePHP 很熟悉,那么这些 Traits 让我想起了模型的行为或控制器的组件。查一下:-)

抽象 class 略有不同,因为您可以像这样将其用于继承:

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

特质更像是class的延伸。我在 MVC 框架中使用 Traits 来扩展 classes 以这种方式进行日志记录:

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

        ...
        ...
    }
}

它非常方便,因为我在每个 class 中都使用它,而且我不必在必须连接到数据库并生成 SQL 查询的地方再次编写所有这些行。而且由于我在整个 MVC 框架中有很多继承,所以我不必将 Logger 作为某些父项包含在内 class。只需将它与 "use"-关键字一起放入任何应该能够将登录信息发送到数据库的 class。

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

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

我的 Debug Trait 正在制作格式良好的警告消息 :-) 希望它有助于理解。

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

use class1, class2; 

如果 classes 中存在相同的方法而我们想使用 class2 中的方法,我们会这样做

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

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

trait Class1 {
    use trait1, trait2;
}

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

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

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