我所理解的 PHP Trait
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 起名困难时,请认真思考你是否真的拆分对了,因为正确的拆分是很容易描述 “它是一个具有什么功能的特性” 的;
总之一定要记住:不要为了让两段相同的代码提到一起这样简单粗暴的方式来拆分。
以上是个人见解,欢迎各位讨论。
我所理解的 PHP Trait的更多相关文章
- php中trait(性状)与generator(生成器)
PHP中trait(性状)与generator(生成器) 一.trait (性状) 最近在看Josh Lockhat的<Modern PHP>,这本书很薄.但是其中给出了一个很重要的学习方 ...
- <基础> PHP 进阶之 抽象类(abstract)、接口(interface)、Trait(特征)
抽象类 PHP 5 支持抽象类和抽象方法.定义为抽象的类不能被实例化. 抽象方法只能在抽象类中,抽象类中可以包含非抽象方法 被定义为抽象的方法只是声明了其调用方式(参数),不能定义其具体的功能实现 继 ...
- 如何在 Laravel 中灵活的使用 Trait
如何在 Laravel 中灵活的使用 Trait Laravel/ 3个月前/ 1740 / 4 / 更新于 3个月前 @这是小豪的第九篇文章 好久没有更新文章了,说好了周更结果还是被自己对 ...
- Scala编程基础
Scala与Java的关系... 4 安装Scala. 4 Scala解释器的使用... 4 声明变量... 5 数据类型与操作符... 5 函数调用与apply()函数... 5 if表达式... ...
- Scala trait特质 深入理解
Scala trait特质 深入理解 初探Scala 特质trait 在Scala中,trait(特质)关键字有着举足轻重的作用.就像在Java中一样,我们只能在Scala中通过extends进行单一 ...
- php5.4 trait 理解与学习
Trait 是 php5.4引入的新特性,手册上说的一大段没看懂,这里直接来过来. Trait 是为类似 PHP 的单继承语言而准备的一种代码复用机制.Trait 为了减少单继承语言的限制,使开发人员 ...
- scala 学习笔记(05) OOP(中)灵活的trait
trait -- 不仅仅只是接口! 接上回继续,scala是一个非常有想法的语言,从接口的设计上就可以发现它的与众不同.scala中与java的接口最接近的概念是trait,见下面的代码: packa ...
- scala 学习:object 和class, trait
object: Scala中没有静态修饰符,static,在object下的成员全部都是静态的,如果在类中声明了与该类相同的名字的object则该object是该类的"半生对象", ...
- trait技术详解,这次包你学得会
trait的使用技巧trait是php5.4以后新增加的一个功能,可以将多个类中,共用的一些属性和方法提取出来做来公共trait类,就像是装配汽车的配件,如果你的类中要用到这些配件,就直接用use导入 ...
随机推荐
- BZOJ2186: [Sdoi2008]沙拉公主的困惑(求[1,N!]与M!互素的个数)(线性筛)
2186: [Sdoi2008]沙拉公主的困惑 Time Limit: 10 Sec Memory Limit: 259 MBSubmit: 6103 Solved: 2060[Submit][S ...
- kafka搭建
接下来一步一步搭建Kafka运行环境. Step 1: 下载Kafka 点击下载最新的版本并解压. > tar -xzf kafka_2.9.2-0.8.1.1.tgz > cd kafk ...
- opencrud 中文参考翻译(完成部分)
opencrud 是一个就比较好的关于graphql 实现的指南(当前只有部分文档,完整的还没有,实际上apollo 有相关的文档都挺不错的) 同时在github 有一个中文的简单翻译(后期应该会和官 ...
- 关于CCRANDOM_0_1
CCRANDOM_0_1的范围是[0,1)包括0但不包括1 CCRANDOM_0_1() * 1400.0f / 100.0f是0-13 另外每次随机都是相同的数,要随机下种子 srand((unsi ...
- 修改Gradle 和Maven本地仓库的位置方法
本文转载自:https://www.cnblogs.com/dwb91/p/6523541.html 关于Maven的配置: 用过Maven的开发人员应该知道Maven可以通过配置 conf文件夹下面 ...
- Bootstrap-CL:面包屑导航
ylbtech-Bootstrap-CL:面包屑导航 1.返回顶部 1. Bootstrap 面包屑导航(Breadcrumbs) 面包屑导航(Breadcrumbs)是一种基于网站层次信息的显示方式 ...
- linux Valgrind使用说明-内存泄漏
Valgrind使用说明 Valgrind manual: http://valgrind.org/docs/manual/manual.html valgrind的介绍.安装和使用 valgrind ...
- Linux操作系统-基本命令(二)
Linux操作系统基本命令 文件操作类命令 – ln命令 另外一种链接方式称为符号链接(软链接),是指一个文件指向另外一个文件的文件名.软链接类似于Windows系统中的快捷方式.软链接由ln -s命 ...
- Java 8 : Stream API 练习
//店铺属性类 public class Property { String name; // 距离,单位:米 Integer distance; // 销量,月售 Integer sales; // ...
- Running Your App(运行你的应用程序)
如果你按照上一篇课程创建了android项目,它将包含helloworld的源文件,让你可以立即运行你的应用程序. 你如何运行你的应用程序app依赖于两件事: 你是否拥有一台真正的android设备 ...