php7 trait 属性介绍

Trait

trait 是一个和类有点像的结构,其本身不能被实例化,但是可以被类引用。任何定义在 trait 中的方法,都能被包含trait 的类访问。可以简单的把 trait 的作用看做是 include 一个类。

Trait 解决什么问题

面向对象设置的目标就是代码重用(剔除重复的代码),trait 提供了一种解决方案。

<?php

// 定义一个 Trait 
trait PriceUtilities
{
	private $taxrate = 17;

	public function calculateTax(float $price):float
	{
		return (($this->taxrate / 100) * $price);
	}
}

class ShopProduct
{
	use PriceUtilities; // 调用 Trait
}

abstract class Service{}

class UtilityService extends Service
{
	use PriceUtilities;
}

$p = new ShopProduct();
echo $p->calculateTax(100) . "\n";

$u = new UtilityService();
echo $u->calculateTax(100) . "\n";

上面代码中 calculateTax 方法一处定义,多处使用。

调用多个 Trait

当需要调用多个 Trait 时,可以这么写

<?php
// ...

// 定义另外一个 Trait
trait IdentityTrait
{
	public function generateId(): string
	{
		return uniqid();
	}
}

// ...
class UtilityService extends Service {
	use PriceUtilities, IdentityTrait;
}
// ...

$u = new UtilityService();
echo $u->calculateTax(100) . "\n";
echo $u->generateId() . "\n";

Trait 和 interface 结合

<?php

interface IdentityObject{
	public function generateId(): string;
}

trait IdentityTrait{
	public function generateId(): string
	{
		return uniqid();
	}
}

trait PriceUtilities
{
	private $taxrate = 17;
	public function calculateTax(float $price): float
	{
		return (($this->taxrate / 100) * $price);
	}
// other utilities
}

class ShopProduct implements IdentityObject
{
	use PriceUtilities, IdentityTrait; // IdentityTrait Trait 的引入作为接口 IdentityObject 的实现
}

$p = new ShopProduct();
echo $p->calculateTax(100) . "\n";
echo $p->generateId() . "\n";

补充: interface 和抽象方法有和区别和联系?

关键字 insteadof 解决引入多个 Trait 的命名冲突问题

<?php
# 执行下面的代码,会产生一个致命错误 (因为引入的 trait 中的同名方法冲突了)
abstract class Service{};

trait TaxTools
{
	function calculateTax(float $price): float
	{
		return 222;
	}
}

trait PriceUtilities
{
	private $taxrate = 17;

	public function calculateTax(float $price): float
	{
		return (($this->taxrate / 100) * $price);
	}
}

class UtilityService extends Service
{
	use PriceUtilities, TaxTools;
}

$u = new UtilityService();
echo $u->calculateTax(100) . "\n";

通过改写 UtilityService 类如下来解决冲突问题:

<?php
// ...
class UtilityService extends Service
{
	use PriceUtilities, TaxTools
	{
		TaxTools::calculateTax insteadof PriceUtilities; // 使用 TaxTools::calculateTax 方法覆盖同名方法
	}
}
// ...

当然,如果不覆盖还有别名法可选:

<?php
class UtilityService extends Service
{
	use PriceUtilities, TaxTools {
		TaxTools::calculateTax insteadof PriceUtilities;
		PriceUtilities::calculateTax as basicTax;
	}
}

在 Trait 中使用静态方法

<?php

// 好吧,和类中并无不同
abstract class Service{};

trait PriceUtilities
{
	private static $taxrate = 17;

	public static function calculateTax(float $price): float
	{
		return ((self::$taxrate / 100) * $price);
	}
}

class UtilityService extends Service
{
	use PriceUtilities;
}

$u = new UtilityService();
echo $u::calculateTax(100) . "\n";

访问宿主类属性

<?php

// 尽管我们通常不会这么做,因为这是一个操作的设计
abstract class Service{};

trait PriceUtilities
{
	function calculateTax(float $price): float
	{
		return (($this->taxrate / 100) * $price);
	}
}

class UtilityService extends Service
{
	public $taxrate = 17;
	use PriceUtilities;
}

$u = new UtilityService();
echo $u->calculateTax(100) . "\n";

在 Trait 中定义抽象方法

下面使用抽象方法改写前面的例子:

<?php

// 和类中定义抽象方法相同
abstract class Service{};

trait PriceUtilities
{
	function calculateTax(float $price): float
	{
		return (($this->getTaxRate() / 100) * $price);
	}

	abstract function getTaxRate():float;
}

class UtilityService extends Service
{
	use PriceUtilities;

	public function getTaxRate():float
	{
		return 17;
	}
}

$u = new UtilityService();
echo $u->calculateTax(100) . "\n";

改变 Trait 的访问级别

<?php
abstract class Service{};

trait PriceUtilities
{
	public function calculateTax(float $price): float
	{
		return (($this->getTaxRate() / 100) * $price);
	}

	public abstract function getTaxRate(): float;
}

class UtilityService extends Service
{
	use PriceUtilities {
		PriceUtilities::calculateTax as private; // 定义访问级别
	}

	private $price;

	public function __construct(float $price) {
		$this->price = $price;
	}

	public function getTaxRate():float
	{
		return 17;
	}

	public function getFinalPrice(): float
	{
		return ($this->price + $this->calculateTax($this->price));
	}
}

$u = new UtilityService(100);
print $u->getFinalPrice() . "\n";

// 注意: 下面这个访问会报错,类似访问了私有的方法错误
print $u->calculateTax()."\n";

延时静态绑定

下面是一个列子:

<?php
abstract class DomainObject{}

class User extends DomainObject
{
	public static function create(): User
	{
		return new User();
	}
}

class Document extends DomainObject
{
	public static function create(): Document
	{
		return new Document();
	}
}

这段代码可以正常工作,只是有一些代码重复,我不想为每个 DomainObject 子类复制类似的代码,于是我将 create () 方超类中实现,如下:

<?php

// 代码执行会出现一个错误提示
abstract class DomainObject{
	public  function create(): DomainObject
	{
		return new self(); // 如果你使用的编辑器带有提示功能,会发现这里已经标记错误提示了
		// return new static(); // 使用这行代替上面那行就可以正常工作了,这是 php5.3 一个不怎么优雅的变通方案
	}
}

class User extends DomainObject
{

}

class Document extends DomainObject
{

}

Document::create();

上面的代码看起来解决了代码重复的问题,但是实际上代码没有按我们预想的那样工作 - 我们假设 self 关键字会和 $this 有类似的表现,实际上 self 和调用方法没有关联,和定义方法关联,这是报错的原因 ( 代码注释中,我们有一个变通方案 )。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

胡德咏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值