面向对象3

面向对象3

1. ==PHP中的接口==

PHP中的接口指的不是我们平常听到的支付接口,物流API接口等,指的是纯粹的PHP中的语法;

==实现语法==:

1
2
3
4
#使用关键字interface来定义接口
interface 接口名{
接口成员
}

接口中的成员

接口中的成员包含两个部分:

  • 接口常量
  • 接口抽象方法

定义方式:

1
2
3
4
5
6
7
8
9
10
11
<?php

#使用关键字interface声明一个接口
interface InterfaceDemo{

//接口常量
const URL='www.home.com';

//接口抽象方法,注意,定义接口抽象方法不能指定abstract关键字
public function f1($v1, $v2=100);
}

接口的特点

  1. 接口需要使用implements关键字来实现;

    演示代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <?php

    interface inter1{

    }

    //使用implements关键字来实现接口
    class A implements inter1{

    }
  1. 接口可以被多实现;

    演示代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <?php

    interface inter1{

    }
    interface inter2{

    }
    interface inter3{

    }

    //使用implements关键字可以实现多个接口
    class A implements inter1, inter2, inter3{

    }
  1. 接口如果被普通类所实现,那么,接口中的所有接口抽象方法都要被全部实现;

    演示代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <?php

    interface inter1{
    public function f1($name);
    public function f2($age);
    }

    //使用implements关键字可以实现多个接口
    class A implements inter1{
    public function f1($name){
    echo $name;
    }
    }

    访问演示代码,输出效果:

    1530498245014

  2. 接口还可以被抽象类所实现,如果被抽象类实现,那么接口中的接口抽象方法可以不被实现;

    演示代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <?php

    interface inter1{
    public function f1($name);
    public function f2($age);
    }

    #使用了一个抽象类A去实现了接口inter1,接口被抽象类所实现后,接口中的抽象方法可以不被实现
    abstract class A implements inter1{

    abstract public function f3();

    public function f1($name){

    }
    }
  1. 接口中的接口抽象方法只能是public类型;

    演示代码:

    1
    2
    3
    4
    5
    6
    <?php

    interface inter1{
    public function f1($name);
    private function f2($age);//接口中的抽象方法只能是公有的
    }

    访问演示代码,输出效果:

    1530498698510

2. 静态延时绑定

引言:静态延时绑定是PHP5.3之后才出现的特性。

静态延时绑定需要用到的关键字是:==static==

static关键字的意思:==表示哪个类调用,就代表那个类==

==需求==:使用code/prepare目录下的StaticDemo.php文件中的代码,

  1. 在StaticDemo类的f1方法中打印出子类Demo中的$var2属性的值;
  2. 在Demo类的f2方法中打印父类$var1的值 和 本类中$var2的值;

==解答==

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?php


class StaticDemo{

public static $var1='var1';

public static function f1(){
var_dump( Demo::$var2 ); //访问Demo类中的$var2静态属性
echo '<br/>';
var_dump( static::$var2 );
}
}

class Demo extends StaticDemo{

public static $var2='var2';

public function f2(){
var_dump( parent::$var1 ); echo '<br/>';
var_dump( self::$var2 ); echo '<br/>';
echo 'static:<br/>';
var_dump( static::$var1 ); echo '<br/>';
var_dump( static::$var2 );
}
}

//调用f1方法
//StaticDemo::f1(); echo '<hr/>';//这次访问f1方法中输出的static::$var2将会报错,因为static表示哪个类调用就代表那个类,所以现在是StaticDemo类在调用f1方法,所以f1方法中的static就代表StaticDemo类,而StaticDemo中没有$var2静态属性,所以报错
//Demo::f1();//这次访问f1方法中输出的static::$var2可以正常输出,因为现在f1方法中的static代表Demo类,所以能够访问Demo自己类中的$var2静态属性;

//echo '<hr/>';echo '<hr/>';

//调用f2方法
$obj = new Demo;
$obj->f2();//这次访问f2方法中的static::$var1和static::$var2都能正常输出,因为此时$obj对象在调用f2方法,而$obj对象是由Demo类实例化出来的对象,所以f2方法里的static关键字就代表Demo类,所以能够正常访问到父类的$var1和自己内部的$var2属性;

==小结==

  1. static关键字就表示:哪个类调用就代表那个类;

3. ==PHP中的重载==(overload)

概念:PHP中的重载,指的是当访问一个==不可访问==的成员时,如何来进行处理,这个==处理过程==就是PHP中的重载。

重载在PHP中分成两个部分:

  • 默认的重载 当访问一个不可访问的成员时,PHP默认的处理过程
  • 自定义重载 当访问一个不可访问的成员时,开发者自定义的处理过程

通过观察相关概念,不难发现,在重载这个部分,最关键是要弄明白什么情况属于访问到一个==不可访问==的成员。

在重载中,所谓的访问到一个不可访问的成员,指的是:

  1. 访问到一个不存在的成员(方法 和 非静态属性)
  2. 访问到一个存在却不能访问的成员(方法 和 非静态属性)

PHP中默认的重载

==需求==:使用code/prepare目录下的OverloadDemo.class.php文件中的代码,在类的外部创建一个基于OverloadDemo类的对象$obj,

  1. 使用对象$obj增加一个非静态属性$name=zhangsan,然后打印对象$obj,查看效果;
  2. 使用对象$obj获取$var1属性的值,并尝试打印,查看效果;
  3. 使用unset尝试删除对象$obj中的$var1属性,查看效果;
  4. 使用isset判断$obj中的$var1是否存在,查看效果;
  5. 在类的外部尝试调用f1和f2方法,查看效果;

==解答==

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?php

class OverloadDemo{

protected $var1='var1';

protected function f1($v1){ //protected类型 的 非静态方法
echo $v1;
}

private static function f2($v1){ //private类型 的 静态方法
echo $v1;
}
}


$obj = new OverloadDemo;
var_dump( $obj ); echo '<br/>';

#当访问到一个 不存在的 非静态属性时,PHP默认往对象中添加一个新的非静态属性
$obj->name = 'zhangsan';
var_dump( $obj ); echo '<hr/>';

#当访问(获取)到一个 存在但是不能访问的 非静态属性时,PHP默认的处理方式是拒绝访问直接报错
//var_dump( $obj->var1 ); echo '<hr/>';

#当使用unset访问(删除)到一个 存在但是不能访问的 非静态属性时,PHP默认的处理方式是拒绝访问直接报错
//unset($obj->var1);

#当使用isset访问(判断是否存在)到一个 存在但是不能访问的 非静态属性时,PHP直接返回false
var_dump( isset($obj->var1) );
echo '<hr/>';echo '<hr/>';

#
//$obj->f1(100);//当访问到一个 存在但是不能访问的 非静态方法时,PHP默认的处理方式是拒绝访问直接报错
OverloadDemo::f2(200);//当访问到一个 存在但是不能访问的 静态方法时,PHP默认的处理方式是拒绝访问直接报错

==小结==

  1. 当访问一个不存在的非静态成员属性时,PHP默认的处理方式是直接添加一个新的属性;
  2. 当访问到一个存在但不能访问的成员时,除了isset操作是返回false之外,其他的都是拒绝访问直接报错;

PHP中自定义重载

PHP中自定义重载又被划分为两个类别,分别是:

  • (非静态)属性的重载
  • 方法的重载

属性的重载

(非静态)属性的重载,涉及的魔术方法:

__get(属性名) 当==获取==一个不可访问的(非静态)成员属性值时,进行自定义的处理

__set(属性名, 属性值) 当==设置==一个不可访问的(非静态)成员属性值时,进行自定义的处理

__unset(属性名) 当==删除==一个不可访问的(非静态)成员属性时,进行自定义的处理

__isset(属性名) 当==判断==一个不可访问的(非静态)==成员属性是否存在==时,进行自定义的处理

__get魔术方法

==需求==:使用code/prepare目录下的__getDemo.php文件中的代码,在类的外部创建一个基于__getDemo类的对象$obj,

  1. 构建__get魔术方法,在方法中实现判断,当属性名为name时返回该属性的实际值;当属性名为var1时返回提示信息’就不给你看!’;

==解答==:构建名为code10.php的程序文件,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php

class __getDemo{

protected $name='郭嘉';

#魔术方法__get
public function __get($varName){

if( $varName=='name' ){
// $this->name
return $this->$varName;//直接将name属性的值返回出去
}elseif( $varName=='var1' ){
return '就不给你看!';
}
}
}

$obj = new __getDemo;
// $obj->name=$obj->__get('name');
var_dump( $obj->name ); //获取一个不可访问的非静态属性值时,
// $obj->var1=$obj->__get('var1');
var_dump( $obj->var1 ); //获取一个不可访问的非静态属性值时,
// $obj->var100=$obj->__get('var100');
var_dump( $obj->var100 );

访问code10.php,效果为:

1530504072822

==小结==

  1. __get魔术方法PHP不负责定义,只负责调用;
  2. 当获取一个不可访问的非静态属性值时,则会触发PHP自动调用执行__get魔术方法,并且会将这个不可访问的非静态属性的名字传递给__get魔术方法;
__set魔术方法

==需求==:使用code/prepare目录下的__setDemo.php文件中的代码,在类的外部创建一个基于__setDemo类的对象$obj,

  1. 构建__set魔术方法,在方法中实现判断,当属性名为name时则允许进行设置;当属性名为age时则固定设置为100;其他情况则输出提示信息’不允许添加额外的属性哦!’;

==解答==:构建code12.php程序,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?php

class __setDemo{

protected $name='王翦';
private $age=18;

#创建一个名为__set的魔术方法,需要两个参数,第一个参数不可访问的非静态属性名;第二个参数为不可访问的非静态属性新值;
public function __set($varName, $varValue){

if( $varName=='name' ){
//$this->name = $varValue;
$this->$varName = $varValue;
}elseif( $varName=='age' ){
$this->age = 100;
}else{
echo '不允许添加额外的属性哦!';
}
}
}

$obj = new __setDemo;
$obj->height = 1.78;
echo '<br/>';
var_dump( $obj );
echo '<hr/>';
$obj->name = '大战神';
var_dump( $obj );
echo '<hr/>';
$obj->age = 2000;
var_dump( $obj );

访问code12.php,效果如下:

1530515476054

==小结==

  1. PHP不负责定义__set魔术方法,只负责调用这个魔术方法;
  2. 调用这个魔术方法的时机是:当设置了一个不可访问的非静态属性的值时,则会触发PHP自动调用执行__set魔术方法,这个魔术方法是需要两个参数的,具体参数的说明请参看上面的代码注释
__unset魔术方法

==需求==:使用code/prepare目录下的__unsetDemo.php文件中的代码,在类的外部创建一个基于__unsetDemo类的对象$obj,

  1. 构建__unset魔术方法,在方法中实现判断,当属性名为age时则允许删除,返回true;其他情况则输出提示信息’再删!给你小拳拳哦!’,返回false;

==解答==:构建code13.php,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?php

class __unsetDemo{

protected $name='梦奇';
private $age=19;

#构建__unset魔术方法,需要一个必填参数,这个参数表示删除的不可访问的非静态属性的属性名
public function __unset($varName){

if( $varName=='age' ){
unset($this->age);
return true;//给返回值无效,所以不用去指定返回值
}else{
echo '再删!给你小拳拳哦!';
return false;//给返回值无效,所以不用去指定返回值
}
}
}

$obj = new __unsetDemo;
var_dump( $obj ); echo '<br/>';
unset($obj->age);echo '<hr/>';
var_dump( $obj ); echo '<br/>';
unset($obj->name);echo '<hr/>';
var_dump( $obj );

访问code13.php,效果为:

1530516511557

==小结==

  1. PHP不负责构建这个魔术方法,只负责调用这个魔术方法;
  2. 调用该魔术方法的时机是:当删除一个不可访问的非静态成员属性时,则会触发PHP自动调用__unset魔术方法自动执行,这个魔术方法有一个必填参数,参数说明请参看上面案例中的代码注释。
__isset魔术方法

==需求==:使用code/prepare目录下的__issetDemo.php文件中的代码,在类的外部创建一个基于__issetDemo类的对象$obj,

  1. 构建__isset魔术方法,在方法中实现判断,当属性名为name时则返回true;其他情况则返回false;

==解答==:构建code15.php,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php

class __unsetDemo{

protected $name='二师兄';
private $age=19;

#构建__isset魔术方法,需要指定一个必填参,参数表示判断的不可访问的非静态属性的属性名
public function __isset($varName){
if( $varName=='name' ){
return true;
}else{
return false;
}
}
}

$obj = new __unsetDemo;
var_dump( isset($obj->name) ); echo '<hr/>';
var_dump( isset($obj->age) );

访问code15.php,效果为:

1530517042589

==小结==

  1. PHP不负责定义该魔术方法,只负责调用该魔术方法;
  2. 调用__isset魔术方法的时机是:当判断一个不可访问的非静态属性是否存在时,则会触发PHP自动调用__isset魔术方法执行一次,需要传递一个必填参数,参数说明请参看上面代码的注释。

方法的重载

涉及的魔术方法:

__call(方法名, 传递给方法的参数) 当访问一个不可访问的(非静态)成员方法时,进行自定义处理

__callstatic(方法名, 传递给方法的参数) 当访问一个不可访问的(静态)成员方法时,进行自定义处理

__call魔术方法

==需求==:使用code/prepare目录下的__callDemo.php文件中的代码,在类的外部创建一个基于__callDemo类的对象$obj,

  1. 构建__call非静态魔术方法,在方法中实现判断,当方法名为f1时则允许访问;其他情况则提示”没有这个方法!”,返回false;

==解答==:构建code16.php,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?php

class __callDemo{

private function f1($v1, $v2){
echo '$v1:' . $v1 . ';$v2:' . $v2;
}

#构建一个__call的魔术方法,需要两个必填参,第一个参数指定的是方法名,第二个参数指定的是传递给方法的所有参数形成的数组集合
public function __call($methodName, $methodParams){
//echo '方法名:';
//var_dump( $methodName ); echo '<br/>';
//echo '参数数据:';
//var_dump( $methodParams );

if( $methodName=='f1' ){
//$this->f1($methodParams[0], $methodParams[1]);
$this->$methodName($methodParams[0], $methodParams[1]);
}else{
echo '没有这个方法!';
return false;
}
}
}

$obj = new __callDemo;
$obj->f1(100, 200); echo '<hr/>';

$re = $obj->f2(100);
var_dump( $re );

访问code16.php,效果为:

1530518364734

==小结==

  1. PHP不负责定义__call魔术方法,只负责调用这个魔术方法;
  2. 调用该魔术方法的时机是:当访问到一个不可访问的非静态方法时,将会触发PHP自动调用该魔术方法,这个魔术方法本身也是一个非静态方法,参数请参看上面代码中的注释;
__callstatic魔术方法

==需求==:使用code/prepare目录下的__callstaticDemo.php文件中的代码,在类的外部创建一个基于__callstaticDemo类的对象$obj,

  1. 构建__callstatic静态魔术方法,在方法中实现判断,当方法名为f1时则允许访问;其他情况则提示”没有这个方法!”,返回false;

==解答==:构建code17.php,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?php

class __callstaticDemo{

private static function f1($v1){
echo $v1;
}

private static function f2(){
echo 'f2';
}
#构建(静态)魔术方法__callstatic,需要两个必填参,第一个参数指定的是方法名,第二个参数指定的是传递给方法的所有参数形成的数组集合
public static function __callstatic($methodName, $methodParams){

if( $methodName=='f1' ){
self::f1($methodParams[0]);
}else{
echo '没有这个方法!';
return false;
}
}
}

__callstaticDemo::f1(100);echo '<hr/>';
$re = __callstaticDemo::f2();
var_dump( $re );

访问code17.php,效果如下:

1530518988382

==小结==

  1. PHP不负责定义__callstatic只负责调用这个魔术方法;
  2. 调用的时机:当访问到一个不可访问的静态方法时,将会触发PHP自动调用该魔术方法,这个魔术方法本身也是一个静态方法,参数请参看上面代码中的注释;

4. 面向对象的三大特性

1) ==封装==的特性, 将特定的功能打包成一个类;

2) ==继承==的特性, 指的就是extends语法特性;

3) ==多态==的特性, 指的就是重写的特性,一种方法可以有多种实现方式;

5. ==两种常见的设计思想==(模式)

引言:在PHP中,存在两种常见的设计模式,一个是单例模式,另一个是工厂模式,这两种设计模式,并不是固定的PHP语法,而是==广大的开发工作者在长期的开发工作中总结出来的开发经验==。

单例模式

引言:在程序中,我们可以通过使用new关键字来达到实例化对象的目的,并且只要new一次,都将会得到一个全新的对象(空间);而实际上,在项目中,如果我们需要使用对象来调用类中的成员进行操作的话,基于这个类创建一个对象就足以满足所有的调用操作需求,没有必要额外再创建更多的对象。因为,创建更多的对象,也就意味着额外会有更多的损耗。

广大的开发工作者也注意到这个问题,为了能够减少损耗,提升程序运行的效率,通过逐步总结开发经验,得出了单例这种设计思想模式。

单例模式实现的目标效果是: 无论我们实例化多少次,都只能得到唯一的一个对象(空间)。

==TIPS==:单例模式也被称为:三私一公

实现步骤

  1. 第一步目标,先实现禁止创建对象,让我们在程序中无法通过new来得到对象

    1530521448563

  2. 第二步,想办法,开放一个对象,创建一个私有的静态属性$_obj,再创建一个名为single的静态方法,方法中的代码内容如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    <?php

    class A{

    private static $_obj;//保存本类唯一对象的静态属性

    private function __construct(){ //将构造方法私有化,使得基于类A在类的外部没有办法通过new的方式创建对象

    }

    public static function single(){

    if( !is_object(self::$_obj) ){//如果不是一个对象
    //创建一个全新的对象
    self::$_obj = new self;//创建一个基于本类的对象
    }
    //返回新创建的对象
    return self::$_obj;
    }
    }

    $a1 = A::single();
    var_dump( $a1 ); echo '<hr/>';
    $a2 = A::single();
    var_dump( $a2 ); echo '<hr/>';
    $a3 = A::single();
    var_dump( $a3 ); echo '<hr/>';

    访问该程序的效果为:

    1530522407724

  3. 我们还需要将clone魔术方法禁掉,代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    <?php

    class A{

    private static $_obj;//保存本类唯一对象的静态属性

    private function __construct(){ //将构造方法私有化,使得基于类A在类的外部没有办法通过new的方式创建对象

    }

    public static function single(){

    if( !is_object(self::$_obj) ){//如果不是一个对象
    //创建一个全新的对象
    self::$_obj = new self;//创建一个基于本类的对象
    }
    //返回新创建的对象
    return self::$_obj;
    }

    private function __clone(){ //禁掉clone魔术方法,使得在类的外部无法通过clone来得到全新的对象

    }
    }

    $a1 = A::single();
    var_dump( $a1 ); echo '<hr/>';
    $a2 = A::single();
    var_dump( $a2 ); echo '<hr/>';
    $a3 = A::single();
    var_dump( $a3 ); echo '<hr/>';
    $a4 = clone $a3;
    var_dump( $a4 );

    访问程序代码,效果为:

    1530522614001

工厂模式

引言:在程序中,如果使用面向对象的开发方式进行开发,那么,基于类创建对象这种操作一定是非常频繁的。

广大的开发工作者为了在程序中能够对创建对象这种频繁的操作能够有集中的管理,故而从大量的开发实践中总结出工厂模式设计思想。

工厂模式实现的目标效果:构建一个类,专门用于生产对象。

实现步骤

构建的程序代码为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php

//构建一个工厂类
class Factory{

//构建一个专门生产对象的静态方法,需要一个必填参,这个必填参是以后要实例化类的类名
public static function produce($className){

if( $className=='A' ){//如果是A类
include './A.class.php';//则引入A类所在的类文件
$obj = new A;//实例化一个A类的对象
}

return $obj;//将对象返回
}
}


$a1 = Factory::produce('A');
var_dump( $a1 );

==小结==

  1. 工厂模式指的就是构建一个专门生产对象的类;

7. 全天总结

  1. 什么是重载?

    重载指的就是当访问到一个不可访问的成员(非静态属性和方法)时,如何去处理,这个处理过程就是重载;

  2. __set、__get、__unset和__call魔术方法的操作

    __set魔术方法是当设置一个不可访问的非静态属性的值时,PHP将会自动调用__set魔术方法执行一次;

    __get魔术方法是当获取一个不可访问的非静态属性的值时,PHP将会自动调用__get魔术方法执行一次;

    __unset魔术方法是当删除一个不可访问的非静态属性时,PHP将会自动调用__unset魔术方法执行一次;

    __call魔术方法是当调用一个不可访问的非静态方法时,PHP将会自动调用__call魔术方法执行一次;

  3. 面向对象的三大特性:

    封装 就是将功能打包成一个类

    继承 指的就是extends的特性

    多态 指的就是重写,同一种方法有多种实现方式

  4. 能够实现单例模式

    单例模式的目标:无论在类的外部实例化多少次,只能得到唯一的一个对象。

    实现单例模式的方式叫:三私一公

    我们的实现方式:1)禁掉所有在类的外部创建的对象;2)在类的内部开放一个;3)禁掉clone魔术方法