博客列表 >静态绑定与拦截器

静态绑定与拦截器

溪边小树
溪边小树原创
2020年05月11日 16:45:21636浏览

继承、抽象类、接口、trait要点整理

近期学习了面向对象编程中一些比较抽象的概念,在此对部分重点内容的要点做一下整理小结。
1、类的继承,也叫类的扩展(父类: 基类、子类: 扩展类)
二类成员: 子类有权访问的父类成员类型, public, protected
三种操作: 子类对父类的三种操作: 继承, 重写, 扩展

  1. 继承: 父类的二类成员自动成为子类的成员
  2. 重写: 覆写与父类/基类同名的成员(属性, 方法)
  3. 扩展: 子类添加自身的方法来增加/扩展父类的功能

2、抽象类: 部分分离了设计(抽象类中完成)与实现(工作类中完成)
设计类:具体方法,有方法体: {…};抽象方法:没有方法体,
只要一个类中有一个抽象方法, 那么这个类就是:抽象类

实现类/工作类: 是一个可以被实例化的普通类,最终工作类: 允许实例化
工作类不一定是可以被实例化的普通类,也可是一个抽象类
抽象类也可以被继承, 抽象类也可以用在继承的上下文环境中
抽象类中允许有抽象成员, 但不是强制的,也可以没有。
必须将抽象类中的抽象方法实现

3、接口: 完全分离了”设计与实现”,关键字: interface,
使用与类相似的语法: 抽象方法, 常量, 构造方法(魔术方法的一种),
默认访问控制必须是public,接口允许多继承, 从而间接实现了PHP的多继承,implements (英婆慢吃),单接口, 工作类实现接口 接口常量的访问与类常量一样。多接口继承,一个接口同时继承多个接口,接口可以突破php类的继承限制, 允许多继承, 形成了多层级的接口。
用抽象类来部分实现一个接口,接口是实现多态的重要手段。
因为是面向接口编程, DB_PDO, DB_MySQLi都实现了同一接口,只要将对象参数的类型限定在接口上就可以实现多态,至于选用哪个类实例,由用户自行选择。

4、trait: php5.4+, 代码复用,与抽象类,接口一样不能实例化, 只能嵌入到宿主类中使用,它是一个特殊类: 1. 常规, 2. 静态, 3. 抽象, 不能用类常量。
类中使用trait , 用use 关键,将use tDemo理解为加载。
trait 功能1: 代码复用;trait2: 在继承上下文环境中, 具有优先级, 通过优先的设置, 降低单继承的影响。
trait中的同名方法将父类中同名方法覆盖了, 重写了;当前类的同名方法又会覆盖掉trait中同名方法;方法使用的优先级:子类 > trait > 父类。Trait组合的同名方法的命名冲突的解决方案:一是替换,用关键字insteadOf,二是别名,用关键字as,as 还可以修改trait成员的访问控制。

静态绑定与拦截器

1、实例演示后期静态绑定的原理与实现

  1. <?php
  2. // 后期静态绑定: 又叫延迟静态绑定
  3. // 后期静态绑定工作在: 静态继承上下文的环境中
  4. abstract class CreateInstance
  5. {
  6. }
  7. // 实现类1
  8. class User extends CreateInstance
  9. {
  10. public static function create() : self
  11. {
  12. return new self();
  13. }
  14. }
  15. // 实现类2
  16. class Product extends CreateInstance
  17. {
  18. public static function create() : self
  19. {
  20. return new self();
  21. }
  22. }
  23. // 客户端
  24. $user = User::create();
  25. var_dump($user);
  26. echo '<hr>';
  27. $product = new Product();
  28. var_dump($product);
  29. ----------------------
  30. // 将demo1.php中的代码进行简化, 将类实例的创建过程上移到它的基类中
  31. abstract class CreateInstance
  32. {
  33. public static function create()
  34. {
  35. // 出错原因: self 代表当前类,而当前是抽象类, 不能实例化, 不能用new 调用
  36. // 解决:将类的定义与类的调用完全分离开
  37. // 方案: 后期静态绑定, 使用关键字: static, 将self 替换掉
  38. // return new self();
  39. return new static();
  40. // 程序执行有二个过程: 编译, 执行
  41. // 类方法与有一个调用上下文, 定义上下文
  42. // self: self始终与定义该方法(属性)的类进行绑定
  43. // static: static总是自动与该方法(属性)调用类进行绑定
  44. // self: 与定义类绑定
  45. // static: 与调用类绑定
  46. }
  47. }
  48. // 实现类1
  49. class User extends CreateInstance
  50. {
  51. }
  52. // 实现类2
  53. class Product extends CreateInstance
  54. {
  55. }
  56. // 客户端
  57. $user = User::create();
  58. var_dump($user);
  59. echo '<hr>';
  60. $product = Product::create();
  61. var_dump($product);
  62. ---------------------
  63. // 后期静态绑定的应用场景: 动态绑定静态成员的调用上下文
  64. // 调用上下文者: 调用者
  65. class Base
  66. {
  67. // 静态方法: 允许重写
  68. public static function index()
  69. {
  70. return '当前调用方法: ' . __METHOD__;
  71. }
  72. public static function fetch()
  73. {
  74. // return self::index();
  75. return static::index();
  76. }
  77. }
  78. class User extends Base
  79. {
  80. // 重写父类中的静态方法: index()
  81. public static function index()
  82. {
  83. return '当前调用方法: ' . __METHOD__;
  84. }
  85. }
  86. // 客户端
  87. // echo Base::fetch();
  88. echo User::fetch();

2、魔术方法之构造方法与析构方法

  1. <?php
  2. // 魔术方法:
  3. // 语法: 类中有一些方法总是使用双下划线开头, 这就叫魔术方法
  4. // 调用者: 由系统根据一定的条件或用户行为, 自动调用/触发, 禁止用户主动调用
  5. // 构造方法: __construct(), 是类的实例化过程中被自动调用, new 的时候
  6. class Scorelist
  7. {
  8. private $course;
  9. private $score;
  10. // 在实例化的时候,自动完成类属性的初始化/赋值,如何做到呢?
  11. // 构造方法: 构造器, 功能就是生成一个新对象
  12. public function __construct($course, $score)
  13. {
  14. // 1. 生成一个新对象/类实例
  15. // $this是由php接管的对象, 不能用户设置, 下面是伪代码,辅助理解
  16. // $this = new self();
  17. // 2. 初始化这个新对象: 给这个新对象添加属性并赋值, 或自动执行某些操作方法
  18. $this->course = $course;
  19. $this->score = $score;
  20. $this->write();
  21. // 3. 返回这个刚刚创建并初始化的对象
  22. // 隐式返回当前新生的类实例, 不用显式返回, 自动完成
  23. // return $this;
  24. }
  25. // 类方法
  26. public function write()
  27. {
  28. echo "$this->course : $this->score 分<hr>";
  29. }
  30. // 析构方法, 没有参数, 销毁/删除一个对象的时候调用
  31. public function __destruct()
  32. {
  33. echo "Destroying " . $this->score ;
  34. }
  35. }
  36. // 客户端
  37. $Scorelist1 = new Scorelist('PHP网络编程', 90);
  38. //var_dump($Scorelist1);

3、实例演示属性重载/拦截器的所有方法

  1. <?php
  2. // 重载: php中的重载与其它语言不同(C++/Java), 称为"访问拦截器"更贴切
  3. // 拦截器: 属性拦截器, 方法拦截器
  4. // 使用场景: 当用户访问一个不存在的或无权限访问属性/方法时, 自动调用
  5. // 访问: 包括二个操作:就是读和写, 或者称为: 查询和设置
  6. // 属性拦截器: __set(), __get(), __isset(), __unset()
  7. class Product
  8. {
  9. private $name;
  10. private $price;
  11. private $taxRate = 0.06;
  12. public function __construct($name, $price)
  13. {
  14. $this->name = $name;
  15. $this->price = $price;
  16. }
  17. // 1. 属性查询拦截器
  18. public function __get($property)
  19. {
  20. // return $this->$property;
  21. // return $property === 'name' ? $this->name : '无权访问';
  22. // 拦截转发器
  23. // 1. 先事先约定一些方法专用于处理属性访问
  24. $method = 'get' . ucfirst($property);
  25. // 2. 转发访问请求
  26. return method_exists($this, $method) ? $this->$method() : null;
  27. }
  28. private function getName()
  29. {
  30. return mb_substr($this->name, 0, 5) . '...';
  31. }
  32. private function getPrice()
  33. {
  34. return $this->price + $this->price * $this->taxRate;
  35. }
  36. // 2. 属性设置拦截器
  37. public function __set($property, $value)
  38. {
  39. $method = 'set' . ucfirst($property);
  40. // 转发访问请求
  41. return method_exists($this, $method) ? $this->$method($value) : null;
  42. }
  43. private function setName($value)
  44. {
  45. $this->name = trim($value);
  46. }
  47. private function setPrice($value)
  48. {
  49. if ($value === null) unset($this->price);
  50. else $this->price = $value * (1-$this->taxRate);
  51. }
  52. // 3. 属性检测拦截器
  53. public function __isset($property)
  54. {
  55. return $property === 'name' ? isset($this->name) : false;
  56. }
  57. // 4. 属性销毁拦截器
  58. public function __unset($property)
  59. {
  60. if ($property === 'price') {
  61. $method = 'set' . ucfirst($property);
  62. // 转发访问请求
  63. if (method_exists($this, $method)) return $this->$method(null) ;
  64. }
  65. }
  66. }
  67. // 客户端
  68. $product = new Product('一坛绍兴女儿红',520);
  69. // 访问了一个无访问权限的类属性
  70. echo $product->name;
  71. echo $product->price;
  72. echo '<hr>';
  73. $product->name = '一辆8手的夏利';
  74. $product->price = 2000;
  75. echo $product->name;
  76. echo $product->price;
  77. echo '<hr>';
  78. echo isset($product->name) ? '存在' : '不存在';
  79. echo isset($product->price) ? '存在' : '不存在';
  80. unset($product->name);
  81. echo $product->name;
  82. unset($product->price);
  83. echo $product->price;

4、实例演示方法重载/拦截器的所有方法

  1. <?php
  2. // 方法拦截器, 其实比属性拦截器更有用
  3. // __call(), __callStatic()
  4. class User
  5. {
  6. // 方法拦截器
  7. public function __call($name, $arguments)
  8. {
  9. printf('方法名: %s , 参数: [%s]', $name, implode(', ', $arguments));
  10. }
  11. // 静态方法拦截器
  12. public static function __callStatic($name, $arguments)
  13. {
  14. printf('静态方法名: %s , 参数: [%s]', $name, implode(', ', $arguments));
  15. }
  16. }
  17. $user = new User();
  18. $user->demo(1,2,3,4);
  19. echo '<br>';
  20. User::demo(6,7,8,9);

课程学习小结

本次课程老师从浅显易懂的实例出发,详解介绍了静态绑定与拦截器相关内容,通过回看视频及讲义代码,加深了理解, 并通过修改实现部分代码,同时对前期学习的内容作了阶段性的回顾和整理,感觉非常有必要。

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