Первую часть читайте здесь.
Теперь приступим к практике. Наиболее показательным примером использования LSB, по-моему, является случай, когда у вас есть набор классов выполняющих похожие действия. В терминах веб-разработки мы часто встречаемся с такими задачами при обращениях к таблицам базы данных, особенно в ORM системах. Все ваши объекты для работы с таблицами будут похожи по сути, но при этом будут иметь собственный функционал ( и, соответственно, свои подклассы).
Допустим, у нас в системе есть класс Storable, который реализует (как вы догадались) (прим. переводчика: я не догадался :) ) паттерн Storable. Мы определяем классы, которые наследуются от класса Storable и задаем имена таблиц в конструкторах. Вот как это выглядит:
Можете пропустить подробности конструктора, он приведен только для того, чтобы вы представляли как работает класс Storable. Как вы уже успели понять, это сэкономит нам немного времени и позволит не тратить его на такие мелочи как простые запросы SELECT, INSERT и UPDATE.
В нашей системе, помимо основной таблицы Статьи (ArticleEntry), будут использоваться еще несколько таблиц, которые содержат мета-данные (в отношении многие-к-одному), например: теги, вложения. Мне также нужен простой способ удаления данных из под-таблиц, прежде чем обновлять данные в основной таблице (проще удалить мета-данные и создать заново, чем беспокоится о синхронизации данных). Итак, чтобы смоделировать код наиболее приближенный к схеме базы данных, я остановился на такой реализации:
Весь фокус в методе purge(). Свойство $this->tableName является защищенным свойством, которое мы получили от класса Storable. Вот пример использования:
У меня есть куча таких мелких классов для работы с таблицами мета-данных. К сожалению, поскольку наша система использует PHP версии 5.2, я не мог использовать функциональность LSB описанную в первой части статьи. И чтобы удалять мета-данные, я должен писать:
Если посмотрите выше, как определен метод purge(), то увидите, что tableName он получает из класса Storable, который получает его из конструкторов классов-потомков. Помимо этих тривиальных (но абсолютно необходимых) данных, а также получения объекта базы данных в $this->db, мы не достигли ничего создавая объект класса articleentryattachment. Код был бы намного понятнее и чище ( и конечно эффективнее), если бы можно было бы вызвать метод purge() статически. Рассмотрим такой код:
Первое, что, я надеюсь, вы заметили, это то что ArticleEntryAttachment стал намного проще. Теперь нет необходимости переопределять конструктор для подклассов, потому, что конструктор родительского класса самодостаточен. И теперь можно использовать метод purge() (используя LSB):
Поскольку, теперь purge() может получать имя таблицы, которое определяется в момент выполнения, мы можем сделать его статическим. В итоге, код — чище, выполнение — эффективней, поддержка (например, добавление новых под-классов) — пустяковая, потому как избыточность полностью убрана. Спасибо разработчикам PHP за то что это стало возможным!
В мануале также обсуждаются другие способы использования LSB, включая использование константы __CLASS__, так что не забудьте посетить php.net
Via Denis.in.ua: Позднее статическое связывание в PHP (Часть II: Практика)
Оригинал: Late Static Binding: a practical example
Теперь приступим к практике. Наиболее показательным примером использования LSB, по-моему, является случай, когда у вас есть набор классов выполняющих похожие действия. В терминах веб-разработки мы часто встречаемся с такими задачами при обращениях к таблицам базы данных, особенно в ORM системах. Все ваши объекты для работы с таблицами будут похожи по сути, но при этом будут иметь собственный функционал ( и, соответственно, свои подклассы).
Допустим, у нас в системе есть класс Storable, который реализует (как вы догадались) (прим. переводчика: я не догадался :) ) паттерн Storable. Мы определяем классы, которые наследуются от класса Storable и задаем имена таблиц в конструкторах. Вот как это выглядит:
class ArticleEntry extends Storable { public function __construct($id = null) { if (!is_null($id)) { $id = array('id' => $id); } parent::__construct('articleEntry', '*', $id); } } // вывод текста вхождения: $entry = new ArticleEntry(10); // Выборка записи из таблицы articleEntry для которой id = 10; echo $entry->html('articleBody'); // вывод тела загруженного вхождения // обновляем запись: $entry['ts'] = time(); // устанавливаем время в NOW $entry->save(); // Обновляем запись
Можете пропустить подробности конструктора, он приведен только для того, чтобы вы представляли как работает класс Storable. Как вы уже успели понять, это сэкономит нам немного времени и позволит не тратить его на такие мелочи как простые запросы SELECT, INSERT и UPDATE.
В нашей системе, помимо основной таблицы Статьи (ArticleEntry), будут использоваться еще несколько таблиц, которые содержат мета-данные (в отношении многие-к-одному), например: теги, вложения. Мне также нужен простой способ удаления данных из под-таблиц, прежде чем обновлять данные в основной таблице (проще удалить мета-данные и создать заново, чем беспокоится о синхронизации данных). Итак, чтобы смоделировать код наиболее приближенный к схеме базы данных, я остановился на такой реализации:
abstract class ArticleEntryAbstract extends Storable { public function __construct($table, $id = null) { if (!is_null($id)) { $id = array('id' => $id); } parent::__construct($table, '*', $id); } public function purge() { $s = $this->db->prepare('DELETE FROM ' . $this->tableName . ' WHERE pulseEntryId = ?'); $s->execute(array($this->data['entryId'])); } }
Весь фокус в методе purge(). Свойство $this->tableName является защищенным свойством, которое мы получили от класса Storable. Вот пример использования:
class ArticleEntryAttachment extends ArticleEntryAbstract { public function __construct($id = null) { parent::__construct('articleEntryAttachment', $id); } }
У меня есть куча таких мелких классов для работы с таблицами мета-данных. К сожалению, поскольку наша система использует PHP версии 5.2, я не мог использовать функциональность LSB описанную в первой части статьи. И чтобы удалять мета-данные, я должен писать:
$attach = new ArticleEntryAttachment(10); // SELECTS from the articleEntryAttachment table WHERE entryId = 10 $attach->purge();
Если посмотрите выше, как определен метод purge(), то увидите, что tableName он получает из класса Storable, который получает его из конструкторов классов-потомков. Помимо этих тривиальных (но абсолютно необходимых) данных, а также получения объекта базы данных в $this->db, мы не достигли ничего создавая объект класса articleentryattachment. Код был бы намного понятнее и чище ( и конечно эффективнее), если бы можно было бы вызвать метод purge() статически. Рассмотрим такой код:
abstract class ArticleEntryAbstract extends Storable { public function __construct($table, $id = null) { if (!is_null($id)) { $id = array('id' => $id); } parent::__construct(static::TABLE_NAME, '*', $id); } static function purge($entryId) { $db = Db::get(); // получаем синглетон базы данных $s = $db->prepare('DELETE FROM ' . static::TABLE_NAME . ' WHERE pulseEntryId = ?'); $s->execute(array($entryId)); } } class ArticleEntryAttachment extends ArticleEntryAbstract { const TABLE_NAME = 'articleAttachment'; }
Первое, что, я надеюсь, вы заметили, это то что ArticleEntryAttachment стал намного проще. Теперь нет необходимости переопределять конструктор для подклассов, потому, что конструктор родительского класса самодостаточен. И теперь можно использовать метод purge() (используя LSB):
ArticleEntryAttachment::purge(10);
Поскольку, теперь purge() может получать имя таблицы, которое определяется в момент выполнения, мы можем сделать его статическим. В итоге, код — чище, выполнение — эффективней, поддержка (например, добавление новых под-классов) — пустяковая, потому как избыточность полностью убрана. Спасибо разработчикам PHP за то что это стало возможным!
В мануале также обсуждаются другие способы использования LSB, включая использование константы __CLASS__, так что не забудьте посетить php.net
Via Denis.in.ua: Позднее статическое связывание в PHP (Часть II: Практика)
Оригинал: Late Static Binding: a practical example