面向对象

早期编程由于受电脑硬件限制,程序都是追求效率,而忽略可理解性,扩充性,随着硬件技术的发展,编程越来越重视多人开发,程序员越来越重视程序的可靠性,可扩展性,可维护性,所以刺激了程序语言的发展。

面向过程

  • 程序员设计好程序代码流程图,辅助程序设计。优点:用什么功能就编写什么函数。缺点:数据管理上比较混乱,主要集中在函数层面上,面向对象把属性和方法进行封装,更好的可重用性和可扩展性。

面向对象

  • 万物皆对象,将构成问题的事务分解到各个对象上,建立对象的目的不是为了完成一个工作,而是为了描述某个事物在解决问题中的行为,更符合人的思维习惯,代码重用性高,可扩展性好。

    面向对象和面向过程的核心区别是如何分配职责。

类和对象

面向对象是由一系列具有属性和方法的对象构成,对象之间相互交互,从而实现业务需求。

属性

  • 在类中定义的变量,即为成员属性,用于描述对象静态特性的数据。如人的性命,性别,首字母小写等。

方法

  • 函数定义在类中即为成员方法,用于描述对象动态特性的操作行为,方法名不区分大小写,不可重名,首字母小写。

对象生命周期

  • 创建后,生命周期开始,当程序结束后或程序员清除对象后即销毁,PHP 会自动销毁对象。

类是一种抽象的概念,是具有相同语义定义对象的集合(具有相同属性和方法的集体),使用具体的类是不可行的,只能实例化。拿汽车举例,汽车的设计图纸就是类,汽车是对象。设计中重点是类的创建。

类名书写规范:

  • 类名首字母大写
  • 一个类定义在文件中

$this

对象中使用 $this 指针可以访问属性或方法。

  1. class Code
  2. {
  3. protected $len = 5;
  4. public function make()
  5. {
  6. return $this->len . $this->show();
  7. }
  8. public function show()
  9. {
  10. return ' : is show';
  11. }
  12. }
  13. echo (new Code)->make();

继承

通过使用 extends 可以继承父类的属性与方法,在 PHP 中继承是单一的。

  1. class Notify
  2. {
  3. public function message()
  4. {
  5. return 'notify message';
  6. }
  7. }
  8. class User extends Notify
  9. { }
  10. echo (new User)->message();

父类调用

子类可以使用 parent 关键字调用父类方法。

  1. ...
  2. public function message()
  3. {
  4. return parent::message();
  5. }
  6. ...

方法重写

子类可以重写父类的方法,除非父类的方法使用 final 修饰。

  1. class Notify
  2. {
  3. public function message()
  4. {
  5. return 'notify message';
  6. }
  7. }
  8. class User extends Notify
  9. {
  10. public function message()
  11. {
  12. return 'user notify';
  13. }
  14. }
  15. echo (new User)->message();

禁止重写

使用 final 声明的方法,将禁止在子类中重写父类方法。

  1. public final function message()
  2. {
  3. return 'notify message';
  4. }

封装

public 公有

  • 在类的内部与外部或子类都可以访问,是最开放的权限。

private 私有

  • 定义类的私有属性和私有方法,在类的内部可以访问,在类的外部或子类都不可以访问。

protected 受保护

  • 定义类受保护的属性和受保护的方法,在类的内部或子类可以访问,类的外部不可以访问。

模块设计

  • 强内聚(功能尽量在类的内部完成),弱耦合(开放尽量少的方法给外部调用)。例:公司销售接项目,具体工作交给公司内部程序员,设计人员,服务器管理人员协同完成。

trait

使用 trait 机制可以变相的使用多重继承。

  1. <?php
  2. class Alipay{
  3. use Pay;
  4. }
  5. class WePay{
  6. use Pay;
  7. }
  8. trait Pay{
  9. public function sn(){
  10. return 'ZBC';
  11. }
  12. }
  13. echo (new WePay)->sn();

如果本类与 trait 中存在同名的属性和方法时,将使用本类中的属性和方法。

  1. ...
  2. class WePay
  3. {
  4. use Pay;
  5. public function sn()
  6. {
  7. return __METHOD__;
  8. }
  9. }
  10. trait Pay
  11. {
  12. public function sn()
  13. {
  14. return 'ABCDEF';
  15. }
  16. }
  17. ...

多个trait

可以使用多个 trait 用逗号连接。

  1. ...
  2. use Pay,Site;
  3. ...

解决冲突

  1. class WePay
  2. {
  3. use Pay, Email {
  4. Pay::notify insteadof Email;
  5. Email::notify as EmailNotify;
  6. }
  7. trait Pay
  8. {
  9. public function notify()
  10. {
  11. return __METHOD__;
  12. }
  13. }
  14. trait Email
  15. {
  16. public function notify()
  17. {
  18. return __METHOD__;
  19. }
  20. }
  21. echo (new WePay)->notify();

Pay::notify insteadof Emaill 表示使用 Pay::notify 方法代替 Email::notify 方法。

Email::notify as EmailNotifyEmaill::notify 别名为 EmailNotify

访问控制

可以为继承的 trait 方法重新定义访问控制。

  1. class WePay
  2. {
  3. use Pay, Email {
  4. Pay::notify insteadof Email;
  5. Email::notify as protected EmailNotify;
  6. ...
  7. }

多重trait

可以通过多个 trait 组合来使用。

  1. trait Notify
  2. {
  3. public function response()
  4. {
  5. return 'notify response';
  6. }
  7. }
  8. trait Pay
  9. {
  10. use Notify;
  11. }
  12. class User
  13. {
  14. use Pay;
  15. }
  16. echo (new User)->response();

抽象方法

  1. trait Notify
  2. {
  3. public function response()
  4. {
  5. return 'notify response' . $this->sn();
  6. }
  7. abstract protected function sn();
  8. }
  9. trait Pay
  10. {
  11. use Notify;
  12. }
  13. class User
  14. {
  15. use Pay;
  16. protected function sn()
  17. {
  18. return 'SN999';
  19. }
  20. }
  21. echo (new User)->response();

静态方法

trait 中可以使用静态方法、抽象方法、静态属性。

  1. ...
  2. trait Pay
  3. {
  4. public function sn()
  5. {
  6. return 'ABCDEF';
  7. }
  8. public static function notify()
  9. {
  10. return __METHOD__;
  11. }
  12. }
  13. class WePay
  14. {
  15. use Pay;
  16. ...
  17. }
  18. echo WePay::notify();

static

static:

  • 声明类属性或方法为静态,就可以不实例化类而直接访问。静态属性不能通过一个类已实例化的对象来访问(但静态方法可以)。

static 变量:

  • 通过 static 声明的成员属性为静态属性或叫类属性,是该类的公共变量,在第一次使用时即生成,对于该类的所有对象只有一份,是属于类的,不是属于对象的。static 变量是属于类而不属于对象,可以在任何地方通过类来访问,是类的全局变量,类创建时即存入内存。对多个对象来说,静态数据成员只存储一处,可以节省内存。只要对静态数据成员的值更新一次,保证所有对象存取更新后的相同的值,

static 方法:

  • 用 static 声明的方法为静态方法或叫类方法,执行该方法时不会将对象引用传给函数,所以我们不能访问非静态成员,只能访问静态方法或静态变量。只能使用关于类的方式如:self、static、parent等。使用时不用生成对象即可执行。

类常量

使用 const 来定义类常量,常量使用 self:: 来调用。

  1. class Model implements ArrayAccess, Iterator
  2. {
  3. use ArrayIterator, Relation, Validate, Auto, Filter;
  4. //----------自动验证----------
  5. //有字段时验证
  6. const EXIST_VALIDATE = 1;
  7. //值不为空时验证
  8. const NOT_EMPTY_VALIDATE = 2;
  9. ...
  10. }

$this、self::、parent::

$this

  • 是当前对象的引用,一般出现在方法里,用于获取类的成员属性,或执行类的成员方法。

self::

  • 对本类的引用,用于获取当前类的静态成员属性或静态成员方法 self::run()

parent::

  • 对父类的引用,调用父类的方法或属性。

魔术方法

构造方法 & 析构方法

构造方法 __construct()

  • 在创建对象时自动执行,没有返回值,用于执行类的一些初始化工作,如对象属性的初始化工作,构造方法为 __construct()
  • 可以在构造方法中传递参数,用于定义属性,在父类和子类都定义构造方法,执行子类的构造方法。

析构方法 __destruct()

  • 当所有对象的引用被销毁时执行。

get 与 set

读取不可访问或不存在的属性时,get() 会被调用,同理设置不可访问或不存在的属性时会执行 set() 方法。

  1. <?php
  2. abstract class Query
  3. {
  4. abstract protected function record(array $data);
  5. public function select()
  6. {
  7. return $this->record(['name' => 'Laot', 'age' => 20]);
  8. }
  9. }
  10. class Model extends Query
  11. {
  12. protected $field = [
  13. 'name'
  14. ];
  15. public function all(){
  16. $this->select();
  17. return $this->field;
  18. }
  19. protected function record(array $data)
  20. {
  21. $this->field = $data;
  22. }
  23. public function __get($name)
  24. {
  25. return $this->field[$name] ?? null;
  26. }
  27. public function __set($name, $value)
  28. {
  29. $this->field[$name] = $value;
  30. }
  31. }
  32. $user = new Model;
  33. $user->all();
  34. echo $user->name;
  35. $user->name = 'admin';
  36. echo $user->name;

isset() 与 unset()

当使用 isset() 函数或者 empty() 函数判断属性是否存在或者是否为空的时候会自动触发。

当使用 unset() 函数清除属性时,如果存在 __unset() 方法将会执行。

  1. ...
  2. public function __unset($name)
  3. {
  4. if (!isset($this->field[$name]) || in_array($name, $this->deny)) {
  5. throw new Exception('禁止操作');
  6. }
  7. }
  8. public function __isset($name)
  9. {
  10. return isset($this->field[$name]);
  11. }
  12. ...

抽象类 & 抽象方法

具有抽象方法的类为抽象类,抽象方法即为没有内容的空方法,要求子类进行完善内容,抽象类不能实例化,只能继承,通过 extends 来实现,抽象类中也可以定义普通方法。

父类方法执行方式不确定,但子类还都有这个方法。

  • 例1:如交通工具类:定义抽象方法控制交通工具运行方式,这样每个交通工具如飞机,汽车都要重写父类方法。如果在父类工具类定义该方法(比如在地上走)没有任何意义,因为每个交通工具都要重写(飞机要重写方法,船要重写方法),所以针对子类方法的不确定性,我们需要抽象方法,实现多态。
  • 例2:定义动物类,每个动物都有叫声方法,但是表现不同,所以要定义为抽象类,让每种动物类去实现功能。
  • 当父类为抽象类时,子类必须重写父类的抽象方法。
  • 抽象类例不一定非要写抽象方法,但有抽象方法的类必须定义为抽象类。
  • 抽象类必须继承使用。
  • 抽象方法不能由主体即{} ``` <?php

abstract class AbstractClass { // 强制要求子类定义这些方法 abstract protected function getValue();

  1. abstract protected function prefixValue($prefix);

// 普通方法(非抽象方法) public function printOut() { print $this->getValue();

  1. }

}

  1. <a name="ooQEQ"></a>
  2. ## 接口
  3. 接口是一组声明成员方法的集合,包含空的成员方法和常量,空的方法要求继承类去具体实现。成员方法为 public,属性为常量。
  4. - 例如:现实中的迪南 USB 或 PCI 插槽,插线板等都有接口。<br />
  5. 继承是一级一级层次式,如果某一层出现问题,整个继承就会出现意外。而接口只影响实现接口的类,接口可以在破坏原来的继承基础上对类扩展。接口可以实现多继承。
  6. - 例:电脑 USB 接口,规定各个厂商必须构造合适的接口方法,比如手机,数码相机,网银U盾。要让各个厂商写自己的方法如U盾插到USB上他会自动安装网银驱动,弹出网页,手机装上后可以打开手机里的内容,同时可以充电。
  7. 抽象类及普通类都可以实现接口,通过关键字 implements
  8. 接口与抽象类的区别:
  9. 1. 接口只能用 implements 实现,抽象类用 extends 继承实现。
  10. 1. 接口没有数据成员,可以定义常量,抽象类可以有。
  11. 1. 接口没有构造函数,抽象类可以定义构造函数。
  12. 1. 接口方法都是 public 抽象类方法可以用 protected、private、public来修饰
  13. 1. 一个类可以实现多个接口,但只能继承一个抽象类
  14. 1. 接口中不可以有成员方法,抽象类可以有成员方法。

<?php interface DbInterface { public function connectDb(); //获得连接 参数为表名 public function close(); //关闭数据库 public function exe($sql); //发送没有返回值的sql public function query($sql); //有返回值的sql }

class Db implements DbInterface { public function connectDb(){ } public function exe($sql){ echo $sql; } public function query($sql){ } public function close(){ } }

$db = new Db; $db->exe(‘test’); ``` 个人理解:

  • 继承抽象类是对父类方法不确定性的实现,比如父亲给儿子留了一套房子,但具体怎么装修儿子自己实现。
  • 而接口则像是规定好了哪间是厨房,哪间是客厅,具体厨房和客厅中放些什么自己决定。

实现一个功能的方法可能不止一种,最终还是要尽可能的保证代码的可维护性、可扩展性,选择最优的解决方式。