让一个孩子在PHP中扩展已初始化的父级

时间:2010-06-20 18:37:19

标签: php inheritance oop

我一直很难找到这个解决方案。我希望你们都能帮助我。

最好用一个例子来描述:

class Parent {
    public $nationality;

    function __construct($nationality)
    {
        $this->nationality = $nationality
    }
}

class Child extends Parent {
    function __construct() {
        echo $this->nationality; // hispanic
    }
}

// Usage:
$parent = new Parent('hispanic');
$child = new Child();

我希望孩子从已初始化的父级继承属性和方法。


编辑:感谢大家的回复 - 让我给你一些背景知识。我正在尝试制作一个模板系统。我有两个类 - 比如Tag.php和Form.php。

我希望它看起来像这样:

class Tag {
    public $class_location;
    public $other_tag_specific_info;
    public $args;

    function __construct($args)
    {
        $this->args = $args;
    }

    public function wrap($wrapper) {
        ...
    }

    // More public methods Form can use.
}

class Form extends Tag {
    function __construct() {
        print_r($this->args()) // 0 => 'wahoo', 1 => 'ok'
        echo $this->class_location; // "/library/form/form.php"
        $this->wrap('form');
    }

    function __tostring() {
        return '<input type = "text" />';
    }
}

// Usage:
$tag = new Tag(array('wahoo', 'ok'));
$tag->class_location = "/library/form/form.php";
$tag->other_tag_specific_info = "...";
$form = new Form();

我不喜欢复合图案的原因是它没有 我觉得为什么我会将Tag的实例传递给构造函数 其中一个子类,afterall - Form是一种Tag吗?

谢谢! 马特穆勒

4 个答案:

答案 0 :(得分:7)

你想做的事情可以说是糟糕的设计。如果您创建具有不同国籍的多个Parent实例,然后创建新的Child实例,会发生什么。这个孩子接受哪个国籍?

为什么不让Child类成为复合体,在构造函数中给它一个父元素?

class Child
{
    private $parent;

    function __construct($parent)
    {
        $this->parent = $parent;
    }

    function getNationality()
    {
        return $this->parent->nationality;
    }
}

然后使用

创建它
$parent = new Parent('hispanic');
$child = new Child($parent);

或者使用父母的工厂方法......其余的都取决于你的想象力。


注意:我忽略了这样一个事实:我没有在父类上使用getter获取国籍,也没有为Child提供超类(也许是父级?不是真的有意义) 。这些都是与设计相关的要点,但不在这个问题的范围内。

答案 1 :(得分:3)

我们已经在这里编辑了很多,所以我只想发一个新答案,希望没问题。为了它,我会留下旧答案。

无论如何,我们从最新的例子(Form extends Tag)开始。

好像你试图模仿某种Builder pattern,所以请耐心等待,让我们举一个简短的例子:

class TagBuilder
{
    protected $args;

    protected $className;

    protected $classLocation;

    function __construct()
    {

    }

    public function setClass($file, $class)
    {
        /* @todo Use reflection maybe to determine if $class is a Tag subclass */

        if (file_exists($file))
        {
            require_once $file; // Or use the autoloader
        }

        $this->classLocation = $file;
        $this->className = $class;
    }

    /**
     * @return Tag the built tag (or tag subclass) instance
     */
    public function getTag()
    {
        $fullyQualifiedClassName = $this->getFullyQualifiedClassName();

        $newTag = new $fullyQualifiedClassName($this->args);
        $newTag->class_location = $this->classLocation;

        return $newTag;
    }

    protected function getFullyQualifiedClassName()
    {
        return $this->className;
    }

    public function setArgs($args)
    {
        $this->args = $args;
    }
}

此外,您必须修改Form构造函数以调用它的parent::__construct($args)方法,以便传递$args依赖项。

然后用

运行它
$builder = new TagBuilder();
$builder->setClass('/library/form/form.php', 'Form');
$builder->setArgs(array('wahoo', 'ok'));

$form = $builder->getTag();

这是一个很长的例子,它意味着改变了一些OP的API,但是如果我正确地理解了这个问题,那么这就是OP希望在他的应用程序中模仿的所需行为(或者它的一部分)


注意:我知道这不是Builder模式的经典示例,并且getTag方法对某些纯粹主义者来说可能看起来有些含糊不清。

答案 2 :(得分:0)

这不应被视为良好做法,我只将该功能作为概念证明,以确定是否可以完成。

class MyClass
{
    public    $_initial    = NULL;

    public function setInitial($value) {
        $this->_initial = $value;
    }

}


class MyClass2 extends MyClass
{
    private    $_reversed = NULL;

    private function reverseInitial() {
        $this->_reversed = strrev($this->_initial);
    }

    public function getReversed() {
        $this->reverseInitial();
        return $this->_reversed;
    }

}


$class1 = new MyClass();
$class1->setInitial('rekaB kraM');

inherit($class1,'MyClass','MyClass2');

echo $class1->getReversed();

function inherit(&$object, $oldClassName, $newClassName) {
    $oldClass = new ReflectionClass($oldClassName);
    $newClass = new ReflectionClass($newClassName);

    $wrkSpace = serialize($object);
    $wrkTmp = explode('{',$wrkSpace);
    $startBlock = array_shift($wrkTmp);

    $oldClassProperties = $oldClass->getProperties();
    $oldClassPropertyNames = array();
    foreach($oldClassProperties as $oldClassProperty) {
        if (!$oldClassProperty->isStatic()) {
            if ($oldClassProperty->isPrivate()) {
                $oldClassPropertyNames[] = "\x0".$oldClassName."\x0".$oldClassProperty->getName();
            } elseif($oldClassProperty->isProtected()) {
                $oldClassPropertyNames[] = "\x0*\x0".$oldClassProperty->getName();
            } else {
                $oldClassPropertyNames[] = $oldClassProperty->getName();
            }
        }
    }

    $newClassProperties = $newClass->getProperties();
    $newClassPropertyValues = $newClass->getDefaultProperties();

    $newClassPropertyNames = array();
    foreach($newClassProperties as $newClassProperty) {
        $propertyName = $newClassProperty->getName();
        if (!$newClassProperty->isStatic()) {
            if ($newClassProperty->isPrivate()) {
                $newPropertyName = "\x0".$newClassName."\x0".$propertyName;
            } elseif($newClassProperty->isProtected()) {
                $newPropertyName = "\x0*\x0".$propertyName;
            } else {
                $newPropertyName = $propertyName;
            }
            $newClassPropertyNames[] = $newPropertyName;

            if (isset($newClassPropertyValues[$propertyName])) {
                $newClassPropertyValues[$newPropertyName] = $newClassPropertyValues[$propertyName];
            }
        }
    }


    $newClassPropertySet = array_diff($newClassPropertyNames,$oldClassPropertyNames);

    $toClassName = 'O:'.strlen($newClassName).':"'.$newClassName.'":'.(count($newClassPropertySet)+count($oldClassPropertyNames)).':';

    $newPropertyEntries = array_fill_keys($newClassPropertySet,NULL);
    foreach($newPropertyEntries as $newPropertyName => $newClassProperty) {
        if (isset($newClassPropertyValues[$newPropertyName])) {
            $newPropertyEntries[$newPropertyName] = $newClassPropertyValues[$newPropertyName];
        }
    }
    $newPropertyEntries = substr(serialize($newPropertyEntries),4+strlen(count($newClassPropertySet)),-1);

    $newSerialized = $toClassName.'{'.$newPropertyEntries.implode('{',$wrkTmp);

    $object = unserialize($newSerialized);
}

它并不完全符合您的要求...它需要父类的一个实例并将其更改为子类的实例,但可能会修改它以创建一个新的子项,同时保留父项的完整性

答案 3 :(得分:0)

好的,其他人告诉你这是一个糟糕的设计理念。我打算准不同意见并告诉你,你可能遇到了一个问题领域,基于类的OO层次结构不会对你有所帮助。如果你对这个想法很好奇,我建议你阅读Steve Yegge的“通用设计模式”(http://steve-yegge.blogspot.com/2008/10/universal-design-pattern.html)。

但是他有点啰嗦,所以它可能更简洁,同样有趣的是在JavaScript中考虑,你在这里谈论的内容将是微不足道的:

t = new Tag({arg1: val1, arg2: val2})
// ...
// later, when you decide t is a Form
t.prototype = Form.prototype. 

这与JavaScript一起工作的原因很大程度上与其基于原型的OO有关,但很多事实上,函数是可以分配给变量并传递的一等值。 PHP有一些动态调用函数的工具(和5.3,用于分配/传递它们)。我想如果你感兴趣并且愿意走出基于类的语言设施之外,你可能会做很多这样的事情。

首先,你会做很多人在没有类的语言中编写面向对象代码的事情:你的“类”可以只是哈希/关联数组(AAs)。

$quasiObj = array('prop1' => $val1, 'prop2' => $val2, '_class' => 'quasiClass');

您的方法可以只是将AA引用作为其第一个参数的函数。

function quasiClass_method1Name($this,$args = array()) {
   $this['prop1'] = $args[1] * $args[2]; 
}

如果您想要与类无关的方法调用,可以使用包装函数:

function method($this,$methodName,$args) {
   $fullMethodName = "{$this['_class']}_$methodName";
   $fullMethodName($this,$args);
}

// and now, the invocation
method($quasiObj,'method1Name',array(6,7));

在此系统下,当您想要将$quasiObjectTag推广到Form时,只需更改数组的_class密钥即可。如果需要进行内部状态更改,可以编写一个函数/方法来处理它。

你确实失去了解释器,为你追踪事物,有些人真的为此烦恼。这些人中的大多数人都在使用Java,因为PHP对他们的播放速度太快而且松散。这是一种动态语言。利用这个事实。 :)