【问题标题】:Why Traits cannot be instantiated directly?为什么 Traits 不能直接实例化?
【发布时间】:2016-07-07 05:16:30
【问题描述】:

在 PHP 中测试特征时,我有点困惑为什么要引入特征。我做了一些小实验。首先,我直接在一个类中调用了 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 有什么优势,它与抽象类有什么不同呢?请帮助我了解用法。谢谢。

【问题讨论】:

  • 你为什么不直接做$this-&gt;sayHello();
  • @tkausl 这意味着当我在类中使用特征时,它会自动实例化。
  • - what is advantage of using Traits? + 调用它的方法而不实例化。
  • @revo 所以基本上我们只保存一行代码。
  • 在设计方面,还有很多。

标签: php class abstract traits php-5.4


【解决方案1】:

Traits 不应该被实例化。它们只是代码部分,您可以通过useing 在您的类中重用它们。您可以想象,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();
?>

【讨论】:

  • 这个答案帮助我理解了特征是如何工作的。它补充了 PHP 文档相关文章 :)
【解决方案2】:

好的,所以这可能不是这样做的方法,但我想出了一种方法来了解如何使用 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”关键字一起放入任何应该能够将 loginfo 发送到数据库的类中。

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

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

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

【讨论】:

  • 非常感谢。这非常接近我的需要。
  • 我认为您没有测试就匆忙给出了答案。正在调用您的类 ConcreteHelloWorld 中的受保护函数,这会引发错误。它应该是公开的。类名也不匹配。
  • 你是对的。我在我的代码中将它们用作公共方法。 :-) 如果您需要更多帮助和具体建议,请联系我。
  • 你说我们不能在特征中使用抽象方法,但我用抽象方法进行了测试,它可以工作。 &lt;?php trait HelloWorld { abstract public function sayHello(); } class TheWorldIsNotEnough { use HelloWorld; public function sayHello() { echo "something"; } } $o = new TheWorldIsNotEnough(); $o-&gt;sayHello(); ?&gt;
  • 有趣。这对我不起作用,因此我需要抽象类。无论如何,我不会那样使用它,即使它似乎是可能的。我将不得不检查为什么它在我的代码上不起作用:-(。如果您想使用抽象方法来生成模板,请不要使用 Traits。使用它们只是为了扩展功能。这将是我的建议。
【解决方案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

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

与接口实现不同的是,所有的traits方法都可以在不重新定义的情况下访问。

【讨论】:

  • 这是一个很好的比较!而且我不知道特征中的静态变量,这很好。
【解决方案4】:

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

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

【讨论】:

  • 在抽象类中,方法不必是抽象的。因此,取决于抽象类中方法的实现,是必须在继承类中实现还是直接使用抽象类中的方法。此外,一个类只能从一个抽象类继承,这在过去导致了复杂的类层次结构(PHP 4 - 5)。另一方面,特征可以在一个类中使用多个。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-10
  • 1970-01-01
  • 2021-07-20
  • 1970-01-01
相关资源
最近更新 更多