代码之家  ›  专栏  ›  技术社区  ›  Boldewyn

PHP中通过抽象关键字的静态类?

  •  21
  • Boldewyn  · 技术社区  · 14 年前

    根据 PHP manual

    abstract class Example {}
    

    class Registry {}
    // and later:
    echo Registry::$someValue;
    

    简单地将类声明为抽象类会被认为是好的风格吗?如果不是,那么与抽象类相比,将构造函数隐藏为受保护的方法有哪些优点?

    在我看来,这可能有点滥用功能,因为手册中提到的抽象类更像是具有实例化可能性的后期类的蓝图。

    更新: 首先,感谢所有的答案!但许多答案听起来很相似:“不能实例化抽象类,但对于注册表,为什么不使用单例模式呢?”

    优势 使用单例模式(又称隐藏) __construct() )而不是仅仅宣布 abstract 不用担心吗(比如说,开发者之间有一个很强的内涵 摘要 习惯于 大概吧。)

    10 回复  |  直到 14 年前
        1
  •  19
  •   Pascal MARTIN    14 年前

    abstract 我得说。


    现在,为什么要使用单例,而不仅仅是静态方法?我想,至少有几个理由是正确的:

    • 使用单例意味着使用类的实例;更容易将非单例类转换为单例类:只需 __构造 __克隆 私人,再加上一些 getInstance
    • 使用单例还意味着您可以访问所有可用于普通实例的内容: $this ,属性。。。
    • (不确定,但可能有其重要性) :使用PHP<5.3,静态方法/数据的可能性较小:
    • Late Static Binding 只添加了PHP5.3;当使用静态方法/类时,没有它通常会使它变得更加困难;尤其是在使用继承时。


    abstract class MyClass {
        protected static $data;
        public static function setA($a) {
            self::$data['a'] = $a;
        }
        public static function getA() {
            return self::$data['a'];
        }
    }
    
    MyClass::setA(20);
    var_dump(MyClass::getA());
    

    (参见我前面所说的后期静态绑定和魔术方法) .

        2
  •  3
  •   Bill Karwin    14 年前

    abstract 班级。我不会用的 static 班级。

    class MyRegistry extends AbstractRegistry { }
    $reg = new MyRegistry();
    

    诚然,如果您要将抽象类交给另一个不符合您预期用途的开发人员,那么您只需要担心这个问题,但这就是为什么您要将该类也设置为单例的原因。不合作的开发人员可以重写私有构造函数:

    class Registry
    {
      private function __construct() { }
    }
    
    class MyRegistry extends Registry
    {
      public function __construct() { } // change private to public
    }
    

    如果你自己使用这个类,你只会记住 实例化类。那你就不需要任何一种机制来阻止它了。因此,既然您将此设计为供其他人使用,就需要某种方法来防止这些人绕过您的预期用途。

    1. 坚持使用单例模式,并确保构造函数也是 final

      class Registry
      {
        private final function __construct() {
        }
      }
      
    2. 使您的注册表支持 二者都 静态和对象用法:

      class Registry
      {
        protected static $reg = null;
      
        public static function getInstance() {
          if (self::$reg === null) {
            self::$reg = new Registry();
          }
          return self::$reg;
        }
      }
      

      然后你可以打电话 Registry::getInstance() 静态的,或者你可以打电话 new Registry() 如果你想要一个对象实例。

      然后您可以做一些漂亮的事情,比如在全局注册表中存储一个新的注册表实例!:-)

      我将其作为Zend框架的一部分在 Zend_Registry

        3
  •  1
  •   Ondrej Slinták    14 年前

    正如其他人所说,你不能实例化一个抽象类。你可以在你的类中使用静态方法来阻止实例化,但我并不喜欢这样做,除非我有正当的理由。

    我现在可能有点离题了,但在您的示例中,您说过您希望将此用于Registry模式类。你不想实例化它的原因是什么?为要使用的每个注册表创建一个Registry实例不是更好吗?

    class Registry {
        private $_objects = array( );
    
        public function set( $name, $object ) {
            $this->_objects[ $name ] = $object;
        }
    
        public function get( $name ) {
            return $this->_objects[ $name ];
        }
    }
    

    在这种情况下我甚至不会使用Singleton。

        4
  •  1
  •   elias    14 年前

    摘要也是一种误导。抽象是定义一个实现某些功能,但需要更多行为(通过继承添加)才能正常工作的类。除此之外,它通常是一个特性,不应该使用静态的。你实际上是在邀请程序员错误地使用它。

        5
  •  1
  •   Bert F    14 年前

    OO中有一些模式是常见的,并且是公认的。使用 abstract 以一种非常规的方式可能会引起混淆(对不起,我的一些示例是用Java而不是PHP编写的):


      • 使用 摘要 类上的修饰符,以防止直接实例化,但允许从类派生
    • 实用程序类 -在解空间中不表示对象的类,而是有用的静态操作/方法的集合,例如Java中的Math类。
      • 使类不可派生,例如Java使用 final 类的修饰符,以及
      • 阻止直接实例化-不提供构造函数并隐藏或禁用任何隐式或默认构造函数(和复制构造函数)
    • 单例类 -在解决方案空间中表示对象的类,但其实例化是受控制或限制的,通常是为了确保只有一个实例。
      通常由以下人员实施:
      • 使类不可派生,例如Java使用 最终的 类的修饰符,以及
      • 提供获取实例的特定方法-静态方法(通常是 getInstance() )返回唯一实例或有限实例数中的一个实例
        6
  •  0
  •   keithjgrant    14 年前

    abstract 实际上是为了表示类继承的“蓝图”,如您所说。

    注册表通常遵循单例模式,这意味着它们将在私有变量中实例化自身。定义为 摘要 会阻止这一切。

        7
  •  0
  •   prodigitalson    14 年前

    $instance 它是实际的注册表实例。最近我成了Zend框架的粉丝,典型的模式是这样的:

    class MyRegistry {
    
      protected static $instance = null;
    
      public function __construct($options = null)
      {
      }
    
      public static function setInstance(MyRegistry $instance)
      {
        self::$instance = $instance;
      }
    
      public static function getInstance()
      {
         if(null === self::$instance) {
            self::$instance = new self;
         }
    
         return self::$instance;
      }
    }
    

        8
  •  0
  •   Rupert Madden-Abbott    14 年前

    为了对一些php文档进行截取,假设您正在连接到一个数据库。连接到数据库没有多大意义,除非您要连接到特定类型的数据库。然而,无论数据库的类型如何,连接都是您想要做的事情。因此,可以在抽象数据库类中定义连接,并通过MYSQL类继承连接,使连接变得有意义。

    从您的需求来看,听起来您并不打算这样做,而是只需要一个没有实例的类。虽然您可以使用抽象类来强制执行这种行为,但对我来说这似乎有点麻烦,因为它滥用了抽象类的用途。如果我遇到一个抽象类,我应该能够合理地预期这将有一些抽象方法,例如,但您的类将没有。

    因此,单身似乎是一个更好的选择。

    更新:

    这里的正确方法肯定不是使用单例或抽象类,而是使用依赖注入。快速的方法是拥有大量的global或抽象类。

        9
  •  0
  •   user187291    14 年前

    Registry::$someValue $GLOBALS['Registry_someValue'] 前者看起来更“花哨”,但两种方法都不是真正面向对象的。

    因此,要回答您的问题,您不需要“singleton类”,而是需要一个singleton对象,可选地配备工厂方法:

    class Registry
    {
        static $obj = null;
    
        protected function __construct() {
            ...
        }
    
        static function object() {
            return self::$obj ? self::$obj : self::$obj = new self;
        }
    }
    
    ...
    
    Registry::object()->someValue;
    

    abstract 在这里不行。

        10
  •  0
  •   Decko    14 年前

    我想说这是一个编码习惯的问题。当您想到一个抽象类时,通常需要将其子类化才能使用。所以声明类抽象是违反直觉的。

    除此之外,只需在方法中使用self::$somevar,而不是$this->somevar,如果您将其实现为单例。