САМОУЧИТЕЛЬ PHP 4

       

Ссылки и интерфейсы


Как мы знаем, в PHP оператор присваивания всегда копирует значения переменных, какой бы сложной структуры они ни были. Это же, напомню, происходит и с объектами. Что тогда получится, если мы скопируем, например, объект класса MysqlTable? Вообще говоря, ничего хорошего. Произойдет дублирование всех свойств и методов объекта. Фактически, мы получим сразу две независимые "обертки"

для одной и той же таблицы MySQL. Таким образом, изменения, внесенные в первый объект, никак не повлияют на второй, и наоборот.

Я специально проектировал класс MysqlTable так, что даже после копирования объектов этого типа не происходило никаких фатальных недоразумений описанного выше рода. Однако так можно сделать далеко не всегда. Представьте, например, что нам приходится очень часто использовать функцию GetInfo() и довольно редко — SetInfo(). Так как GetInfo() при каждом запросе обращается к MySQL, мы можем получить здесь ощутимый проигрыш в быстродействии. Очевидное решение заключается в промежуточном хранении данных, возвращаемых нашим "обычным"

методом GetInfo() в специальном свойстве объекта. Действительно, зачем загружать сервер лишней работой по чтению одних и тех же данных, когда можно хранить их в программе и сразу же использовать? Это свойство будет инициализироваться при конструировании объекта класса MysqlTable и обновляться каждый раз при обращении к методу SetInfo().

То есть наше свойство будет представлять собой аналог "зеркала" записи в таблице MySQL, по аналогии с "зеркалами" сайтов в Интернете. Класс MysqlTable

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

Но, к сожалению, описанная схема не может быть реализована в PHP напрямую, и именно по причине обязательного полного копирования переменных. Вот пример, который породит ошибку:

$t1=new MysqlTable("MyTable");

. . .

function DoIt($t)

{ $t->SetInfo("This is the new info!");

}



. . .


$t=new MysqlTable("MyTableName");

$t->SetInfo("Data");

DoIt($t);

$Inf=$t->GetInfo(); // в $Inf будет строка Data!

Впрочем, в приведенном только что фрагменте это недоразумение можно легко преодолеть, передав функции ссылку на объект:

function DoIt(&$t)

{ $t->SetInfo("This is the new info!");

}

Я намеренно привел здесь пример, когда ограничение на копирование объектов все же можно обойти относительно безболезненно. Настало время описать неразрешимую (во всяком случае, похожим методом) задачу. Но прежде обратите внимание, что в нашем примере объект передается "вглубь"

кода (внутрь функции), а не "наружу"

(из функции). Вот как раз в последнем случае и будет возникать неразрешимая проблема.

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

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



Возможно, вы спросите: зачем нам вообще такая функция, когда можно воспользоваться оператором new

напрямую? Тогда еще раз перечитайте предпоследнюю фразу предыдущего абзаца: "В случае, если таблица уже существует, новый объект не создается". В то же время оператор new

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

Легко сказать — "возвращает уже существующий объект", но несколько сложнее — реализовать это. Рассмотрим два различных способа, с помощью которых мы можем достичь цели.



Как следует из законов Мэрфи, "у любой сложной задачи всегда имеется одно простое, красивое и легкое для понимания… неправильное решение". В нашем случае это будет возврат из функции объекта класса MysqlTable "обычным" способом, подразумевающим копирование. Но ведь, по имеющейся между нами договоренности, объекты этого класса нельзя копировать!


Содержание раздела