博客列表 >PHP 15 trait与trait与接口实战(0723thu)

PHP 15 trait与trait与接口实战(0723thu)

老黑
老黑原创
2020年07月25日 22:44:09593浏览

主要内容:

  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>
声明:本文内容转载自脚本之家,由网友自发贡献,版权归原作者所有,如您发现涉嫌抄袭侵权,请联系admin@php.cn 核实处理。
全部评论
文明上网理性发言,请遵守新闻评论服务协议