面向对象的优势在于类的复用,继承和多态都是对类进行复用,他们一个是类级别的复用,一个是方法级别的复用。其中提到继承必提组合,有什么区别呢。组合与继承都是提高代码可重用性的手段。通过总结,可以得出继承是一种“是、像”的关系;而组合则是一种“需要”的关系,利用这个“定律”可以很好的判断出是继承关系还是组合关系。

继承最大的优点就是扩展简单、代码简洁,但是缺点大于优点。而组合却很灵活,仅通过唯一接口与外界通信,耦合度低于继承,但是却增加了代码量。

如果既想要组合的灵活,又要继承的代码简洁,该怎么办呢?

这是可以做到的,比如使用多重继承就可以实现。多重继承一个类会同时继承多个父类,组合两个父类的功能。但是多重继承过于灵活,可能会带来“菱形问题”,使模型变的复杂起来,因次大多数语言,都放弃了多重继承这一模型。

PHP5.4.0引入了新的语法结构Traits,实现了代码复用的方法。Traits 是一种为类似 PHP 的单继承语言而准备的代码复用机制,为了减少单继承语言的限制,使开发人员能够自由地在不同层次结构内独立的类中复用方法集,是出extends、implements外的另外一种扩展对象的方式。

下面总结下Traits的用法,先看下实例代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<?php 
trait traitName {
    function func_one () {/*code*/}
    function func_two () {/*code*/}
}
Class class_one {
    use traitName;
    /*code*/
}
Class class_two {
    use traitName;
    /*code*/
}
?> 

优先级

优先顺序是来自当前类的成员覆盖了trait的方法,而trait则覆盖了被继承的方法。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<?php
 class  Base  {
public function  sayHello () {
    echo  'Hello ' ;
}
}
trait  SayWorld  {
public function  sayHello () {
    parent :: sayHello ();
    echo  'World!' ;
}
}
class  MyHelloWorld  extends  Base  {
    use  SayWorld ;
}
 $o  = new  MyHelloWorld ();
 $o -> sayHello ();
 ?>  

输出:Hello World!

多个trait

通过逗号分隔,在 use 声明列出多个 trait,可以都插入到一个类中。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
<?php 
trait Hello {
    Public function sayHello () {
        echo 'Hello';
    }
}
trait World {
    Public function sayWorld () {
        echo 'World';
    }
}
Class Say {
    use Hello, World;

}
$a = new Say();
$a->sayHello();
$a->sayWorld();
?>

输出:HelloWorld

冲突的解决

如果两个 trait 都插入了一个同名的方法,如果没有明确解决冲突将会产生一个致命错误。为了解决多个 trait 在同一个类中的命名冲突,需要使用 insteadof(而不是的意思,可以这么理解) 操作符来明确指定使用冲突方法中的哪一个。以上方式仅允许排除掉其它方法,as 操作符可以将其中一个冲突的方法以另一个名称来引入。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?php
 trait  A  {
    public function  smallTalk () {
        echo  'a' ;
    }
    public function  bigTalk () {
        echo  'A' ;
    }
}

trait  B  {
    public function  smallTalk () {
        echo  'b' ;
    }
    public function  bigTalk () {
        echo  'B' ;
    }
}

class  Talker  {
    use  A ,  B  {
         B :: smallTalk  insteadof  A ;
         A :: bigTalk  insteadof  B ;
    }
}

class  Aliased_Talker  {
    use  A ,  B  {
          B :: smallTalk  insteadof  A ;
          A :: bigTalk  insteadof  B ;
         B :: bigTalk  as  talk ;
    }
}
 ?> 

修改方法的访问控制

使用 as 语法还可以用来调整方法的访问控制。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
<?php 
 trait  HelloWorld  {
    public function  sayHello () {
        echo  'Hello World!' ;
    }
}
 // 修改 sayHello 的访问控制
 class  MyClass1  {
    use  HelloWorld  {  sayHello  as protected; }
}
 // 给方法一个改变了访问控制的别名
// 原版 sayHello 的访问控制则没有发生变化
 class  MyClass2  {
    use  HelloWorld  {  sayHello  as private  myPrivateHello ; }
}
$a = new MyClass1();
$a->sayHello();        //输出错误
$b = new MyClass2();
$b->sayHello();        //输出Hello World!
$b->myPrivateHello();        //输出出错
?>

从 trait 来组成 trait

正如类能够使用 trait 一样,其它 trait 也能够使用 trait。在 trait 定义时通过使用一个或多个 trait,它能够组合其它 trait 中的部分或全部成员。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
<?php 
trait Hello {
    function sayHello () {echo 'Hello';}
}
trait World {
    function sayWorld () {echo 'World';}
}
trait HelloWorld {
    use Hello, World;
}

Class Say {
    use HelloWorld;
}

$a = new Say();
$a->sayHello();
$a->sayWorld();
?>

Trait 的抽象成员

为了对使用的类施加强制要求,trait 支持抽象方法的使用。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<?php 
trait HelloWorld {
    function hello () {echo 'Hello';}
    abstract function world ();
}
Class Say {
    use HelloWorld;
    function world () {        //实现抽象方法
        echo 'World!';
    }
}
?>

Trait 的静态成员

Trait可以被静态成员静态方法定义。

1
2
3
4
5
6
7
8
9
<?php 
trait HelloWorld {
    Static public function say () {echo 'Hello,World';}
}
Class Say {
    use HelloWorld;
}
Say::say();
?>

属性

Trait 同样可以定义属性,如果 trait 定义了一个属性,那类将不能定义同样名称的属性,否则会产生一个错误。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<?php 
trait A {
    Public $a = 'Hello World';
}
Class B {
    use A;
}
$b = new B();
echo $b->a;
?>

总结:

在笔者看来,Traits的用法在某些方面是跟类的继承很相似的,学习的时候可以对比着来使用,只是Traits使用起来更加的灵活方便,减少了语言单继承的限制,使得应用类的成员不需要继承。:)