关于php:何时需要使用后期静态绑定?

关于php:何时需要使用后期静态绑定?

When would you need to use late static binding?

阅读了有关后期静态绑定(LSB)的描述后,我可以很清楚地了解发生了什么。 现在,在哪种情况下可能最有用或最需要?


在以下情况下,我需要LSB:

  • 想象一下,您正在构建一个"邮件处理器"守护程序,该守护程序将从电子邮件服务器下载消息,对其进行分类,解析,保存,然后根据消息的类型执行某些操作。
  • 类层次结构:您有一个基本的Message类,带有子级" BouncedMessage"和" AcceptedMessage"。
  • 每种消息类型都有自己的方式将自己持久保存在磁盘上。例如,所有BouncedMessage类型的消息都尝试将自身保存为BouncedMessage-id.xml。另一方面,AcceptedMessage需要以不同的方式保存自身-作为AcceptedMessage-timestamp.xml。这里重要的是,用于确定文件名模式的逻辑对于不同的子类是不同的,但是对于子类中的所有项目都是共享的。这就是为什么采用静态方法才有意义。
  • 基本消息类具有抽象的静态方法(是,抽象和静态)"保存"。 BouncedMessage使用具体的静态方法实现此方法。然后,在实际检索消息的类中,可以调用" :: save()"。

如果您想了解更多有关该主题的信息:

  • http://www.qcodo.com/forums/topic.php/2356
  • http://community.livejournal.com/php/585907.html
  • http://bugs.php.net/bug.php?id=42681

我对后期静态绑定的一个主要需求是一组静态实例创建方法。

这个DateAndTime类是我从Smalltalk / Squeak移植到PHP的年代表库的一部分。使用静态实例创建方法可以创建具有各种参数类型的实例,同时将参数检查保留在静态方法中,从而使库的使用者无法获得不是完全有效的实例。

在这种情况下,后期静态绑定非常有用,因此这些静态实例创建方法的实现可以确定该调用最初针对的类。这是用法示例:

使用LSB:

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
class DateAndTime {

    public static function now() {
        $class = static::myClass();
        $obj = new $class;
        $obj->setSeconds(time());
        return $obj;
    }

    public static function yesterday() {
        $class = static::myClass();
        $obj = new $class;
        $obj->setSeconds(time() - 86400);
        return $obj;
    }

    protected static function myClass () {
        return 'DateAndTime';
    }
}

class Timestamp extends DateAndTime {

    protected static function myClass () {
        return 'Timestamp';
    }
}


// Usage:
$date = DateAndTime::now();
$timestamp = Timestamp::now();

$date2 = DateAndTime::yesterday();
$timestamp2 = Timestamp::yesterday();

在没有后期静态绑定的情况下,(例如在我当前的实现中)每个类都必须实现每个实例创建方法,如本示例所示:

没有LSB:

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
class DateAndTime {

    public static function now($class = 'DateAndTime') {
        $obj = new $class;
        $obj->setSeconds(time());
        return $obj;
    }

    public static function yesterday($class = 'DateAndTime') {
        $obj = new $class;
        $obj->setSeconds(time() - 86400);
        return $obj;
    }

}

class Timestamp extends DateAndTime {

    public static function now($class = 'Timestamp') {
        return self::now($class);
    }

     public static function yesterday($class = 'Timestamp') {
        return self::yesterday($class);
    }

}

随着实例创建方法和类层次结构数量的增加,方法的重复变成了难题。 LSB减少了这种重复,并允许更简洁,更直接的实现。


在以下情况下很有用:

  • 您所具有的功能随类的层次结构而变化,

  • 该功能在层次结构上具有相同的签名,并且

  • (非常重要)您没有实例可以停止使用该功能。

  • 如果仅获得#1和#2,则将使用普通实例方法。因此,Alex的问题(请参阅他对这个问题的回答)不需要LSB。

    典型的情况是对象创建,其中子类以不同的方式创建自己,但是使用相同的参数。显然,您没有要调用的实例,因此创建方法(也称为工厂方法)必须是静态的。但是您希望它的行为根据子类而有所不同,因此普通的静态方法是不合适的。有关示例,请参见亚当·佛朗哥的答案。


    假设您具有在简化的对象关系映射器中表示表(行实例)的类。
    您将拥有一个" User"类和一个" Company"类,它们的实例代表相应表的行。
    用户和公司将从某个基本抽象类继承,比如说" BaseObject",它将具有一些常见的方法,例如save(),delete(),validate()等。

    如果要存储有关验证和表定义的数据,则最好的位置是在每个派生类中的静态变量中-因为对于每个User实例,验证和表定义都是相同的。

    没有LSB,即使您通过User实例调用它,BaseObject中提到的validate()方法也无法引用User和Company中定义的静态变量。它将在Ba??seObject类中查找相同的静态变量,并且将引发错误。

    这是我对PHP 5.2.8的经验-LSB将在5.3中引入


    如果您需要在子类中尚未重载的方法中访问重载的静态属性/方法,则需要后期静态绑定。一个简单的例子:paste2.org

    经典示例是Rails的ActiveRecord类,如果您尝试在PHP中实现类似的内容,则如下所示:class User extends ActiveRecord,然后尝试调用User::find(1),实际上,调用的方法是ActiveRecord::find(),因为您没有在User中没有重载find()-但是没有后期静态绑定,在ActiveRecord中的find()方法无法知道从哪个类调用了它(其中的self总是指向ActiveRecord) ,因此它无法为您获取用户对象。


    我有一个带有处理某些格式的静态方法的类。我有另一个类,除了它处理格式的方式之外,它需要原始类的所有功能。


    推荐阅读