LINUX.ORG.RU

[PHP suck of the week] Как всегда, мы не смогли осилить объектную модель


0

0

В общем, есть некий класс. У этого класса есть дети, внуки.

И вдруг мне нужно узнать имя класса, из которого вызывается статический метод. Типа вот:

<?php
class MyParentClass {
  public static function munchName() {
    return strtolower(get_class()); // myparentclass
  }
}

class MyChildClass extends MyParentClass {
}

echo MyChildClass::munchName(); // myparentclass... WTF?!

В сердцах я пошел в багзиллу узнать, что за фигня. И узнал — сначала до них два экрана не доходило, о чем речь, а когда дошло, они радостно сказали, что баг явственно показывает, какая у них долбанутая архитектура, из-за чего они его фиксить не будут. Сам тамошний диалог с багрепортером вызвал стойкие ассоциации с Хлебовводовым.

★★★★★

Сталкивался с этим где-то год назад. В принципе можно попробовать обойти костылями, но скорее всего выйдет некрасиво. Ещё одним способом обхода является заявление «а зачем это нужно» (при этом нужно сделать вид, что если тебе не нужно, то и никому больше не нужно).

AlexKiriukha ★★★★
()

ну в общем это lsb который в новых версиях работает как надо, а в старых да, костыль
но разве вы не знали на что вы подписывались собираясь делать проект на пыхе? по-моему никто там и не отрицает «игрушечность» объектной модели...
а вообще в вашем случае наверняка можно обойтись без такой фичи, подумайте, поменяйте что-то.

trashymichael ★★★
()

>И вдруг мне нужно узнать имя класса, из которого вызывается статический метод

Ты только сейчас с этим столкнулся? :D

KRoN73 ★★★★★
()
Ответ на: комментарий от trashymichael

>а вообще в вашем случае наверняка можно обойтись без такой фичи, подумайте, поменяйте что-то.

Дык, можно и вообще без объектов обойтись :)

А пока приходится писать в духе
$posts = objects_array('forum_post', array('topic_id' => $topic->id()));
вместо более удобного и не вызывающего проблемы пересечения пространств имён
$posts = forum_post::objects(array('topic_id' => $topic->id()));

Понятно, что для каждого класса можно объявить статический метод class_name(), возвращающий строку с его именем и пользоваться этим методом, но это - лишняя строчка и лишняя забота голове.

KRoN73 ★★★★★
()
Ответ на: комментарий от KRoN73

Короче, и так и так — херня выходит. Насчет же LSB, так оно только в 5.3. Вопрос, какая там версия пхп в текущих стабильных дистрибутивах? Особенно учитывая то, что все нетривиальное становится wontfix, пипл же хавает.

shimon ★★★★★
() автор топика
Ответ на: комментарий от trashymichael

> а вообще в вашем случае наверняка можно обойтись без такой фичи, подумайте, поменяйте что-то.

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

Хотя оно как-то проще s/PHP/Python/ и не знать горя — но для этого нужно свободное время для портирования стека приложений, а его (времени) нет.

shimon ★★★★★
() автор топика

офигеть.. что такое статическое связывание мы не знаем, но начнём всех ругать.

да это так, так же как в яве и т.д. это не баг, а чьё-то непонимание принципов :)

qnikst ★★★★★
()
Ответ на: комментарий от wfrr

ну с точностью до синтаксиса же.

P.S. ваще я сначала не понял автора, я подумал он хочет себе ткнуть в ногу ножом, а оказалось, что он вообще пытается её нафиг отстрелить

ну или в этом пхп getclass != this.getClass()

qnikst ★★★★★
()
Ответ на: комментарий от qnikst

оказалось, что он вообще пытается её нафиг отстрелить

Честн оя тоже не понял, с одной стороны имя класса может нужно знать для сохранения состояния, или логирования.

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

wfrr ★★☆
()
Ответ на: комментарий от KRoN73

Статический метод это метод класса, а не объекта и он может существовать без существования объекта. Поэтому код (внутри метода) получается аналогичный this::getClass(), что не имеет смысла :), т.е. это ClassName::getClass(). В общем-то вполне логичное поведение.

а для любителей пострелять в ногу добавили:

string get_called_class ( void )

P.S. а нафига всё-таки это надо я лично не очень понимаю.

qnikst ★★★★★
()
Ответ на: комментарий от qnikst

Часто статический метод путают с методом класса. Мне из контекста показалось, что нужен именно метод класса. В любом случае за get_called_class спасибо, для общего развития полезно. Хотя IMHO немного не логично, но хоть как-то.

AlexKiriukha ★★★★
()

Только таким костылем:

<?php
class MyParentClass
{
    const myname = __CLASS__;

    public static function munchName()
    {
        return strtolower(static::myname)."\n";
    }

}

class MyChildClass extends MyParentClass
{
    const myname = __CLASS__;
}

echo MyChildClass::munchName();
?>

iSage ★★★★
()
Ответ на: комментарий от AlexKiriukha

сможешь озвучить обоснование того, что статический метод (в php, java) не является методом класса?

qnikst ★★★★★
()
Ответ на: комментарий от qnikst

>get_called_class

Это только в 5.3

Т.е. пока можно считать, что его нету. Ибо для себя можно и на чём-то более удобном, чем PHP писать, а на хостингах пока 5.3 не ожидается.

а нафига всё-таки это надо я лично не очень понимаю.


Выше пример приводили. Удобно для статической загрузки объектов, например.

KRoN73 ★★★★★
()
Ответ на: комментарий от KRoN73

> Честн оя тоже не понял, с одной стороны имя класса может нужно знать для сохранения состояния, или логирования.

это?

я не понимаю почему метод живущий в классе A и вызывающийся в B должен знать, что он вызывается из B и как, если он не имеет доступа к псевдопеременной $this или не дублируется в классе B. Т.е. он на самом деле находится в A о чём и сообщает :)

[code] <?php class MyParentClass { static $myname = 'A';

public static function munchName() { return strtolower(self::$myname).«\n»; }

}

class MyChildClass extends MyParentClass { static $myname = 'B'; }

echo MyChildClass::munchName(); [/code]

может ещё тут munchName должен B выводить?

qnikst ★★★★★
()

Это не проблема этого языка. Так будет в любом языке с классической типизацией. Хочешь по-другому ищи языки с прототипированием.

theos ★★★
()
Ответ на: комментарий от qnikst

>это?

Нет: http://www.linux.org.ru/jump-message.jsp?msgid=5036383&cid=5037526

я не понимаю почему метод живущий в классе A и вызывающийся в B должен знать, что он вызывается из B


Например, когда другой статический метод класса B переопределяется в A и нужно вызывать именно его из нашего статического метода класса A.

KRoN73 ★★★★★
()
Ответ на: комментарий от KRoN73

про ссылку без контекста мне щас сложно понять, вечером пересмотрю может и соглашусь.

Например, когда другой статический метод класса B переопределяется в A и нужно вызывать именно его из нашего статического метода класса A.

я похоже что-то не понимаю в этой жизни.


<?php
class MyParentClass
{
  static $myname = 'A';

  public static function munchName() {
      return strtolower(self::$myname)."\n";
  }

}

class MyChildClass extends MyParentClass {
  static $myname = 'B';
  public static function munchName() {
    return 'asdasdasdl';
  }
}

echo MyChildClass::munchName();

у меня успешно вызвался статический метод дочернего класса.

qnikst ★★★★★
()
Ответ на: комментарий от qnikst

вот так вызвался B и всё работает, а из статического метода A мы ничего не знаем не можем и не должны знать об наследниках переопределении и т.д.

qnikst ★★★★★
()
Ответ на: комментарий от qnikst

Слишком поверхностно знаю java и слишком не люблю php, чтобы заниматься размышлениями на их тему. Зато точно знаю, что в python есть явное различие и мне этого (пока) достаточно.

AlexKiriukha ★★★★
()
Ответ на: комментарий от qnikst

>а из статического метода A мы ничего не знаем не можем и не должны знать об наследниках переопределении и т.д.

Но в динамическом языке это элементарно. И очень удобно. Было бы. А пока приходится обходится костылями.

KRoN73 ★★★★★
()
Ответ на: комментарий от qnikst

> у меня успешно вызвался статический метод дочернего класса.

Насколько я понял задачу, нужен статический метод только в базовом классе, но при этом он должен знать как он вызывается (MyParentClass или MyChildClass в данном случае).

AlexKiriukha ★★★★
()

Функция get_class() актуальна в основном для объектов и возвращает имя класса ассоциированного с данным объектом.

swwwfactory ★★
()

Потому что «get_class — Returns the name of the class of an object» а где в этом примере объект?

RR
()
Ответ на: комментарий от RR
<?php 
interface iMunchName {
  public static function munchName();
}

class MyClass implements iMunchName { 
  public static function munchName() { 
    return strtolower(__CLASS__);
  } 
}  

средство против склероза.

RR
()
Ответ на: комментарий от KRoN73

но почему вы решили что objects должен быть статичным методом? делайте себе $posts = $forum_posts->objects(... ???
насмотрятся на ror а потом слюной истекают. php вам не руби, хотите нормальный язык — меняйте.

trashymichael ★★★
()
Ответ на: комментарий от trashymichael

>делайте себе $posts = $forum_posts->objects

А $forum_posts - это костыль в виде $forum_posts = new forum_post(NULL)?

Нет уж, я лучше тогда продолжу использовать вариант с функциональным, а не объектным загрузчиком, как в первом примере привёл.

насмотрятся на ror а потом слюной истекают


Не знаю. ROR не видел. Просто здравый смысл. Ничто (кроме лени) не мешает в динамическом языке (а при желании разработчика - и в статическом) знать с каким классом вызывали статический метод.

хотите нормальный язык — меняйте.


Не учите меня жить и я не скажу, куда Вам идти :)

KRoN73 ★★★★★
()
Ответ на: комментарий от trashymichael

>а почему костыль?

Потому что нормально так не ходят :)

а что у вас forum_post, некий orm?


Естественно.

почему вы считаете что таблицу должны воплощать классы а не объекты?


Ок. Есть абстрактный object_db(), класс, загружаемый через ORM. Есть его наследник, описывающий конкретные привязки - таблица, поля, свойства.

Задача - загружать массив таких элементов. Примеры разумного и реального подходов - см. в самом начале темы.

KRoN73 ★★★★★
()

> И вдруг мне нужно узнать имя класса, из которого вызывается статический метод

... и это означает, что что-то пошло не так. Причем скорей всего, в твоем проекте :-)

no-dashi ★★★★★
()
Ответ на: комментарий от KRoN73

Есть абстрактный object_db(), класс, загружаемый через ORM. Есть его наследник, описывающий конкретные привязки - таблица, поля, свойства.

Ага. И разруливает их внешний класс. Фабрика то бишь.

$fabric = new Persistence();
$object = $fabric->loadKnown("UserAccount",1);
print $object->getName(); // Must be maxcom or anonymous
$object->setName("SiteOwner");
$fabric->save($object);

Не надо тащить в класс прикладной модели работу с базой.

no-dashi ★★★★★
()
Ответ на: комментарий от no-dashi

Не надо тащить в класс прикладной модели работу с базой.

А в классе и нет работы с базой. В классе есть бэкенд базы и данные для него.

И ты не прав. Ты своей фабрикой, вытащенной наружу, светишь потрохами класса. А его параметры - это его внутреннее дело. Один класс имеет данные, отображаемые на БД, другой - вычисляемые, третий - статические. делая один класс для загрузки разного рода классов тебе всё равно придётся или выносить часть классовой инкапсуляции в фабрику, или прописывать внутрь класса модель базы и её тип, а фабрика будет лазить за этими данными в класс. Опять же, излишне нарушая инкапсуляцию.

Скажем, сейчас у меня работа фактически похожа на твою, только уровень фабрики спрятан:

=php]
$object = object_load('user_account', 1);
print $object->name();
$object->set_name("SiteOwner");

object_load, по сути, берёт из класса $object->storage_engine() и делает для него $storage->load($object). Понятно, что забота о том, какой у него strorage_engine (mysql, xml, сетевой источник) и нужен ли он вообще - это забота уже самого класса.

Но функциональный вызов, хоть и менее некрасив, чем прямое указание фабрики, всё равно - некрасив. Раз уж класс знает свою фабрику, знает её статически (это допущение, так как сложно придумать реальную ситуацию, когда фабрика будет разной, в зависимости от содержимого объекта), то логично бы было, чтобы эта фабрика вызывалась объектно:

=php]
$object = user_account::load(1);

И такой подход совершенно замечательно работал бы, если бы get_called_class() появился раньше. А пока его применение задержится ещё на пару лет, пока не обновятся последние популярные массовые хостинги.

KRoN73 ★★★★★
()
Ответ на: комментарий от trashymichael

>вы так говорите будто нет вменяемых реализаций ar на пхп

Чего нет?

вот мол надо так а иначе не ходят, я дартаньян короче


Ну так приведите вменяемый и удобный пример, а не тот ужас, что Вы привели :)

KRoN73 ★★★★★
()
Ответ на: комментарий от KRoN73

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

object_load, по сути, берёт из класса $object->storage_engine()


Вот это и есть причина твоей проблемы. Экземпляру класса (да и самому классу) нет необходимости знать что-либо вообще о своем происхождении.

так как сложно придумать реальную ситуацию, когда фабрика будет разной, в зависимости от содержимого объекта


Нет ничего легче. Например, если надо взять объект из одной базы и скопировать в другую, где названия полей немного отличаются, да и самих полей поменьше, что может быть проще? А теперь еще забавней - представим, что объекты твоего класса берутся из двух разных источников (ну например из двух баз). К чему это приведет? К тому, что ты потащишь лоадер в каждый экземпляр класса. Поздравляю :-)

тебе всё равно придётся или выносить часть классовой инкапсуляции в фабрику, или прописывать внутрь класса модель базы и её тип


Нет, не придется. В том, что я сейчас пишу, у меня есть:
1. Иерархия прикладных классов
2. Схема, сопоставляющая атрибуты объектов разных классов полям в базе
3. Фабрика, которая умеет сохранять и восстанавливать объекты в соответствии со схемой
Результат - фабрика ничего не знает о классах, классы ничего не знают о фабрике (и о базе или другом хранилище), а указывать соответсвие атрибут<->поле базы все равно где-то надо, чем и занимается схема. В результате, я могу свободно перщелкивать лоадеры на ходу.

если бы get_called_class()


Каждый раз, когда кто-то заводит речь про get_called_class() и его аналоги, выясняется, что этот «кто-то» допустил идеологическую ошибку на этапе проектирования. Что же до инкапсуляции, то фабрика как объект другого уровня, не вмешивающаяся в логику, имеет право быть «более доверенной» чем остальные (friend?), хотя у меня в этом необходимости не возникало.

P.S.: Да, я извращенец. И у меня нет ни одного атрибута у прикладного объекта кроме «списка свойств» и «списка значений», и методов-оберток для универсального сеттера с двумя параметрами - имя свойства и значение свойства, в результате чего объект умеет отлавливать запись строки в «числовое» свойство, передачу объекта не того класса и много-много других «усекновений слишком шаловливых ручек», типа невозможности записать в список значение не того типа, который задекларирован при создании списка :-)

no-dashi ★★★★★
()
Ответ на: комментарий от no-dashi

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

Предложи более удобный вариант.

Экземпляру класса (да и самому классу) нет необходимости знать что-либо вообще о своем происхождении.


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

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


Это очень редкая ситуация. И неправильная. Объект должен быть простым. Это после таких наворотов и идёт потом ругань на сложности или недостатки объектных моделей (совсем недавно на ЛОРе был спор).

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

А теперь еще забавней - представим, что объекты твоего класса берутся из двух разных источников (ну например из двух баз).


То же самое. Это должны быть два вида объектов. Не может один целостный объект быть так разорван. Кто будет гарантировать целостность данных в разных БД?

К чему это приведет? К тому, что ты потащишь лоадер в каждый экземпляр класса. Поздравляю :-)


Именно так. Всё остальное - гязное хакерство и костыли.

3. Фабрика, которая умеет сохранять и восстанавливать объекты в соответствии со схемой


Фабрика уже привязывается к структуре объекта. Ну, вот тебе пример. Сделать класс страницы, выводящей список объектов с разбивкой на страницы. Типа такого (реальный пример):
[code=php]
<?php

class xp1990_dealers_admin_logs extends bors_paginated
{
var $main_class = 'bors_log_mysql';
function config_class() { return 'rp1990_dealers_admin_config'; }

var $title_ec = 'Обработка файлов'; }
var $nav_name_ec = 'файлы'; }
function where()
{
return array_merge(parent::where(), array('category' => 'files'));
}
}
[/code]

Родительский класс ничего не знает о структуре классов объектов, которые будет загружать. Есть только имя класса, условия выборки, параметры разбивки на страницы (в данном классе нет, так как наследуются из родителя) для вычисления лимитов. Но даже их родительский класс не вычисляет. Всё о себе знают сами объекты, которые он выводит. Поэтому структура объектов может быть любой.

Если же ты в родительском классе будешь применять конкретные фабрики объектов, то ты или должен будешь лезть в потроха классов, чтобы те сказали, какой фабрикой они грузятся, или ограничиться в наборе поддерживаемых классов только одним типом фабрики. Второй вариант я даже не рассматриваю, у меня он реально неприменим, а в первом случае - нагляднее, компактнее и безопаснее комплект извлечения из класса объекта класса фабрики, её инициацию и загрузку объектов вынести в отдельный метод или функцию, как у меня и сделано. object_load($class_name, $object_id) - это и есть по сути извлечени из класса имени его фабрики, инициация её и загрузка с её помощью объекта.

Результат - фабрика ничего не знает о классах, классы ничего не знают о фабрике


Такого не может быть на практике. Или тебе придётся всё связывание фабрик и классов делать вручную. Как XML-фабрика будет грузить данные из БД Oracle? :)

В результате, я могу свободно перщелкивать лоадеры на ходу.


Это грязный хак.


если бы get_called_class()

Каждый раз, когда кто-то заводит речь про get_called_class() и его аналоги, выясняется, что этот «кто-то» допустил идеологическую ошибку на этапе проектирования


Пока я вижу только идеологически кривые костыли в твоих объяснениях :)

P.S.: Да, я извращенец. И у меня нет ни одного атрибута у прикладного объекта кроме «списка свойств» и «списка значений», и методов-оберток для универсального сеттера с двумя параметрами - имя свойства и значение свойства


По сути в базовом варианте у меня то же самое. В простейшем случае ORM-класс - это набор полей, можно даже без карты соответствия БД/бэкенда, если имена свойств совпадают с именами полей. Но для меня первый приоритет - как можно более высокий уровень инкапсуляции. Поэтому при ручной работе с классом я нигде не касаюсь вопроса его внутренней структуры. Поэтому и о своей фабрике класс тоже сообщает сам (обычно это описано в одном из базовых родительских классов).

Так вот, get_called_class() позволит сделать то же самое красивее и удобнее. Хотя по сути это будет чисто синтаксический сахар. Принципиальной разницы между
$object = object_load('class_name', $object_id);
и
$object = class_name::load($object_id);
нет. Но второй вариант красивее и логичнее.

в результате чего объект умеет отлавливать запись строки в «числовое» свойство


У меня это тоже имелось. Но где-то через год использования отключил. Ни разу за это время не сталкивался с ошибками типов, а производительность снижается, особенно на массовой инициализации объектов. А сейчас такие ошибки система юнит-тестов может ловить, если понадобится.

KRoN73 ★★★★★
()
Ответ на: комментарий от KRoN73

> $object = object_load('class_name', $object_id);

и

$object = class_name::load($object_id);


нет. Но второй вариант красивее и логичнее.



Вот ты и попался. Представим себе, что у нас есть Person и Account как расширение Person, и мы грузим из базы сообщение от некоего пользователя с id=1. Вопрос - метод какого класса ты будешь вызывать, Person::load() или Account::load()? Фабрика от этого свободна, ибо в ее логику можно заложить «отличатор типов», а ты как выкрутишься?

no-dashi ★★★★★
()
Ответ на: комментарий от KRoN73

> а производительность снижается, особенно на массовой инициализации объектов.

Не настолько сильно, чтобы заботитсья об этмо имело смысл, IMHO

no-dashi ★★★★★
()
Ответ на: комментарий от no-dashi

>Вопрос - метод какого класса ты будешь вызывать, Person::load() или Account::load()?

Блин! Так с этого тема и началась! :D

Сейчас - никак. И потому такой вариант не используется. При повсеместном get_called_class() можно будет узнать, для какого класса вызывался load() и использовать call_user_func() для статического обращения к геттерам этого класса.

Фабрика от этого свободна


Да. Потому что в неё передаётся имя загружаемого класса в явном виде. Поэтому я фабрики и использую. Хотя и скрываю реализацию от конечного использования.

KRoN73 ★★★★★
()
Ответ на: комментарий от no-dashi

>Не настолько сильно, чтобы заботитсья об этмо имело смысл, IMHO

Сильно. На загрузках массивов в сотни-тысячи объектов основное время процесса уходит на эти проверки. Результат профилирования :)

KRoN73 ★★★★★
()
Ответ на: комментарий от trashymichael

>все это как-то скучно и длинно

Посмотрел. У меня - гибче :)

KRoN73 ★★★★★
()
Ответ на: комментарий от KRoN73

При повсеместном get_called_class() можно будет узнать, для какого класса вызывался load()

Ты походу чего-то недопонимаешь. Все что у тебя есть в момент вызова - это идентификатор объекта, который может быть как Person, так и Account. В твоей схеме цепочка вызовов такая:

object_load() {
  Message::storage_engine()->... {
    $msg->setSender(object_load($sender_id) {
        (???) Person::storage_engine() или Account::storage_engine (???)
        return (экземпляр Account или экземпляр Person)
      }
    );
    return (экземпляр Message)
  }
}

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

no-dashi ★★★★★
()
Ответ на: комментарий от no-dashi

(???) Person::storage_engine() или Account::storage_engine (???)

class Abstract
{
    static function load($id) { return object_load(get_called_class(), $id); }
}

class Person extends Abstract { var $table = 'persons'; }
class Account extends Person { var $table = 'accounts'; }

$p = Person::load(123); // select * from persons where id = 123 ...
$a = Account::load(456); // select * from accounts where id = 456 ...

function object_load($class_name, $id)
{
    $object = new $class_name;
    $object->loader()->load($object, array('id' => $id));
    return $object;
}

В каком месте непонятно? :)

...

На самом деле можно даже так:

class Abstract
{
    function __construct($id = NULL)
    {
        if($id)
            $this->loader()->load($this);
    }
}

$p = new Person(123);
$a = new Account(456);

Но это уже извращение :)

KRoN73 ★★★★★
()
Ответ на: комментарий от KRoN73

Ты лучше покажи как ты будешь выкручиваться из такой ситуации:

class Person {
    var $name; // String
    [... тут всякие методы ...]
}
class Account extends Person {
    var $login; // String
    [... тут всякие методы ...]
}
class Message {
    var $author; // Instance Person
}

А теперь подумай, как ты будешь в лоадере делать загрузку сообщений, которые изначально создавались примерно так:

$msg1 = new Message();
$msg1->setAuthor(new Person(1));
$msg2 = new Message();
$msg2->setAuthor(new Account(2));

Причем надо обеспечить, чтобы после загрузки сообщений из базы

print $msg1->getAuthor()->get_class(); // Напечатала "Person"
print $msg2->getAuthor()->get_class(); // напечатала "Account"

Чтобы получить работающее решение, тебе придется сколотить очень большие костыли.

no-dashi ★★★★★
()
Ответ на: комментарий от no-dashi

Ты лучше покажи как ты будешь выкручиваться из такой ситуации

Э... А в чём проблема?

$msg1->setAuthor(new Person(1));

тут свойству Author будет присвоена ссылка на объект класса Person.

$msg2->setAuthor(new Account(2));

Тут - на объект класса Account.

print $msg1->getAuthor()->get_class(); // Напечатала «Person»

Соответственно, этот код будет работать автоматически. Без какого-либо участия с моей стороны или костылей.

getAuthor вернёт объект. Метод вида

function get_class() { return get_class($this); }
работает для любых объектов без всяких хитростей. Собственно, у меня и используется часто. Только называется:
function class_name() { return get_class($this); }

KRoN73 ★★★★★
()
Ответ на: комментарий от no-dashi

У меня такое решение достигается тривиально, ибо у фабрики есть умные потроха:

class Persistence {
    var $classSelector; // instance of IClassSelector
    public function loadKnown($classname,$id) { ... }
    public function loadUnknown($id) {
        $this->loadKnown(
            $this->classSelector->getClassFor($id), $id
        );
    }
}

Никаких static'ов нет, вообще, объекты не знают откуда они взялись, куда их пишут - нефиг им голову взякой фигней забивать.

А static'и вообще нужны только для организации синглетонов, и за попытку использования static'ов во всех остальных случаях надо лупить линейкой по пальцам, ибо нефиг. Обрати внимание - в твоем первом примере ты поступаешь почти правильно (не считая static в классах, но этот static имеет чисто декоративный характер).

А во втором случае как раз уходишь на путь, который ведет к стройной системе костылей и подпорок.

no-dashi ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.