转 PHP Trait 使用指南
通过更好地组织代码和代码复用来最大程度地减少代码重复是面向对象编程的重要目标。但是在 PHP 中,由于使用单一继承模型的局限性,有些时候要做到这些可能会比较困难。您可能有一些要在多个类中使用的方法,但它们可能不太适合继承层次结构。
诸如 C ++ 和 Python 之类的语言允许我们从多个类继承,这在某种程度上解决了这一问题,而 Ruby 中的 mixin 则允许我们混合一个或多个类的功能而无需使用继承。但是多重继承存在诸如钻石问题之类的问题,而 mixin 则能是一个复杂的工作机制。
在本文中我打算探讨一下 Trait,这是一个出现在 PHP 5.4 版本中的新特性,可以用来解决此类问题。Trait 的概念本身对编程而言并非什么新鲜事物,而且在其他语言(例如 Scala 和 Perl)中也已经被使用。它允许我们在不同类层次结构中的各个独立类之间水平地重用代码。
Trait 长什么样
Trait 与抽象类类似,自身无法实例化。PHP 文档对 Trait 的描述如下:
Trait 是为类似 PHP 的单继承语言而准备的一种代码复用机制。Trait 为了减少单继承语言的限制,使开发人员能够自由地在不同层次结构内独立的类中复用 method。
考虑以下的例子:
<?phpclass DbReader extends Mysqli
{
}
class FileReader extends SplFileObject
{
}
当不同类都用到一些相同功能时就会出问题。例如,几个类都需要生成单例,由于 PHP 不支持多继承,那么只能在每个类重复编写代码,或者说将单例功能写在父类中并让这几个类都继承这个父类,这种继承是无意义的。Trait 提供了这类问题的解决方案。
<?php trait Singleton { private static $instance; public static function getInstance() { if (!(self::$instance instanceof self)) { self::$instance = new self; } return self::$instance; } } class DbReader extends ArrayObject { use Singleton; } class FileReader { use Singleton; }
SingletonTrait 直接实现了单例模式。该 Trait 通过getInstance()静态方法创建并返回使用该 Trait 的类的实例(如果实例还没有创建)。
让我们试着用getInstance()方法创建这些类的对象:
<?php $a = DbReader::getInstance(); $b = FileReader::getInstance(); var_dump($a); //object(DbReader) var_dump($b); //object(FileReader)
我们可以看到$a是DbReader的实例,$b是FileReader的实例,但是它们现在都有了实现单例的能力。 通过Singleton这个 Trait,它里边的方法已经被水平注入类中。
Trait 不会对类施加任何额外的影响。在某种程度上,你可以将其看作是编译器辅助的复制和粘贴机制,trait 中的方法会被复制到组成类中。
如果我们只是从具有私有属性$instance的父类中将类DbReader子类化, 属性不会被显示在通过ReflectionClass::export()导出的备份中。有了 trait,你就能实现之!
Class [ class FileReader ] { @@ /home/shameer/workplace/php54/index.php 19-22 - Constants [0] { } - Static properties [1] { Property [ private static $_instance ] } - Static methods [1] { Method [ static public method instance ] { @@ /home/shameer/workplace/php54/index.php 6 - 11 } } - Properties [0] { } - Methods [0] { } }
多个 Trait
到目前为止我们在在一个类中只用了一个 trait,但在某些情况下,我们可能需要合并多个 trait 的功能。
<?php trait Hello { function sayHello() { echo "Hello"; } } trait World { function sayWorld() { echo "World"; } } class MyWorld { use Hello, World; } $world = new MyWorld(); echo $world->sayHello() . " " . $world->sayWorld(); //Hello World
在上边的例子中,我们有两个 trait:Hello和World。 TraitHello输出 “Hello” ,而 traitWorld输出 “World”。在类MyWorld中我们引入Hello和World两个 trait ,以便MyWorld类的对象拥有前边两个 trait 的方法,输出 “Hello World”。
Traits 构成的 Traits
随着应用的增长,我们很可能会有一套可用于不同类的 traits 。PHP 5.4 允许我们拥有由其他 traits 构成的 traits 因此我们可以在所有这些类中只包含一个 traits 而不是多个 traits。 这使我们可以重写前一个例子,如下:
<?php trait HelloWorld { use Hello, World; } class MyWorld { use HelloWorld; } $world = new MyWorld(); echo $world->sayHello() . " " . $world->sayWorld(); //Hello World
在此我们创建了一个 traitHelloWorld,使用了 traits Hello 和 World 然后包含在MyWorld类里。 由于HelloWorldtrait 具有其他两个 traits 的方法,就如同我们自己在类里包含了两个 traits 。...