案例分析

先前的PHP项目中,看到类似于以下的一段代码:

<?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);

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

进行简单重构

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

<?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以后引入了延迟静态绑定。看下面这段代码:

<?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呢?可以这么干:

<?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关键字的另类用法。对于文章一开始的例子,可以这么改:

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

父类方法返回子类实例:PHP延迟静态绑定的更多相关文章

  1. httpservlet在创建实例对象时候默认调用有参数的init方法 destroy()方法 service方法, 父类的init方法给子类实例一个config对象

  2. Java学习笔记13---如何理解“子类重写父类方法时,返回值若为类类型,则必须与父类返回值类型相同或为其子类”

    子类重新实现父类的方法称重写:重写时可以修改访问权限修饰符和返回值,方法名和参数类型及个数都不可以修改:仅当返回值为类类型时,重写的方法才可以修改返回值类型,且必须是父类方法返回值的子类:要么就不修改 ...

  3. C++——子类调用父类方法

    原创声明:本文系博主原创文章,转载或引用请注明出处. 1. 如果类B是类A的子类,则在类B的成员方法中调用类A的方法时,可以直接以 A::method(paramlist); 来调用. 2. 若子类B ...

  4. PHP延迟静态绑定

    类可以自下往上调用父类方法,如果需要在父类中根据不同的子类,来调用子类的方法,那么就需要延迟静态绑定.延迟静态绑定用的是保留关键词static. 所谓延迟静态绑定,顾名思义,静态调用时::符号左侧的部 ...

  5. C# 继承实现父类方法、重写、重载

    继承是派生类(子类)去实现(重写<override>.重构<new>)基类(父类)的方法或属性.从而获取在派生类中要实现的功能. 子类调用父类构造方法,在父类中有个实现姓名和年 ...

  6. OC 继承子类对象调用方法机制 子类对象访问父类中的实例变量

    在继承中,子类对象如何调用到正确方法的机制 每一个Objective - C对象都有一个隐藏的指针指向类的代码,当向一个对象发送消息的时候,当前的对象会首先在当前类里去查找相应的方法,如果找到的话,直 ...

  7. 为何JAVA虚函数(虚方法)会造成父类可以"访问"子类的假象?

      首先,来看一个简单的JAVA类,Base. 1 public class Base { 2 String str = "Base string"; 3 protected vo ...

  8. [转python 父类可以调用子类的方法

    问题描述:我也是在读500 Line 里满的DBDB 的代码时发现的,python的父类可以调用子类的方法,这跟平常习惯的理解方式很不一样,所以就查了下原因,记录如下:   1.现象:最近使用到了So ...

  9. Python 在子类中调用父类方法详解(单继承、多层继承、多重继承)

    Python 在子类中调用父类方法详解(单继承.多层继承.多重继承)   by:授客 QQ:1033553122   测试环境: win7 64位 Python版本:Python 3.3.5 代码实践 ...

随机推荐

  1. python爬虫学习(1) —— 从urllib说起

    0. 前言 如果你从来没有接触过爬虫,刚开始的时候可能会有些许吃力 因为我不会从头到尾把所有知识点都说一遍,很多文章主要是记录我自己写的一些爬虫 所以建议先学习一下cuiqingcai大神的 Pyth ...

  2. 理解ThreadLocal(之一)

    ThreadLocal是什么 在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路.使用这个工具类可以很简洁地编 ...

  3. WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable

    http://dl.bintray.com/sequenceiq/sequenceiq-bin/ http://www.secdoctor.com/html/yyjs/31101.html

  4. 软件工程(FZU2015)赛季得分榜,第五回合

    目录 第一回合 第二回合 第三回合 第四回合 第五回合 第6回合 第7回合 第8回合 第9回合 第10回合 第11回合 积分规则 积分制: 作业为10分制,练习为3分制:alpha30分: 团队项目分 ...

  5. 响应式疑惑? CSS单位研究

    各种单位要搞清楚,自己试一试,实践出真知! 2.屏幕分辨率    响应式 哦,电脑的分辨率:1440x900表示水平有1440个像素点哦! 垂直有900个像素点. 而网页在浏览器中,所以宽度是电脑的分 ...

  6. sql特殊语句

    1.联表查询 select * from zongyi zongyiitem where zongyi.id=zongyiitem.id 2.联表删除 delete from zongyi ,zong ...

  7. 使用VelocityTracker来完成MotionEvent移动速率计算

    先看效果图 关键代码(此处记录单点): switch (event.getAction()){ case MotionEvent.ACTION_DOWN: if (veloctiy==null) { ...

  8. Deepin 15.3 编译同步SudaMod 2.0

    1.设置hosts,方便访问国外站点 $ wget https://raw.githubusercontent.com/racaljk/hosts/master/hosts -qO /tmp/host ...

  9. .NET 学习书籍推荐

    时间过得好快啊,转眼三月过了1周多了,今天把看书的目录记录下,方便初学者 最近由于项目需要研究下dotnet 技术,参考书籍如下: 1.深入理解C# 2.CLR via  C# 3.Framework ...

  10. openerp7 时区问题

    由于目前openerp 的时区,读取的是UTC 时间,而我国本地时间比UTC 快8小时,这个问题就导致:写入数据库的时候时间相差8小时,以及Openerp日志输出时间格式也相差8小时和 前端显示时间的 ...