如何在没有构造函数的情况下强制实现类变量的初始化?

时间:2010-01-05 11:50:44

标签: php oop

我有两个班级:

抽象实体抽象ClassA扩展实体 ClassB扩展ClassA

实体是各种实体的通用类,例如汽车或学生。有一个名为$_entity_name的静态类变量,它包含ER图中实体的确切类名。

最大的问题:实体无法设置此变量的值,但使用它!所以我必须在实体中定义它,但我不能在ClassA中重新定义它。

ClassA是特殊ORM实体的自动生成的基类(比如说“Student”)。用户应该将此基类子类化以添加自己的业务逻辑。

ClassB由用户创建并扩展ClassA。这是他增加业务逻辑的地方。他不应该触摸$_entity_name或设置此值。

我看到的唯一方法是:

ClassA定义了一个init()方法,该方法将这些“系统级”实例变量初始化为合理的值。

我希望用户不必考虑调用任何不寻常的东西。我不知道在PHP中是否通常也明确地调用父类的构造函数。但我猜它不是。

问题: A)在实例化类之后调用init()最方便的方法是什么? B)有没有办法以这种方式实现这一点,创建ClassB的用户一定不要考虑调用init()? C)我还有其他选择吗?

6 个答案:

答案 0 :(得分:1)

不完全是您问题的答案,但可能是您问题的解决方案:
而不是拥有包含实际类名的属性(这是你想要的,不是吗?)你可以使用get_called_class()(从php 5.3.0开始)获取静态函数的类的名称被称为。

 class Entity {
   public static function foo() {
     echo 'name=', get_called_class(), "\n";
   }
 }

 class ClassA extends Entity { }
 class ClassB extends ClassA { }

 Entity::foo();
 ClassA::foo();
 ClassB::foo();

打印

 name=Entity
 name=ClassA
 name=ClassB

答案 1 :(得分:1)

使用parent::__construct()调用父构造函数实际上很常见。您可以接受以下操作吗?

class B extends A { 
    function __construct($entity_name = '') {
        parent::__construct($entity_name);
    }
}

abstract class A extends Entity { 
    function __construct($entity_name = '') {
        parent::__construct($entity_name);
    }
}

abstract class Entity { 
    static $entity_name;
    function __construct($entity_name = '') {
        self::$entity_name = $entity_name;
    }
}

$b = new B('my_name');

答案 2 :(得分:1)

据我了解你的问题:ClassBClassA的子类,当ClassB对象被实例化时,你想拥有一个方法(例如{{ 1}})在init()级别调用,而无需用户在ClassA代码中编写任何类型的内容。

这就是构造函数的用途。

但是,您暗示由于ClassB将由您的用户编写,您不希望强迫他/她从ClassB的构造函数中调用ClassB,这就是您之所以想到的原因使用parent::__construct()方法,以及为什么要寻找一种方法让init()init()的方式自动调用,我是对的吗?那么,这个怎么样:

  • __construct()中添加空init()方法,用户在撰写ClassA
  • 时必须超载
  • 在ClassA的ClassB 调用__construct()中,在ClassA的$_entity_name(例如初始化__construct())中执行您想要执行的操作。
  • 将ClassA的$this->init()设置为__construct(),以便用户不会重载构造函数。
  • 告诉您的用户他们必须重载final方法来为init()
  • 的子类进行自定义初始化

专业人士:您可以让ClassA完成初始化工作,而无需用户编写任何内容。

缺点:您的用户无法重载ClassA,但可以重载__construct()而不是为init()进行自定义初始化

答案 3 :(得分:0)

VolkerK的答案是正确的 - 但您也可以查看instanceof运算符和get_class()函数。

“遗憾的是,我没有使用PHP类,而是ER图中的实体名称。它有点不同”(openfrog)

然而,这是一个完全不同的问题(假设您无法在实体命名系统中修复此缺陷),两个列表之间的映射条目是明智之举。

您在运行时使用属性的解决方案非常混乱且不灵活。它依赖于隐式映射,其细节分散在整个代码库中,任何非线性启发式都很难维护/调试(例如,如果多个类映射到ERD中的同一实体)。

下进行。

答案 4 :(得分:0)

在ClassB下面,我看到了两种确保ClassA初始化的方法:

  1. 指示ClassB的构造函数调用parent::__construct()或不存在。

  2. 创建一个由ClassA调用并由ClassB实现的钩子init函数。这样,ClassA处理__construct()和ClassB中ClassA的初始化$this->init()

答案 5 :(得分:0)

这个建议不能被覆盖,以满足您的需求。

Class A{
   final public function __construct() {
       // do your stuff here

       // optionally add something like this:
       $rc = new ReflectionClass($this);
       if(in_array('init', $rc->getMethods()) {
           $this->init();
       }
   }

如果它没有做你想要的(即你希望从A派生的类能够使用__construct()),你应该考虑封装你的类而不是继承它