【问题标题】:When would you need to use late static binding?什么时候需要使用后期静态绑定?
【发布时间】:2010-09-10 08:58:07
【问题描述】:

在阅读了后期静态绑定 (LSB) 的 this description 之后,我非常清楚地看到了发生了什么。现在,在哪种情况下这可能最有用或最需要?

【问题讨论】:

    标签: php oop


    【解决方案1】:

    在以下场景中我需要 LSB:

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

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

    【讨论】:

    • 如果您可以添加 why 在这种情况下使用 static:: 会比简单地使用抽象的 非静态 更好或更容易i> 方法。对我来说,似乎save() 是你无论如何都没有理由从静态上下文调用的东西,但也许我错过了一些东西。
    【解决方案2】:

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

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

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

    使用 LSB:

    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:

    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 减少了这种重复,并允许更简洁、更直接的实现。

    【讨论】:

      【解决方案3】:

      在以下情况下很有用:

      1. 您的功能在类层次结构中有所不同,

      2. 功能在层次结构中具有相同的签名,并且

      3. (至关重要)您没有实例可以挂起该功能。

      如果只获得#1 和#2,你将使用普通的实例方法。所以 Alex 的问题(参见他对这个问题的回答)不需要 LSB。

      一个典型的例子是对象创建,其中子类以不同的方式创建自己,但使用相同的参数。显然你没有实例可以调用,所以创建方法(也称为工厂方法)必须是静态的。但是您希望它的行为因子类而异,因此普通的静态方法是不正确的。有关示例,请参见 Adam Franco 的回答。

      【讨论】:

        【解决方案4】:

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

        典型的例子是 Rails 的 ActiveRecord 类,如果你尝试在 PHP 中实现类似的东西,它看起来像这样:class User extends ActiveRecord 然后尝试调用User::find(1) 被调用的方法实际上是ActiveRecord::find()因为您没有在User 中重载find() - 但是没有后期静态绑定ActiveRecord 中的find() 方法无法知道它是从哪个类调用的(其中的self 将始终指向@ 987654330@),因此它无法为您获取您的用户对象。

        【讨论】:

          【解决方案5】:

          假设您在简化的对象关系映射器中有表示表(行实例)的类。 您将有一个类“用户”和一个类“公司”,它们的实例代表各自表的行。 User 和 Company 会从一些基本抽象类继承,比如“BaseObject”,它将有一些常用方法,如 save()、delete()、validate() 等......

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

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

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

          【讨论】:

            【解决方案6】:

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

            【讨论】:

              猜你喜欢
              • 2018-11-26
              • 1970-01-01
              • 2011-02-12
              • 2014-12-02
              • 1970-01-01
              • 2021-06-07
              • 1970-01-01
              • 2012-09-16
              • 1970-01-01
              相关资源
              最近更新 更多