Попросите ребенка расширить уже инициализированный родительский элемент в PHP - PullRequest
2 голосов
/ 20 июня 2010

Мне было трудно найти решение с этим.Я надеюсь, что вы все можете мне помочь.

Лучше всего описать на примере:

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 для конструктора одного из его подклассов, в конце концов - Form - это тип Tag, верно?

Спасибо!Мэтт Мюллер

Ответы [ 4 ]

7 голосов
/ 20 июня 2010

То, что вы хотите сделать, это, возможно, плохой дизайн.Что произойдет, если вы создадите несколько экземпляров 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);

Или с фабричным методом от родителя ... остальное зависит от вашего воображения.


Примечание: Я игнорирую тот факт, что я не использовалполучатель национальности в родительском классе, и я не поставлял суперкласс для Child (возможно, Parent? Не имеет смысла).Все это относится к дизайну, но не входит в контекст этого вопроса.

3 голосов
/ 21 июня 2010

Мы уже немного отредактировали здесь, поэтому я просто выложу новый ответ, надеюсь, что все в порядке.Оставлю мой старый ответ ради него.

В любом случае, мы начинаем с вашего последнего примера (Form extends Tag).

Похоже, вы пытаетесь подражать некоторымвроде шаблон Builder , так что потерпите меня, и давайте рассмотрим короткий пример:

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();

Это немного длинный пример, и это подразумевает изменение некоторых API OP, но если яправильно понял вопрос, тогда это желаемое поведение (или его часть), которое ОП хочет имитировать в своем заявлении.


Примечание: Я знаю, что это неЭто классический пример паттерна Builder и то, что метод getTag может показаться некоторым пуристам немного двусмысленным.

0 голосов
/ 21 июня 2010

Хорошо, другие люди говорят вам, что это плохая идея дизайна.Я собираюсь квази-инакомыслие и сказать вам, что, возможно, вы столкнулись с проблемной областью, где основанная на классах иерархия ОО не принесет вам всех желаний.Если вам интересна эта идея, я рекомендую прочитать «Универсальный шаблон дизайна» Стива Йегге (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, связаны с его ОО, основанным на прототипах, но многое также связано с тем фактом, что функции являются первоклассными значениями, которые можно назначать переменным и передавать.В PHP есть некоторые средства для динамического вызова функций (и с 5.3 для их назначения / передачи).Я думаю, если бы вы были заинтересованы и хотели бы выйти за рамки языковых возможностей языка, вы, вероятно, могли бы сделать что-то вроде этого.

Во-первых, вы бы сделали то, что многие люди пишут-ориентированный код на языках без классов делает: ваши "классы" могут быть просто хешами / ассоциативными массивами (AA).

$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));

В этой системе, когда вы хотите продвигать свой $quasiObject изОт Tag до Form, просто изменить ключ _class массива.Если необходимы внутренние изменения состояния, вы можете написать функцию / метод, чтобы позаботиться об этом.

Вы теряете переводчика, следя за вещами для вас, и некоторые люди действительно волнуются по этому поводу.Большинство из этих людей используют Java, потому что PHP играет слишком быстро и свободно для них.Это динамический язык.Воспользуйтесь этим фактом.:)

0 голосов
/ 21 июня 2010

Это не должно считаться хорошей практикой, я только написал функцию как доказательство концепции, чтобы увидеть, можно ли это сделать или нет.

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);
}

Он не делает именно то, что вам нужно ... он берет экземпляр родительского класса и заменяет его на экземпляр дочернего класса, но, вероятно, его можно изменить, чтобы создать нового потомка, оставив родительский объект без изменений .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...