Trait 是从 PHP 5.4 加入的一种细粒度代码复用的语法。以下是官方手册对 Trait 的描述:

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

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

什么是 Trait ?

其实说通俗一点,就是能把重复的方法拆分到一个文件,通过 use 引入以达到代码复用的目的。

那么,我们应该怎么样去拆分我们的代码才是合适的呢?我的看法是这样的:

Trait,译作 “特性”、“特征”、“特点” 。那么问题就来了:什么才是特性?

一个销售公司有很多种产品:电视,电脑与鼠标垫,卡通手办等。其中鼠标垫与卡通手办是非卖品,只用于赠送。

那么这里的 “可卖性” 就是一个特性,非卖品是没有价格的。我们便可以抽象出 “可卖性” 这个 Trait 来:

trait Sellable
{
protected $price = 0; public function getPrice()
{
return $this->price;
} public function setPrice(int $price)
{
$this->price = $price;
}
}

当然我们所有的产品都会有品牌与其它基本属性,所以我们通常会定义一个产品类:

class Pruduct
{
protected $brand;
//... public function __construct($brand)
{
$this->brand = $brand;
} public function getBrand()
{
return $this->brand;
} //...
}

我们的电视与电脑类:

class TV extends Pruduct
{
use Sellable;
//... public function play()
{
echo "一台 {$this->brand} 电视在播放中...";
} //...
} class Computer extends Pruduct
{
use Sellable; protected $cores = 8;
//... public function getNumberOfCores()
{
return $this->cores;
} //...
}

而鼠标垫与手办等礼品是不可卖的:

class Gift extends Pruduct
{
protected $name; function __construct($brand, $name)
{
parent::__construct($brand);
$this->name = $name;
} //...
}

上面的这个例子中,“可卖性” 便是部分商品的一个特性,也可以理解为商品的一个归类。你也许会说,我也可以再添加一个 Goods 类来完成上面的例子啊,Goods 继承 Product,再让所有可卖的商品继承于 Goods 类,把价格属性与方法写到 Goods 里,同样可以代码复用啊。的确,这没啥问题。但是你会发现:你有多个需要区别的特性时,由于 PHP 只有单继承的原因,你不得不组合很多个基类出来,将他们层叠,最终得到的树状结构是很复杂的。这也是 Trait 所带来的优势:随意组合,代码清晰。

其实还有很多例子,比如可飞行的,那么把飞行这个特性所具有的属性(如:高度,距离)与方法(如:起飞,降落)放到一个 trait 就是一个合理的拆分。

Trait 有什么优势 ?

trait 有什么优势?来看一段代码:

class User extends Model
{
use Authenticate, SoftDeletes, Arrayable, Cacheable; ...
}

这个用户模型类,我们引入了四个特性:注册与授权、软删除、数组式操作、可缓存。

我们看到代码的时候一眼便知道当前支持了哪些个特性。再看下面另外一种写法:

abstract AdvansedUser {
// ... 实现了 Authenticate, SoftDeletes, Arrayable, Cacheable 的所有方法
}
class User extends AdvansedUser
{
...
}

你不得不再去阅读 AdvansedUser 的代码才能理解。你想说没有可读性是因为我基类的名称没起好?可是,这种各种特性组合的一个基类是根本无法起一个见名知义的名称的,不信你可以试一下。

就算你真的起了一个见名知义的名称:AuthenticateCacheableAndArrayableSoftDeletesUser,可是当需求变更,要求在 FooUser(同样继承了这个基类) 中去除缓存特性,而 User 类保留这个特性,怎么办?再创建一个基类么?

这就是我理解的 Trait:

它不仅仅是可复用代码段的集合,它应该是一组描述了某个特性的的属性与方法的集合。它的优点在于随意组合,耦合性低,可读性高。

平常写代码的时候也许怎么拆分才是大家的痛点,分享以下几个技巧:

  • 从需求或功能描述拆分,而不是写了两段代码发现代码一样就提到一起;
  • 拆分时某些属性也一起带走,比如上面第一个例子里的价格,它是“可卖性”必备的属性;
  • 拆分时如果给 Trait 起名困难时,请认真思考你是否真的拆分对了,因为正确的拆分是很容易描述 “它是一个具有什么功能的特性” 的;

总之一定要记住:不要为了让两段相同的代码提到一起这样简单粗暴的方式来拆分。

以上是个人见解,欢迎各位讨论。​

from:http://overtrue.me/articles/2016/04/about-php-trait.html

什么是 Trait的更多相关文章

  1. php中trait(性状)与generator(生成器)

    PHP中trait(性状)与generator(生成器) 一.trait (性状) 最近在看Josh Lockhat的<Modern PHP>,这本书很薄.但是其中给出了一个很重要的学习方 ...

  2. 【转】PHP的Trait 特性

    Trait是在PHP5.4中加入的,它既不是接口也不是类.主要是为了解决单继承语言的限制.是PHP多重继承的一种解决方案.例如,需要同时继承两个 Abstract Class, 这将会是件很麻烦的事情 ...

  3. Beginning Scala study note(7) Trait

    A trait provides code reusability in Scala by encapsulating method and state and then offing possibi ...

  4. 速战速决 (4) - PHP: 类基础, 抽象类, 接口, trait

    [源码下载] 速战速决 (4) - PHP: 类基础, 抽象类, 接口, trait 作者:webabcd 介绍速战速决 之 PHP 类基础 抽象类 接口 trait 示例1.类的相关知识点 1(基础 ...

  5. Scala的trait

    一:说明 1.介绍 2.功能 二:具体解释功能 1.定义接口 2.定义方法 3.定义字段 4.定义抽象字段 5.混合trait

  6. 利用GCTA工具计算复杂性状/特征(Complex Trait)的遗传相关性(genetic correlation)

    如文章"Genome-wide Complex Trait Analysis(GCTA)-全基因组复杂性状分析"中介绍的GCTA,是一款基于全基因组关联分析发展的分析工具,除了计算 ...

  7. PHP中Trait特性

    Trait是自 PHP 5.4.0 起添加的一个新特性,是 PHP 多重继承的一种解决方案.例如,需要同时继承两个 Abstract Class, 这将会是件很麻烦的事情,Trait 就是为了解决这个 ...

  8. scala 学习笔记(05) OOP(中)灵活的trait

    trait -- 不仅仅只是接口! 接上回继续,scala是一个非常有想法的语言,从接口的设计上就可以发现它的与众不同.scala中与java的接口最接近的概念是trait,见下面的代码: packa ...

  9. Scala Trait

    Scala Trait 大多数的时候,Scala中的trait有点类似于Java中的interface.正如同java中的class可以implement多个interface,scala中的cals ...

  10. scala中的trait

    这里的trait字面意思是特质或者特征,这个词翻译成特征比较合适.它的意义和java,c#中接口很类似.但是trait支持部分实现,也就是说可以在scala的trait中可以实现部分方法. 下面我们以 ...

随机推荐

  1. saltstack操作

    使用分组 修改minon的ID,做一个标识 [root@node2 ~]# vim /etc/salt/minionmaster: 172.25.254.130 id: web1 #node2[roo ...

  2. 我终于懂得如何使用matplotlib进行画图

    一 前言 本篇文章带大家快速入门如何使用matplotlib画出精美数学的图片:看完本篇文章你将获得熟悉并简单使用matplotlib工具,会画基本得折现图,散点图,sin,cos图,一张画布画出多图 ...

  3. Java工作流系统-父子流程的配置讲解

    父子流程 关键字: 驰骋工作流程快速开发平台 工作流程管理系统 工作流引擎 asp.net工作流引擎 java工作流引擎. 开发者表单  拖拽式表单 工作流系统 适配数据库: oralce,mysql ...

  4. Educational Codeforces Round 53 (Rated for Div. 2) (前五题题解)

    这场比赛没有打,后来补了一下,第五题数位dp好不容易才搞出来(我太菜啊). 比赛传送门:http://codeforces.com/contest/1073 A. Diverse Substring ...

  5. Java虚拟机详解(十一)------双亲委派模型

    在上一篇博客,我们介绍了类加载过程,包括5个阶段,分别是“加载”,“验证”,“准备”,“解析”,“初始化”,如下图所示: 本篇博客,我们来介绍Java虚拟机的双亲委派模型,在介绍之前,我先抛出一个问题 ...

  6. js动态改变下拉框内容

    今天为大家分享一篇js动态设置select下拉菜单的默认选中项实例,具有很好的参考价值,希望对大家有所帮助. 代码实例如下: <!DOCTYPE html> <html lang=& ...

  7. 02_jQuery 验证密码是6位或者8位纯数字

    var reg = new RegExp(/^\d{8}$/); //工作密码必须是8位数字 if(!reg.test("12544444").val())) { alert(&q ...

  8. java基础之----分布式事务tcc

    最近研究了一下分布式事务框架,ttc,总体感觉还可以,当然前提条件下是你要会使用这个框架.下面分层次讲,尽量让想学习的同学读了这篇文章能加以操作运用.我不想废话,直接上干货. 一.什么是tcc?干什么 ...

  9. zabbix 4.04 安装文档 - 基于CentOS 7.6

    1    安装前准备: 1.1   安装JDK 卸载openjdk # rpm -qa | grep java # yum remove java-1.8.0-openjdk # yum remove ...

  10. 小白学 Python 爬虫(36):爬虫框架 Scrapy 入门基础(四) Downloader Middleware

    人生苦短,我用 Python 前文传送门: 小白学 Python 爬虫(1):开篇 小白学 Python 爬虫(2):前置准备(一)基本类库的安装 小白学 Python 爬虫(3):前置准备(二)Li ...