Blogger Information
Blog 62
fans 2
comment 1
visits 42113
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
PHP 15 trait与trait与接口实战(0723thu)
老黑
Original
595 people have browsed it

主要内容:

  1. trait的功能
    • 代码复用
    • 在继承上下文中的应用
    • 实现功能扩展
  2. 在trait组合中命名冲突的解决方案
  3. trait 和 interface的组合
  4. 抽象类中的static方法中的self实现(这部分内容是在第二天0724中的,但适合放到这块)
  5. 实战-抽奖及双色球(双色球这个案例挺无聊的)

1. trait的功能

1-1. 代码复用

  1. <?php
  2. // 使用trait将公共的show()提取出来
  3. trait tDemo
  4. {
  5. public function show()
  6. {
  7. printf('<pre>%s</pre>', print_r(get_class_vars(__CLASS__), true));
  8. //这个地方的class就是指这个class。get_class_vars就是获得这个class中所有的vars。
  9. // __CLASS__: 返回当前类的名称字符串
  10. // self: 返回的当前类的引用
  11. }
  12. }
  13. class User1
  14. {
  15. use tDemo;
  16. protected $name = '张三';
  17. protected $gender = '男';
  18. }
  19. (new User1)->show();
  20. echo '<hr>';
  21. class User2
  22. {
  23. use tDemo;
  24. protected $name = '小王';
  25. protected $gender = '女';
  26. }
  27. (new User2)->show();

1-2. 在继承上下文中的应用

  • 优先次序:子类 > trait > 父类
  1. <?php
  2. trait tDemo
  3. {
  4. // public static $name = 'trait中的属性';
  5. public static function hello()
  6. {
  7. return 'trait中的方法' . __METHOD__;
  8. }
  9. }
  10. // 基类/父类/超类
  11. abstract class Dad
  12. {
  13. public static function hello()
  14. {
  15. return '基类中的方法' . __METHOD__;
  16. }
  17. }
  18. // 子类/扩展类
  19. class Son extends Dad
  20. {
  21. // 引入trait
  22. // use tDemo;
  23. // public static $name = '扩展类中的属性';
  24. // public static function hello()
  25. // {
  26. // return '扩展类中的方法' . __METHOD__;
  27. // }
  28. }
  29. echo Son::hello();

1-3. 实现功能扩展

  1. <?php
  2. trait tDemo1
  3. {
  4. // 打印所有属性
  5. public function getProps()
  6. {
  7. printf('<pre>%s</pre>', print_r(get_class_vars(__CLASS__), true));
  8. }
  9. }
  10. trait tDemo2
  11. {
  12. // 打印所有方法
  13. public function getMethods()
  14. {
  15. printf('<pre>%s</pre>', print_r(get_class_methods(__CLASS__), true));
  16. }
  17. }
  18. // 使用tDemo3来组合tDemo1和tDemo2这二个trait
  19. trait tDemo3
  20. {
  21. use tDemo1, tDemo2;
  22. }
  23. class Work1
  24. {
  25. // 扩展这个类的功能
  26. // 添加二个公共方法
  27. // 1. 打印所有属性
  28. // 2. 打印所有方法
  29. use tDemo1, tDemo2;
  30. public $name = '西瓜';
  31. public $price = 2;
  32. public function getInfo()
  33. {
  34. return $this->name . ': ' . $this->price;
  35. }
  36. }
  37. echo (new Work1)->getInfo(), '<hr>';
  38. echo (new Work1)->getProps();
  39. echo (new Work1)->getMethods();
  40. class Work2
  41. {
  42. // 此时我只需要引入一个trait就可以
  43. use tDemo3;
  44. public $name = '西瓜';
  45. public $price = 2;
  46. public function getInfo()
  47. {
  48. return $this->name . ': ' . $this->price;
  49. }
  50. }
  51. echo (new Work2)->getInfo(), '<hr>';
  52. echo (new Work2)->getProps();
  53. echo (new Work2)->getMethods();

2. 在trait组合中命名冲突的解决方案

  1. <?php
  2. trait tDemo1
  3. {
  4. public function display()
  5. {
  6. return __METHOD__;
  7. }
  8. }
  9. trait tDemo2
  10. {
  11. public function display()
  12. {
  13. return __METHOD__;
  14. }
  15. }
  16. trait tDemo3
  17. {
  18. use tDemo1, tDemo2 {
  19. // 给tDemo2::display()起个别名: td2
  20. tDemo2::display as td2;
  21. // 调用tDemo1::display()替换掉tDemo2::display()
  22. tDemo1::display insteadOf tDemo2;
  23. }
  24. }
  25. // 工作类尽可能写得代码清晰,简洁
  26. class Work
  27. {
  28. use tDemo3;
  29. }
  30. echo (new Work)->display(), '<hr>';
  31. // 别名访问tDemo2::display
  32. echo (new Work)->td2();

3. trait 和 interface的组合

  1. <?php
  2. // 接口
  3. interface iDemo
  4. {
  5. public static function index();
  6. }
  7. // trait
  8. trait tDemo
  9. {
  10. // 将接口中的抽象方法的实现过程放在trait中实现,并在工作类中调用
  11. public static function index()
  12. {
  13. return __METHOD__;
  14. }
  15. }
  16. // 实现类
  17. class Hello implements iDemo
  18. {
  19. use tDemo;
  20. }
  21. // 客户端
  22. echo Hello::index();

4. 抽象类中的static方法中的self实现

  • 有些绕口啊,其实就是part1中普通class中的方法上移到抽象类中,其中用self会报错。
  • 这个时候需要将self换成static(前者的意思应该是绝对绑定的self,后者static则有相对绑定的意思—— 后期静态绑定)

  • self::总是与当前声明该方法(create)的类绑定,并不能与调用类(User/Product)绑定

  • 声明类: CreateInstance(抽象类)
  • 调用类: User, Product
  • 解决: 将类的定义与类的调用完全分离
  • 使用static: 后期静态绑定
  • 前期: 声明
  • 后期: 调用
  • static::关键字,可以自动与当前方法的调用类进行绑定

  • part1

  1. <?php
  2. abstract class CreateInstance
  3. {
  4. }
  5. class User extends CreateInstance
  6. {
  7. // 创建当前类的实例
  8. public static function create() : self
  9. {
  10. return new self();
  11. }
  12. }
  13. class Product extends CreateInstance
  14. {
  15. // 创建当前类的实例
  16. public static function create() : self
  17. {
  18. return new self();
  19. }
  20. }
  21. // 生成User的实例
  22. $user = User::create();
  23. var_dump($user);
  24. // 生成Product的实例
  25. $product = Product::create();
  26. var_dump($product);
  • part2
  1. <?php
  2. abstract class CreateInstance
  3. {
  4. // 创建当前类的实例
  5. public static function create() : self
  6. {
  7. // self::总是与当前声明该方法(create)的类绑定,并不能与调用类(User/Product)绑定
  8. // 声明类: CreateInstance(抽象类)
  9. // 调用类: User, Product
  10. // 因此用下面这个就不行,需要用后面的static部分。
  11. // return new self();
  12. // 解决: 将类的定义与类的调用完全分离
  13. // 使用static: 后期静态绑定
  14. // 前期: 声明
  15. // 后期: 调用
  16. // static::关键字,可以自动与当前方法的调用类进行绑定
  17. return new static();
  18. }
  19. }
  20. class User extends CreateInstance
  21. {
  22. }
  23. class Product extends CreateInstance
  24. {
  25. }
  26. // 生成User的实例
  27. // 当前调用create()方法的类是User,它不是抽象类
  28. $user = User::create();
  29. var_dump($user);
  30. // 生成Product的实例
  31. $product = Product::create();
  32. var_dump($product);
  33. // static: 之前用来声明静态成员, 现在还有可以与静态成员 的调用类进行绑定

5. 实战1: 抽奖程序

  • 教程
  1. <?php
  2. // 实战: 双色球开奖的背景知识
  3. // 抽象类 + 接口 + trait
  4. // 奖品
  5. $prizes = ['电脑', '手机', '平板', '耳机', '拖鞋', '口罩'];
  6. interface iCreateId
  7. {
  8. public static function generateId($min, $max);
  9. }
  10. trait createId
  11. {
  12. // 生成一个唯一ID: 抽象方法的实现
  13. public static function generateId($min, $max)
  14. {
  15. return mt_rand($min, $max);
  16. }
  17. }
  18. // 开奖类
  19. class DrawPrize implements iCreateId
  20. {
  21. // 当前接口中的抽象方法,放在了trait中实现,并引入到当前的工作类中
  22. use createId;
  23. // 发奖品
  24. public static function award($prizes, $id)
  25. {
  26. return $prizes[$id];
  27. }
  28. }
  29. // $id = DrawPrize::generateId(0, 5);
  30. // $prize = DrawPrize::award($prizes, $id);
  31. // printf('奖品是: <span style="color:red">%s</span>', $prize);
  • 自己的部分
  1. <?php
  2. $prizes = ['钢笔','圆珠笔','铅笔','橡皮','手表','手机','宇宙飞船'];
  3. interface iCreateID
  4. {
  5. public static function generateId($min,$max);
  6. //这里是一个抽象方法,然后后面在trait中来实现。
  7. //没有大括号实现的就是抽象方法。
  8. }
  9. trait createID
  10. {
  11. public static function generateID($min, $max)
  12. {
  13. return mt_rand($min, $max);
  14. }
  15. }
  16. class DrawPrize implements iCreateID
  17. {
  18. use createID;
  19. public static function award($prizes, $id)
  20. {
  21. return $prizes[$id];
  22. }
  23. }
  24. $id = DrawPrize::generateId(0, 6);
  25. $prize = DrawPrize::award($prizes, $id);
  26. printf('一共有七个个奖品:<br>钢笔-圆珠笔-铅笔-橡皮-手表-手机-宇宙飞船<br><br>您的运气还不错,
  27. 本次中奖的奖品是:<br ><span style = "color:red">%s<span>', $prize);

6. 实战2:双色球

  • 教程的部分
  1. <?php
  2. // 引入接口iCreateId, trait: createId
  3. require 'demo6.php';//上一个抽奖就是demo6.php
  4. // 抽象类: 彩票
  5. abstract class Lottery implements iCreateId
  6. {
  7. // 在trait中实现接口中的抽象方法
  8. use createId;
  9. // 1. 生成中奖所需要的球的编号。红球、蓝球都用一个function。红球从33个中最终随机产生6个,篮球从16个中随机产生1个。
  10. protected static function createBalls($min, $max, $num)
  11. {
  12. // ①. 按开奖规则生成指定步长与数量的球编号
  13. $allBalls = range($min, $max, 1);
  14. // ②. 根据取出的球的数量来区别当前是红球还是蓝球。
  15. // 数量为1时取篮球。因此就是随机取1个即可。
  16. if ($num === 1) return $allBalls[array_rand($allBalls)];
  17. // ③. 取红球
  18. return array_filter(array_rand($allBalls, $num), function ($key) use ($allBalls) {
  19. // array_rand()返回的索引键名中有可能包括0,以及已经取出的key,需要过滤掉。
  20. // $num为打算取出的数量。
  21. //return array_key_exists($key, $allBalls);
  22. // 添加 ($key > 0) 进行过滤
  23. return ($key > 0) && array_key_exists($key, $allBalls);
  24. }) ;
  25. }
  26. // 2. 生成一个双色球的中奖号
  27. // red: [1,33, 6], blue: [1,16, 1]
  28. abstract protected static function doubleColorBall(array $redRule, array $blueRule);
  29. // 3. 随机生成一组试机号
  30. abstract protected static function createRandBalls(array $redRule, array $blueRule, array $range);
  31. }
  32. // 实现类: 抽奖
  33. class DrawLottery extends Lottery
  34. {
  35. // 生成一个双色球的中奖号
  36. // red: [1,33, 6], blue: [1,16, 1]
  37. public static function doubleColorBall(array $redRule, array $blueRule)
  38. {
  39. // 1. 生成红球的red: [1,33, 6]
  40. $redBalls = self::createBalls(...$redRule);
  41. sort($redBalls);
  42. // 2.生成蓝球 blue: [1, 16, 1]
  43. $blueBalls = self::createBalls(...$blueRule);
  44. // 3. 将红蓝球进行组合生成一组中奖号码
  45. array_push($redBalls, $blueBalls);
  46. return $redBalls;
  47. }
  48. // 随机生成一组试机号
  49. public static function createRandBalls(array $redRule, array $blueRule, array $range=[1, 10])
  50. {
  51. $count = self::generateId(...$range);
  52. //...是将$range中的1-10进行了拓展。因为generateId(demo6中有)后面应该是($min,$max)。...发挥的作用其实就是这样的。
  53. $randBalls = []; //用来接收收取出来的随机数字。
  54. for ($i = 0; $i < $count; $i++) {
  55. $randBalls[]=self::doubleColorBall($redRule, $blueRule);
  56. }
  57. //产生出随机数组。
  58. return $randBalls;
  59. }
  60. }
  61. $draw = DrawLottery::doubleColorBall([1,33,6], [1, 16, 1]);
  62. $randBalls = DrawLottery::createRandBalls([1,33,6], [1, 16, 1], [1,6]);
  63. ?>
  64. <!doctype html>
  65. <html lang="en">
  66. <head>
  67. <meta charset="UTF-8">
  68. <meta name="viewport"
  69. content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  70. <meta http-equiv="X-UA-Compatible" content="ie=edge">
  71. <title>模拟双色球</title>
  72. <style>
  73. .container1 {
  74. display: grid;
  75. grid-template-columns: repeat(7, 40px);
  76. gap: 10px;
  77. margin-top: 10px;
  78. }
  79. .container1 > .ball {
  80. width: 40px;
  81. height: 40px;
  82. color: white;
  83. font-weight: bold;
  84. text-shadow: 1px 1px 1px #555;
  85. border-radius: 50%;
  86. box-shadow: 3px 3px 3px #888;
  87. text-align: center;
  88. line-height: 40px;
  89. }
  90. .container1 > .ball:nth-of-type(-n+6) {
  91. background-color: red;
  92. }
  93. .container1 > .ball:last-of-type {
  94. background-color: deepskyblue;
  95. }
  96. </style>
  97. </head>
  98. <body>
  99. <h2>模拟双色球开奖</h2>
  100. <h3>今日开奖号码: <small style="color: green"><?=date('Y年m月d日', time())?></small></h3>
  101. <div class="container1">
  102. <?php foreach ($draw as $item) : ?>
  103. <span class="ball"><?=$item?></span>
  104. <?php endforeach;?>
  105. </div>
  106. <hr>
  107. <h3>今日试机号:</h3>
  108. <!--将上面显示一组号码的代码再套一个外层循环就可以-->
  109. <?php foreach ($randBalls as $draw): ?>
  110. <div class="container1">
  111. <?php foreach ($draw as $item) : ?>
  112. <span class="ball"><?=$item?></span>
  113. <?php endforeach;?>
  114. </div>
  115. <?php endforeach;?>
  116. </body>
  117. </html>
Correcting teacher:天蓬老师天蓬老师

Correction status:qualified

Teacher's comments:将类分层管理是一个好办法
Statement of this Website
The copyright of this blog article belongs to the blogger. Please specify the address when reprinting! If there is any infringement or violation of the law, please contact admin@php.cn Report processing!
All comments Speak rationally on civilized internet, please comply with News Comment Service Agreement
0 comments
Author's latest blog post