父类方法返回子类实例:PHP延迟静态绑定

案例分析

先前的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
32
33
34
35
36
<?php
class DBHandler {
    public function get() {
    }
}
 
class MySQLHandler extends DBHandler {
    // 这里一个create
    public static function create() {
        echo "MySQL";
        return new self();
    }
 
    public function get() {
        echo "MySQL get()";
    }
}
 
class MemcachedHandler extends DBHandler {
    // 这里又有一个create
    public static function create() {
        echo "Memcached";
        return new self();
    }
 
    public function get() {
        echo "Memcached get";
    }
}
 
function get(DBHandler $handler) {
    $handler->get();
}
 
$dbHandler = MySQLHandler::create();
get($dbHandler);

可以看到,在 MySQLHandlerMemcachedHandler 类中,都有一个 create函数,除掉我的输出语句,发现它们一模一样,这就是代码冗余。是的,需要进行代码重构。

 

进行简单重构

对上面的代码进行重构,如下:

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
<?php
class DBHandler {
    public static function create() {
        echo "create";
        return new self();
    }
 
    public function get() {
    }
}
 
class MySQLHandler extends DBHandler {
    public function get() {
        echo "MySQL get()";
    }
}
 
class MemcachedHandler extends DBHandler {
    public function get() {
        echo "Memcached get";
    }
}
 
function get(DBHandler $handler) {
    $handler->get();
}
 
$dbHandler = MySQLHandler::create();
get($dbHandler);

将create函数移到DBHandler类中,看起来还不错,至少少了一坨那糟糕的代码。

 

貌似是错的

运行一下,却发现,并没有打印出我们期望的MySQL get()。什么情况?这说明,并没有调用MySQLHandler的get函数,但是代码明明调用了啊,这说明,new self()这句代码有问题。这有什么问题?这就需要说到今天总结的重点了————延迟静态绑定。

 

延迟静态绑定

在PHP5.3以后引入了延迟静态绑定。看下面这段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
class A {
    public static function who() {
        echo __CLASS__;
    }
 
    public static function test() {
        self::who();
    }
}
 
class B extends A {
    public static function who() {
        echo __CLASS__;
    }
}
 
B::test();

上面的代码输出了A,但是我希望它输出B,这就是问题的所在。这也是self和__CLASS__的限制。使用self::或者 __CLASS__对当前类的静态引用,取决于定义当前方法所在的类。所以,这就很好的解释了为什么上面的代码输出了A。但是,如果我们需要输出B呢?可以这么干:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
class A {
    public static function who() {
        echo __CLASS__;
    }
 
    public static function test() {
        static::who(); // 这里有变化,后期静态绑定从这里开始
    }
}
 
class B extends A {
    public static function who() {
        echo __CLASS__;
    }
}
 
B::test();

后期静态绑定本想通过引入一个新的关键字表示运行时最初调用的类来绕过限制。简单地说,这个关键字能够让你在上述例子中调用 test() 时引用的类是 B 而不是 A。最终决定不引入新的关键字,而是使用已经预留的 static 关键字。

这就是后期静态绑定的根本————static关键字的另类用法。对于文章一开始的例子,可以这么改:

1
return new static(); // 改变这里,后期静态绑定

这种使用后期静态绑定,在使用PHP实现设计模式的时候,你会感到很轻松的。

 

延伸阅读:

PHP的继承方法如何获取子类名?get_class() 和 get_called_class()

解决 PhpStorm 对 用单例模式实例化PHP类时,代码自动提示功能失效 的问题

 

 

参考:

http://my.oschina.net/liuhui1990/blog/38611?fromerr=1ZoOQLOl

http://www.jellythink.com/archives/956

http://blog.csdn.net/suiye/article/details/8729511

posted @   52php  阅读(1134)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示