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

Возврат интерфейса


Поговорим немного о том, что же собой представляют интерфейсы в объектно-ориентированном программировании. Это понятие довольно сложное, и о нем написано множество томов. Я, разумеется, не собираюсь их здесь пересказывать, потому что эта книга — о PHP, а не об идеологии ООП.

Интерфейсы — главная "изюминка"

практически всех сложных объектно-ориентированных систем (например, COM+, CORBA) и одно из основных понятий такого языка, как Java. Язык C++ также во всем поддерживает эту идеологию. Что же может дать нам PHP в этом отношении? К сожалению, довольно немного. И все-таки даже этого хватает, чтобы избавиться от недостатков, присущих ссылкам в PHP — во всяком случае, для нашей задачи.

Психологи утверждают, что яркие ассоциации запоминаются особенно хорошо. Что ж, проверим. Помните, когда мы были маленькими детьми, всем нам рассказывали сказки. Почему бы не заняться этим вновь? Как считаете, а?.. Ну и прекрасно (хотя я, право, не могу знать наверняка, что вы ответили). В скобках я буду давать комментарий, ведущий параллельную линию повествования. Итак, закроем глаза и представим себе большого кита (объект большого и сложного класса, например, MysqlTable), лениво плавающего по просторам океана (расположенного в оперативной памяти). Мы не настолько смелы, чтобы приблизиться к этому киту на достаточно близкое расстояние и дотронуться до него (не хотим использовать свойства или методы объекта напрямую). Если уж быть честными, мы даже не видим этого кита (не можем напрямую использовать в программе этот объект) — он слишком далеко (на него нет ссылок), и уж подавно не можем его сдвинуть с места (скопировать объект в другую переменную). Но зато, как мы знаем, его постоянно сопровождают рыбы-прилипалы (объекты-интерфейсы), маленькие и юркие (имеющие код небольшого размера), которые иногда заплывают достаточно далеко, чтобы мы могли с ними взаимодействовать. Этих рыб нам удалось выдрессировать, так что теперь они могут передавать киту любые наши приказы (передавать запросы на обслуживание) — разумеется, из тех, что сами понимают (для которых имеются соответствующие методы). Конечно, к киту могут "приклеиваться"


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



Вроде бы понятно, не правда ли? А теперь давайте уберем все, кроме слов-связок, но оставим курсив. Вот что у нас получится. "Представим себе объект большого и сложного класса, например, MysqlTable, расположенный в оперативной памяти. Мы не хотим использовать свойства или методы объекта напрямую. Если уж быть честными, мы даже не можем напрямую использовать в программе этот объект — на него нет ссылок, и уж подавно не способны скопировать объект в другую переменную. Но зато, как мы знаем, его постоянно "сопровождают" объекты-интерфейсы, имеющие код небольшого размера. Эти интерфейсы могут передавать запросы на обслуживание — разумеется, из тех, для которых имеют соответствующие методы. Конечно, объект может иметь несколько разных интерфейсов. Важно то, что мы не можем напрямую использовать объект. При этом мы имеем право копировать объекты-интерфейсы — главному объекту нет до этого ровным счетом никакого дела. В PHP объект "не знает", сколько у него интерфейсов и как они используются".

Итак, основная идея такова: отделим интерфейс MysqlTable

от его реализации, т. е. напишем класс IMysql, с которым и будем всегда работать. Этот класс должен содержать все те методы, которые поддерживаются MysqlTable, только заниматься они будут ни чем иным, как просто переадресацией вызовов на "настоящие" объекты. А последние, в свою очередь, хранятся в глобальном массиве объектов, на элементы которого должно ссылаться одно из свойств IMysql. Реализуем эту стратегию для упрощенной версии MysqlTable, имеющей только метод Drop()



и конструктор (листинг 31.5):

Листинг 31.5. Упрощенный интерфейс к таблице MySQL



// Массив объектов-таблиц, созданных в программе

$GLOBALS["Tables"]=array(); // вначале массив пуст

// Реализация класса. Это — обычный класс без каких-либо особенностей.

// Давайте предположим, что объекты этого класса недопустимо

// копировать обычным способом.

class MysqlTable {

 // . . .

 function MysqlTable($name) { echo "MysqlTable($name)<br>"; }

 function Drop() { echo "Drop()<br>"; }

}

// Класс-интерфейс

class IMysql {

 var $id; // идентификатор реализации таблицы (MysqlTable) в $Tables

 // Открывает таблицу с именем $name. Если эта таблица уже была

 // открыта ранее, то ничего не делает и просто становится ее

 // синонимом, иначе создает экземпляр объекта.

 function IMysql($name)

 { global $Tables;

 $this->id=$name;

 // Если объект для таблицы $name еще не создан, создать его

 if(!isset($Tables[$name])) $Tables[$name]=new MysqlTable($name);

 // Иначе объект уже существует и ничего делать не надо

 }

 // Уничтожает таблицу. Переадресуем вызов реализации

 function Drop() { $obj=&$GLOBALS['Tables'][$this->id]; $obj->Drop(); }

}

// Демонстрация работы с интерфейсом

$m=new IMysql("TestTable"); // объект создается

$m=new IMysql("TestTable"); // новый объект не создается!

$m->Drop(); // очищается единственный объект



Откровенно говоря, мы реализовали здесь не совсем то, что в объектно-ориентированном проектировании принято называть "интерфейсом". По определению интерфейс не может иметь конструктора, класс же IMysql его имеет. Так что слово "интерфейс" здесь, мягко говоря, не подходит, но я буду называть класс IMysql именно так — для краткости. Думаю, в этом нет ничего страш­ного — такова уж специфика PHP, и это самое простое, что можно было бы предложить. В самом деле, не писать же на PHP специальные "классы-фабрики", занимающиеся исключительно созданием объектов, как это принято в ООП…



Таким образом, как при копировании, так и при создании объекта-таблицы, который был уже ранее создан в программе, новый экземпляр объекта не создается. Иными словами, мы можем иметь сколько угодно объектов класса IMysql, ссылающихся на одну и ту же таблицу, и при изменении одного из них это "почувствуют" и все остальные. Нужно только грамотно реализовать все переадресующие функции.

И еще насчет класса-реализации: лучше всего дать ему какое-нибудь некрасивое имя (например, __MysqlTableImpl__), чтобы какой-нибудь неопытный пользователь случайно не стал к нему обращаться напрямую, а не через IMysql.

Хочу заметить, что в настоящих объектно-ориентированных языках нет причин прибегать к столь странным ухищрениям, потому что в них есть такое понятие, как указатель. В этих языках подобъект класса-интерфейса IMysql содержится прямо внутри объекта MysqlTable, и указатель на него можно получить либо посредством явных преобразований типов, либо с помощью специальных функций для "отпочковывания"

интерфейса. Например, в COM+ эти функции часто называют QueryInterface(). Здесь же у нас вышло нечто вроде примитивной поддержки указателей (ведь объект класса IMysql именно указывает на "хозяина" типа MysqlTable, но не содержит его в себе!), которых в PHP нет.

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






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