一、trait五种应用场景
1. trait功能1:代码复用
- 公共show在trait里,包含属性和数组,如何打印两组类里的数组?
<?php
trait tDemo
{
// 方法show
public function show()
{
// 打印所有的属性,打印数组,获取属性get_class_vars(__CLASS__)
printf('<pre>%s</pre>', print_r(get_class_vars(__CLASS__), true));
}
}
class User1
{
use tDemo;
// 属性
protected $name = '小刘';
protected $gender = '男';
}
(new User1)->show();
class User2
{
use tDemo;
// 属性
protected $name = '小芳';
protected $gender = '女';
}
(new User2)->show();
2. trait功能2:在继承上下文中的应用
- 子类>trait>父类
<?php
trait tDemo
{
public static function hello()
{
return 'trait中的方法' . __METHOD__;
}
}
// 不常用的声明为抽象类abstract,基类/父类/超类
// __METHOD__魔术常量
abstract class Dad
{
public static function hello()
{
return '基类中的方法' . __METHOD__;
}
}
// 子类,扩展类
class Son extends Dad
{
}
echo Son::hello();
3. trait功能3:实现功能扩展
- 用tDemo3来组合tDemo1和tDemo2,打印所有属性、打印所有方法
<?php
// trait功能3:实现功能扩展
trait tDemo1
{
// 1.打印所有属性
public function getProps()
{
printf('<pre>%s</pre>', print_r(get_class_vars(__CLASS__), true));
}
// __CLASS__:返回当前类的名称字符串
// self:返回当前类的引用
}
trait tDemo2
{
// 2.打印所有方法
public function getMethods()
{
printf('<pre>%s</pre>', print_r(get_class_methods(__CLASS__), true));
}
}
// 用tDemo3来组合tDemo1和tDemo2
trait tDemo3
{
use tDemo1, tDemo2;
}
class Work1
{
use tDemo1, tDemo2;
public $name = '电脑';
public $price = 8888;
public function getInfo()
{
return $this->name .': ' .$this->price;
}
}
echo (new Work1)->getInfo();
echo (new Work1)->getProps();
echo (new Work1)->getMethods();
class Work2
{
use tDemo3;
public $name = '电脑';
public $price = 8888;
public function getInfo()
{
return $this->name .': ' .$this->price;
}
}
echo (new Work2)->getInfo();
echo (new Work2)->getProps();
echo (new Work2)->getMethods();
4. trait功能4:同名处理
<?php
trait tDemo1
{
public function display()
{
// 打印trait值和方法名称
return __METHOD__;
}
}
trait tDemo2
{
public function display()
{
// 打印trait值和方法名称
return __METHOD__;
}
}
trait tDemo3
{
use tDemo1, tDemo2 {
// 方法同名时,给个别名td2
tDemo2::display as td2;
// 声明调用tDemo1::display()替换掉tDemo2::display()
tDemo1::display insteadOf tDemo2;
}
}
class Work
{
use tDemo3;
}
echo (new Work)->display();
echo "<hr>";
echo (new Work)->td2();
5. trait功能5:trait和interface接口组合
<?php
interface iDemo
{
public static function index();
}
trait tDemo
{
public static function index()
{
return __METHOD__;
}
}
class Hello implements iDemo
{
use tDemo;
}
echo Hello::index();
- 以上为trait5种应用场景,客户端打印如下图:
二、trait与接口,抽象类之间的区别,用双色球做实例
1. 抽象类+接口interface+trait,创建demo6.php
- 接口interface中的抽象类方法可以放在trait中,并引入到当前工作类中
<?php
// 双色球开奖奖品
$prizes = ['手机' ,'电脑', '10万', '100万', '汽车'];
interface iCreateId
{
// 声明一个方法
public static function generateId($min, $max);
}
trait createId
{
// 生成一个唯一ID,用一个方法
public static function generateId($min, $max)
{
return mt_rand($min, $max);
}
}
// 开奖类
class DrawPrize implements iCreateId
{
// 把接口中的抽象方法放在trait中,并引入到当前工作类中
use createId;
// 发奖品
public static function award($prizes, $id)
{
return $prizes[$id];
}
}
// 奖品id从0开始,奖品随机
$id = DrawPrize::generateId(0 ,4);
// echo $id;
// 生成奖品
$prize = DrawPrize::award($prizes, $id);
printf('奖品:<span style="color:red">%s</span>', $prize);
2. 把demo6.php引入到demo7.php,双色球开奖实战,随机生成开奖号码与试机号码
<?php
// 双色球开奖实战
// 引入接口iCreateId,trait,createId
require 'demo6.php';
// 抽象类:彩票
abstract class Lottery implements iCreateId
{
use createId;
// 1.生成中奖所需球的编号
protected static function createBalls($min, $max, $num)
{
// 按照规则生成指定步长与数量的编号
$allBalls = range($min, $max, 1);
// 根据球的数量来区别是红球还是篮球
if ($num === 1) return $allBalls[array_rand($allBalls)];
// 取红球,指定哪些可出现,过滤并判断,出现过的值就不要再出现在结果中
// 生成一组,不重复的
$redBallskeys = array_rand($allBalls, $num);
$redBalls = [];
// 遍历由6个键名组成的数组,根据每个键名 从红球数组中随机取出6个,组成一个新数组$redBalls
foreach (array_rand($allBalls, $num) as $key){
$redBalls [] = $allBalls[$key];
}
return $redBalls;
}
// 2.生成一组中奖号码
//6个红球,1个篮球: red[1,33,6],bule[1,16,1]
abstract protected static function doubeColorBall(array $redRule, array $blueRule);
// 3.随机生成一组号
abstract protected static function createRandBalls(array $redRule, array $blueRule, array $range);
}
// 实现类:抽奖类
class DrawLottery extends Lottery
{
// 生成一组中奖号码
public static function doubeColorBall(array $redRule, array $blueRule)
{
// 生成红球 red[1,33,6]
$redBalls = self::createBalls(...$redRule);
sort($redBalls);
// 生成篮球bule[1,16,1]
$blueBalls = self::createBalls(...$blueRule);
// sort($buleBalls);
// 红球与篮球组合,生成一组号码
array_push($redBalls, $blueBalls);
return $redBalls;
}
// 随机生成5组号
public static function createRandBalls(array $redRule, array $blueRule, array $range=[1, 10])
{
$count = self::generateId(...$range);
$randBalls = [];
for ($i = 0; $i< $count; $i++){
$randBalls[]=self::doubeColorBall($redRule, $blueRule);
}
return $randBalls;
}
}
$draw = DrawLottery::doubeColorBall([1,33,6], [1,16,1]);
// 生成5组试机号
$randBalls = DrawLottery::createRandBalls([1,33,6], [1, 16, 1], [1,5]);
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>模拟双色球</title>
<style>
.container1 {
display: grid;
grid-template-columns: repeat(7, 40px);
gap: 10px;
margin-top: 10px;
}
.container1 > .ball {
width: 40px;
height: 40px;
color: white;
font-weight: bold;
text-shadow: 1px 1px 1px #555;
border-radius: 50%;
box-shadow: 3px 3px 3px #888;
text-align: center;
line-height: 40px;
}
.container1 > .ball:nth-of-type(-n+6) {
background-color: red;
}
.container1 > .ball:last-of-type {
background-color: deepskyblue;
}
</style>
</head>
<body>
<h2>模拟双色球开奖</h2>
<h3>今日开奖号码: <small style="color: green"><?=date('Y年m月d日', time())?></small></h3>
<div class="container1">
<?php foreach ($draw as $item) : ?>
<span class="ball"><?=$item?></span>
<?php endforeach;?>
</div>
<hr>
<h3>今日试机号:</h3>
<!--将上面显示一组号码的代码再套一个外层循环就可以-->
<?php foreach ($randBalls as $draw): ?>
<div class="container1">
<?php foreach ($draw as $item) : ?>
<span class="ball"><?=$item?></span>
<?php endforeach;?>
</div>
<?php endforeach;?>
</body>
</html>
- 总结:
- trait的5大功能:代码复用、继承上下文中的应用、实现功能扩展、同名处理、trait和接口interface组合
- 子类同名成员优先级高于父类,子类>trait>父类
- 可以跨文档引入接口iCreateId,trait,createId,方法:require ‘demo6.php’;
- 接口interface中的抽象方法可放在trait中,并引入到当前工作类中,进行实例