白马非马 【Traits】

那不是歌 那是孤单的歌 , 这白马非马的逻辑鲜有附和。《白马非马》By Vae

从PHP的5.4.0版本开始,PHP提供了一种全新的代码复用的概念,那就是Trait。Trait其字面意思是"特性"、"特点",可以理解为为类添加的特性

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

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

引用segmentfault网友的一句话:

trait = abstract class - interface
//trait提供了另一个维度的灵活性

1.简单的Traits例子

<?php 
trait Hello{
    public function sayHello(){
        echo 'Hello';
    }
}

trait World{
    public function sayWorld(){
        echo 'World';
    }
}

class HelloWorld{
    use Hello,World;
    public function sayMark(){
        echo '!';
    }
}

$a = new HelloWorld();
$a->sayHello();
$a->sayWorld();
$a->sayMark();
输出:Hello World!

我们使用trait关键字来定义类,在类里添加一个方法。
然后在主类里面用use关键字来使用trait,这样子主类对象就拥有访问trait类里的方法的能力

这样大概了解了他的使用方法,那么再看一个例子来理解一下他的使用。

<?php 
trait fly{
   public function fly(){
       echo "我可以飞翔\n";
   }
}

class Trans{
    public function __construct(){
       echo "父类交通工具\n";
    }
    public function move(){
        echo "所有的交通工具都可以动\n";
    }
}

class Plane extends Trans{
     use fly;
     public function __construct(){
         parent::__construct();
        echo "我是飞机\n";
     }
}
class Car extends Trans{
    public function __construct(){
        parent::__construct();
        echo "我是汽车\n";
    }
}

$plane = new Plane();
$plane->move();
$plane->fly();

$car = new Car();
$car->move();
//$car->fly();

可以看到我这里写了一个不太准确的生活中的类,一个飞机,一个汽车,两者都可以move,但是飞机还可以fly,那么我们使用trait给飞机增加一个飞的属性。当然你可以说飞行这个属性直接写在飞机类里不就好了,但是如果我说此时又多了一个火箭的类呢,火箭也会飞,那么就不能在火箭里写fly了,那么就有重复代码了。
那么这时候你又会想到多继承或者接口,但这无疑会增加代码的重叠性,很可能最终得到一个复杂的架构,同时我们也可以看到traits比接口多的内容就有有方法定义,继承自接口的类必须全部实现其中方法,而继承自trait的类,可以自由选择方法,而不必生成无用代码。

那么这里得到的结果就是这样的

深度截图20171022120907.png

那么不知道通过这个生活中的例子你有没有发现trait的优势性。

2.升华一下

我们这样来举例子。

<?php
trait Fashi{
    public function Fashi(){
        echo "法师通用属性";
    }
}
trait Fuzhu{
    public function Fuzhu(){
        echo "辅助通用属性";
    }
}

class Guiguzi{
   use Fashi,Fuzhu;
   public function hero(){
       echo "我的定位是法师和辅助";
       echo "\n我有";
       $this->Fashi();
       $this->Fuzhu();
   }
}

$guiguzi = new Guiguzi();
$guiguzi->hero();

拿我最喜欢的王者荣耀英雄鬼谷子来举例子,当然什么法师辅助这种单词就不要太在意了。

比如说鬼谷子是法师和辅助,那么他将拥有法师和辅助的一些本质属性,有了trait我们可以很轻易的加入这两种属性,但是用多继承或者接口的写法都是很不方便的,或者拿抽象类来说吧,假如我这个英雄是法师和辅助,那么我需要实现一个法师方法一个辅助方法,假如今天我修改了英雄定位,那么我又需要改动他的抽象类,这一改动不要紧,我就需要改动其他用了该类的英雄,很不方便,假如说我用继承来抽取公用方法,那可能需要写很多代码,但是使用trait就不一样了,假设王者荣耀有5大职业,那么我只需要定义五大职业的trait,对应每个英雄就可以use相应的职业属性了。

那么到这里我们可以总结一下trait的特点,他不是简单的抽取重复代码实现复用,他更多的是强调对于类的特性,以及即插即用的快捷,耦合度低可读性高是他主要的特点。

3.冲突

使用trait当然也会遇到冲突的问题,比如下面的这个例子

<?php
trait First{
    public function first(){
        echo "输出first";
    }
}

trait Second{
    public function first(){
        echo "输出Second";
    }
}

class Test{
    use First,Second;
    public function Test(){
        echo "输出内容:\n";
    }
}

$test = new Test();
$test->Test();
$test->first();

两个trait都有first方法,那么必定会导致系统无法判断你调用哪个方法,那样子就乱了。
所以运行结果如下:

PHP Fatal error:  Trait method First has not been applied, because there are collisions with other trait methods on Testin /home/surine/Php/Trait_first.php on line 14

那么官方给我们的解决方法是insteadof和as操作符
insteadof的功能是指出当发生冲突时,系统会选择哪一个冲突方法来调用。
as是可以给冲突的方法加以别名,然后通过别名来访问,同时as还可以修改权限(别名修改,不是修改源)

我们修改一下class Test

class Test{
    use First,Second{
        First::first insteadOf Second;
    }
    public function Test(){
        echo "输出内容:\n";
    }
}

使用insteadOf优先调用First里的方法。

那么as的作用是当我们使用insteadof覆盖其中一个方法后,我们应该使用as来给它别名用于访问。

class Test{
    use First,Second{
        First::first insteadOf Second;
        Second::first as second;
     }
}

这样我们通过$test->second();就可以访问了。

同时我们可以修改他的权限

    Second::first as private second_new;

4.组合使用和优先级

一个类可以调用多个trait,trait也可以调用trait。

<?php
trait Hello{
    public function sayHello(){
       echo "Hello";
    }
}
trait World{
    use Hello;
    public function sayWorld(){
       $this->sayHello();
       echo "World";
    }
}
class Test{
    use World;
    public function Test1(){
        $this->sayWorld();
        echo "!";
    }
}

$a = new Test();
$a->Test1();

输出HelloWorld!

当类里和trait含有同名方法,那么类方法被调用。

<?php
  trait Hello{
      public function sayHello(){
          echo "Hello";
      }
}
class Test{
    use Hello;
    public function sayHello(){
        echo "test say Hello";
    }
}
$test = new Test();
$test->sayHello();

输出test say Hello
如果父类也有同名方法呢?

<?php
  trait Hello{
      public function sayHello(){
          echo "Hello";
      }
}
class Base{
    public function sayHello(){
        echo "base say Hello";
    }
}
class Test extends Base{
    use Hello;
}
$test = new Test();
$test->sayHello();

trait优先级要大于父类,输出Hello

那么我们可以总结如果类本身,类use的trait,类的父类都含有同名方法的时候,优先级 类本身>trait>父类

5.其他

  • 同样的属性也可以被放在trait里,但是属性不能覆盖,也就是说当trait里面有一个a属性,在use他的类里不能重新定义a属性,只能使用a属性
  • trait可以使用静态变量和方法,也可以使用抽象方法,他们所达成的效果和类里使用是一样的。

第一次结合书籍和网络资料来理解trait,比较浅显,如有错误请在评论区指出。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,637评论 18 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,846评论 25 707
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,602评论 18 399
  • 最近这段时间文字写得少了一些,有时候更愿意去读一些东西。因为开始意识到自己肚里只有丁点墨水,越来越淡,有愧于自称为...
    愚指导阅读 233评论 6 4
  • 我喜欢吃黄瓜,凉拌、煎炒皆可,尤喜生吃。一条在手,率性咬一口,慢慢咀嚼,脆脆的,有淡甜清香。家里常有苹果、...
    柳林过客阅读 502评论 5 8