PHP实现代码复用神器:Traits

声明:这是自PHP 5.4.0起,PHP实现的代码复用的新特性,称为traits,如果你现在用的版本比5.4.0低的话,请升级!

简单介绍:

Traits 是一种为类似 PHP 的单继承语言而准备的代码复用机制。Trait 为了减少单继承语言的限制,使开发人员能够自由地在不同层次结构内独立的类中复用方法集。Traits 和类组合的语义是定义了一种方式来减少复杂性,避免传统多继承和混入类(Mixin)相关的典型问题。

Trait 和一个类相似,但仅仅旨在用细粒度和一致的方式来组合功能。Trait 不能通过它自身来实例化。它为传统继承增加了水平特性的组合;也就是说,应用类的成员不需要继承。

举个栗子:
假设现在我们在开发一个站点,目前有很多不同的类:用户(User)、页面(Page)、联系表单(ContactForm)等。那么假如我们在开发该站点的时候,如果能够不去关心对象的类型,而可以使用一个调试工具函数来打印出一个给定的对象的信息,那将会十分有用:

function dumpObject(){
    //打印出对象的相关信息
}

有了这个需求,我们可能需要在每个类中添加这个方法的定义,巧妙的利用继承的话,我们可能仅仅需要在相对应的父类中定义该方法即可,但是这样的话还是会造成不必要的代码冗余(并且一旦对该方法的定义有所修改,就需要修改一大堆东西)。

通常情况下,当我们需要在多个不同的类中使用同一个方法的时候,继承就是一个不错的解决方案。然而在PHP中,每个类只能从单一的一个父类中继承,这样的话就不能为多个类指定同一个比较通用的父类了。你可能会想到接口,毕竟接口是多继承(实现)的,但是实现的时候还是要在子类中实现,代码冗余时没法避免的!

我们能不能这样想?假如现在有一个方法,当我想用的时候我就去用它,而不用管该对象是 User 还是 Page , 亦即不用管该对象是什么类型的。这时候 traits 就登场了。traits允许我们在不使用继承的情况下为一个类增加功能。

使用介绍:

1、要创建一个 trait ,需要使用 trait 关键字,后面跟着它的名字和定义:

trait tSomeTrait
{
    //Attributes
    function someFunction()
    {
        //实现代码
    }
}

像一个抽象类或者一个接口一样,traits 是不能够被初始化的(就是说,我们不能从一个 trait 创建一个对象)。然而,我们可以通过使用 use 关键字在一个类的定义中为这个类增加一个 trait :

class someClass{
    use tSomeTrait;
    public function func(){}
}

假如要包含多个 traits 时,就以逗号将其分隔开:

use trait1,trait2;

就像在一个PHP脚本中使用 include 包含一个外部的PHP脚本就能使其马上生效一样,在这里增加一个 use TraitName 语句就能使这个 trait 的代码对当前类生效。

现在,当我们创建一个SomeClass 类型的对象的时候,这个对象就有了 someFunction() 方法:

$obj = new someClass();
$obj->someFunction();

PS:接口与traits

traits 看上去和接口有很多地方十分相似,但是其实二者有着天壤之别。

  1. 一个接口会强制执行更严格的编程规则,以便类被设计成实现特定的方法。相反,一个 trait 使方法对一个类可用,记住是“可用”!即便它没有在类中定义。

  2. 接口中的规定的方法子类中必须要实现,代码冗余少不了么,但是用 trait 的话,代码只用写一遍,然后想怎么用怎么用。

抽象方法:

假如在 traits 中使用抽象方法,那么必须在使用它的类中实现该方法!(这一点跟接口差不多):

trait tSomeTrait{
    abstract function test();
}

class cSomeClass{
    use tSomeTrait;
    function test(){
        echo "test";
    }
}

相互嵌套:

traits 之间可以互相嵌套,类似于以下栗子:

trait trait1{
    function func1(){
        echo "func1";
    }
}
//在trait2中使用trait1
trait trait2{
    use trait1;
    function func2(){
        echo "func2";
    }   
}

//此时的trait2中其实有两个方法:func1(),func2()

class test{
    use trait2;
}

$test = new test();
$test->func1();
$test->func2();

优先级:

如果在一个类中使用的trait中有一个方法和类中的一个方法同名,PHP就需要决定有限使用哪个方法:

  • 如果方法是在一个类中定义的,那么它的优先级会比trait中 的方法的优先级高;
  • 如果方法的定义是在父类中继承过来的,那么trait中方法的优先级高

不同traits冲突:

假如在一个类中使用了 trait1、trait2 ,但是 trait1 和 trait2 中都定义 somgFunc() 方法,那么就会起冲突,如果不人为的解决了这个冲突,会导致错误。

为了解决多个 trait 在同一个类中的命名冲突,需要使用 insteadof 操作符来明确指定使用冲突方法中的哪一个。

以上方式仅允许排除掉其它方法,as 操作符可以将其中一个冲突的方法以另一个名称来引入。

trait first{
    function test(){
        echo "first";
    }
}
trait second{
    function test(){
        echo "second";
    }
}

class someClass{
    use first,second{
        //决定使用哪个 trait 中的方法
        first::test insteadof second;
        //想使用 second 中的test()方法
        second::test as test2;
    }
}

$class = new someClass();
$class->test();
$class->test2();

关于 traits 的内容我就介绍这么多。

相关推荐