
PHP5_nachinayushim
.pdf
Работа с UML и классами 543
PropertyObject
DataManager
Validator
Address
PhoneNumber
Entity
|
|
|
|
|
|
|
|
|
|
|
|
Individual |
|
|
Organization |
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Рис. 13.12.
Теперь можно начинать разрабатывать классы Entity, Individual и Organization. Следующий код представляет собой полностью реализованный класс Entity:
<?php require_once('class.PropertyObject.php'); require_once('class.PhoneNumber.php'); require_once('class.Address.php'); require_once('class.EmailAddress.php');
abstract class Entity extends PropertyObject {
private $_emails; private $_addresses; private $_phonenumbers;
public function __construct($entityID) {
$arData = DataManager::getEntityData($entityID); parent::__construct($arData);

544 Глава 13
$this->propertyTable['entityid'] = 'entityid'; $this->propertyTable['id'] = 'entityid'; $this->propertyTable['name1'] = 'sname1'; $this->propertyTable['name2'] = 'sname2'; $this->propertyTable['type'] = 'ctype';
$this->_emails = DataManager::getEmailObjectsForEntity($entityID); $this->_addresses = DataManager::getAddressObjectsForEntity($entityID); $this->_phonenumbers =
DataManager::getPhoneNumberObjectsForEntity($entityID);
}
function setID($val) {
throw new Exception('Изменение значения поля id не допускается!');
}
function setEntityID($val) { $this->setID($val);
}
function phonenumbers($index) {
if(!isset($this->_phonenumbers[$index])) {
throw new Exception('Указан некорректный номер телефона!'); } else {
return $this->_phonenumbers[$index];
}
}
function getNumberOfPhoneNumbers() { return sizeof($this->_phonenumbers);
}
function addPhoneNumber(PhoneNumber $phone) { $this->_phonenumbers[] = $phone;
}
function addresses($index) {
if(!isset($this->_addresses[$index])) {
throw new Exception('Указан неправильный адрес!'); } else {
return $this->_addresses[$index];
}
}
function getNumberOfAddresses() { return sizeof($this->_addresses);
}
function addAddress(Address $address) { $this->_addresses[] = $address;
}
function emails($index) {
if(!isset($this->_emails[$index])) {
throw new Exception('Указан неправильный email-адрес!'); } else {
return $this->_emails[$index];
}
}
function getNumberOfEmails() { return sizeof($this->_emails);
}

Работа с UML и классами 545
function addEmail(Email $email) { $this->_emails[] = $email;
}
public function validate() {
//Общие процедуры проверки корректности данных
}
}
?>
Перемещение всех функций для получения и установки свойств в родительский класс PropertyObject упрощает класс Entity и гарантирует, что в нем присутствует только код, необходимый для реализации объекта.
Класс Entity объявлен как абстрактный, потому что сам по себе он бесполезен. Все объекты являются объектами либо класса Individual, либо Organization. Соз+ давать объекты класса Entity бессмысленно. Объявление класса как абстрактного препятствует созданию объектов этого класса.
В код были добавлены обращения к нескольким новым функциям класса DataManager: getEntityData() и get[x]ObjectsForEntity. Функция getEntityData() возвра+ щает данные, необходимые для создания объекта, так же как рассмотренные выше функции для обработки типов контактной информации. Ниже приведен код новых функций в файле class.DataManager.php:
//для краткости начало файла здесь не показывается
...
die("Невозможно получить данные для телефона $phoneID");
}
return pg_fetch_assoc($res);
}
public static function getEntityData($entityID) {
$sql = "SELECT * FROM entities WHERE entityid = $entityID"; $res = mysql_query($sql, DataManager::_getConnection()); if(! ($res && mysql_num_rows($res))) {
die("Failed getting entity $entityID");
}
return mysql_fetch_assoc($res);
}
?>
Чтобы добавить функции get[x]ObjectsForEntity, необходимо поместить сле+ дующий код в конец файла class.DataManager.php сразу после объявления функ+ ции getEntityData:
public static function getAddressObjectsForEntity($entityID) {
$sql = "SELECT addressid from entityaddress WHERE entityid = $entityID"; $res = mysql_query($sql, DataManager::_getConnection());
if(!$res) {
die("Невозможно получить адресные данные для записи $entityID");
}
if(mysql_num_rows($res)) { $objs = array();
while($rec = mysql_fetch_assoc($res)) { $objs[] = new Address($rec['addressid']);
}

546 Глава 13
return $objs;
}else {
return array();
}
}
public static function getEmailObjectsForEntity($entityID) {
$sql = "SELECT emailid from entityemail WHERE entityid = $entityID"; $res = mysql_query($sql, DataManager::_getConnection());
if(!$res) {
die("Невозможно получить email-адрес для записи $entityID");
}
if(mysql_num_rows($res)) { $objs = array();
while($rec = mysql_fetch_assoc($res)) { $objs[] = new EmailAddress($rec['emailid']);
}
return $objs;
}else {
return array();
}
}
public static function getPhoneNumberObjectsForEntity($entityID) { $sql = "SELECT phoneid from entityphone WHERE entityid = $entityID"; $res = mysql_query($sql, DataManager::_getConnection());
if(!$res) {
die("Невозможно получить номер телефона для записи $entityID");
}
if(mysql_num_rows($res)) { $objs = array();
while($rec = mysql_fetch_assoc($res)) { $objs[] = new PhoneNumber($rec['phoneid']);
}
return $objs;
}else {
return array();
}
}
Все эти функции принимают в качестве параметра идентификатор записи. Они выполняют запрос к базе данных, чтобы определить, существуют ли для указанной за+ писи номера телефонов, а также e+mail+ и почтовые адреса. Если это так, функции соз+ дают массив объектов EmailAddress, Address или PhoneNumber, передавая иден+ тификатор соответствующему конструктору. Затем созданный массив возвращается объекту Entity, где он сохраняется в соответствующей частной переменной.
Класс Entity выполняет всю основную работу, поэтому остается только реализо+ вать классы Individual и Organization. Создайте файл class.Individual.php
и введите в него следующий код:
<?php require_once('class.Entity.php');
require_once('class.Organization.php');
class Individual extends Entity {
public function __construct($userID) {

Работа с UML и классами 547
parent::__construct($userID);
$this->propertyTable['firstname'] = 'name1'; $this->propertyTable['lastname'] = 'name2';
}
public function __toString() {
return $this->firstname . ' ' . $this->lastname;
}
public function getEmployer() {
return DataManager::getEmployer($this->id);
}
public function validate() { parent::validate();
//специальная проверка данных частного лица
}
}
?>
Коротко и ясно. Класс Individual устанавливает несколько новых свойств, кото+ рые упрощают доступ к имени и фамилии частного лица и позволяют не использовать довольно неудобные свойства name1 и name2, определенные в классе Entity. Кроме того, в классе также определяется новый метод, getEmployer(), которому требуется новая функция в классе DataManager. Эта функция будет рассматриваться при опи+ сании класса Organization, код которого представлен ниже. Создайте файл с име+ нем class.Organization.php и введите в него следующий код:
<?php require_once('class.Entity.php'); require_once('class.Individual.php');
class Organization extends Entity {
public function __construct($userID) { parent::__construct($userID);
$this->propertyTable['name'] = 'name1';
}
public function __toString() { return $this->name;
}
public function getEmployees() {
return DataManager::getEmployees($this->id);
}
public function validate() {
parent::validate();
//специальная проверка данных организации
}
}
?>

548 Глава 13
Этот класс благодаря эффективности наследования также довольно прост. Объяв+ ляется свойство name, которое упрощает получение названия организации (свойство sname2 для организаций не используется).
Чтобы добавить в класс DataManager функции getEmployer() и getEmployee(), необходимо добавить следующий код в конец файла class.DataManager.php:
public static function getEmployer($individualID) {
$sql = "SELECT organizationid FROM entityemployee " . "WHERE individualid = $individualID";
$res = mysql_query($sql, DataManager::_getConnection());
if(! ($res && mysql_num_rows($res))) {
die("Невозможно получить данные о работодателе для $individualID");
}
$row = mysql_fetch_assoc($res);
if($row) {
return new Organization($row['organizationid']); } else {
return null;
}
}
public static function getEmployees($orgID) {
$sql = "SELECT individualid FROM entityemployee " . "WHERE organizationid = $orgID";
$res = mysql_query($sql, DataManager::_getConnection());
if(! ($res && mysql_num_rows($res))) {
die("Невозможно получить данные о работнике для $orgID");
}
if(pg_num_rows($res)) { $objs = array();
while($row = mysql_fetch_assoc($res)) {
$objs[] = new Individual($row['individualid']);
}
return $objs; } else {
return array();
}
}
Две эти функции зависят от присутствия таблицы entityemployee, которая по+ казана ниже:
CREATE TABLE entityemployee ( individualid int NOT NULL, organizationid int NOT NULL,
CONSTRAINT fk_entityemployee_individualid
FOREIGN KEY (individualid) REFERENCES entity(entityid), CONSTRAINT fk_entityemployee_organizationid
FOREIGN KEY (organizationid ) REFERENCES entity(entityid)
);
Эта таблица связывает частных лиц с организациями, в которых они работают. Соответствующие функции класса DataManager аналогичны уже описанным.
Чтобы заставить систему работать, необходима еще одна последняя функция ++++++
getAllEntitiesAsObjects(), метод класса DataManager, который перечисляет все записи в базе. Этот метод завершает всю работу, которую необходимо выполнять в объектах:

Работа с UML и классами 549
public static function getAllEntitiesAsObjects() { $sql = "SELECT entityid, type from entities";
$res = mysql_query($sql, DataManager::_getConnection());
if(!$res) {
die("Невозможно получить все записи");
}
if(mysql_num_rows($res)) { $objs = array();
while($row = mysql_fetch_assoc($res)) { if($row['type'] == 'I') {
$objs[] = new Individual($row['entityid']);
}elseif ($row['type'] == 'O') {
$objs[] = new Organization($row['entityid']);
}else {
die("Неизвестный тип записи {$row['type']}!");
}
}
return $objs;
}else {
return array();
}
}
Класс DataManager позволяет перечислить все записи с контактной информаци+ ей в системе. Он проверяет значение поля ctype в таблице entity, чтобы опреде+ лить, к какому типу относится запись ++++++ частное лицо или организация, а затем созда+ ет объект соответствующего типа и добавляет его в массив, который возвращает описываемая функция.
Использование системы
К этому моменту уже становится очевидной реальная эффективность объектно+ ориентированного подхода. В результате выполнения следующего кода (test.php) отображается подробный перечень всех контактных сведений в базе данных:
<?php
require_once('class.DataManager.php'); //включаем весь
//необходимый код
function println($data) { print $data . "<br>\n";
}
$arContacts = DataManager::getAllEntitiesAsObjects(); foreach($arContacts as $objEntity) {
if(get_class($objEntity) == 'Individual') {
print "<h1>Частное лицо - {$objEntity->__toString()}</h1>";
} else {
print "<h1>Организация - {$objEntity->__toString()}</h1>";
}
if($objEntity->getNumberOfEmails()) { //Есть e-mail-адреса. Выводим заголовок
print "<h2>Email-адреса</h2>";
for($x=0; $x < $objEntity->getNumberOfEmails(); $x++) { println($objEntity->emails($x)->__toString());
}
}
if($objEntity->getNumberOfAddresses()) { //Есть почтовые адреса.
print "<h2>Адреса</h2>";
for($x=0; $x < $objEntity->getNumberOfAddresses(); $x++) {


Работа с UML и классами 551
Используя всего лишь 36 строк кода, можно отобразить почти всю информацию по записям в системе. Здесь не показаны сведения о работодателях и работниках, это зада+ ние остается в качестве упражнения читателю. Строка, в которой вызывается функция get_class, должна подсказать, какой класс используется, т.е. какая функция вызывает+ ся ++++++ getEmployer() в классе Individual или getEmployees() в классе Organization.
Резюме
UML+диаграммы ++++++ важнейший инструмент для планирования сложных (и про+ стых) приложений. Правильно сформированные диаграммы позволяют документи+ ровать сложные системы более наглядным способом, чем это можно сделать с помо+ щью текстового описания. Использование диаграмм классов как части рутинного процесса разработки программного обеспечения значительно облегчает понимание того, как должны быть организованы классы и таблицы баз данных.
Используя все преимущества OO+возможностей PHP5, можно быстро разрабаты+ вать приложения и создавать легко поддерживаемый код, который будет обладать вы+ сокой степенью гибкости и расширяемости, а также сократить общее количество ко+ да, необходимого для реализации бизнес+требований.
Разделяя программную архитектуру на уровень бизнес+логики (класс Individual) и уровень доступа к данным (класс DataManager), можно легко изменять основной источник данных, структуру таблиц и запросов, не нарушая при этом остальную часть приложения. Объекты, ответственные за реализацию бизнес+логики, не за+ громождены механизмом доступа к данным, присутствие которого может запутать и усложнить бизнес+правила.

14
PEAR
Иногда программисту, начинающему новый проект, может показаться, что по+ ставленная перед ним задача слишком сложна и почти невыполнима.
Даже лучшие программисты часто испытывают трудности в поиске правильной начальной точки. В такой ситуации полезно помнить о том, что половина необходи+ мого для проекта кода, скорее всего, уже написана.
По мере того как проекты разработчика становятся более зрелыми, он понимает, что может повторно использовать большую часть кода из предыдущих разработок (это еще одна причина использовать модульный, объектно+ориентированный под+ ход). Разработка по типу ‘‘найти и заменить’’ за годы сэкономила тысячи часов рабо+ ты программистов.
Конечно, возможны ситуации, когда воспользоваться нечем. Разработка некото+ рых компонентов может занимать чрезвычайно много времени, не внося фактически никакого полезного вклада в ядро проекта. В такой ситуации может возникнуть син+ дром ‘‘чистого листа’’ и появиться желание выпить еще одну чашку кофе, прежде чем браться за работу.
К счастью, самая трудная задача выполнима: к вашим услугам репозиторий, в кото+ ром хранятся тысячи полезных, хорошо протестированных, повторно используемых PHP+компонентов, каждый из которых можно бесплатно использовать и включать
вразрабатываемые приложения.
Вэтой главе рассматривается репозиторий PEAR, а также объясняются причины его возникновения и описываются ситуации, в которых он может помочь. Прочтя ма+ териал главы, читатель узнает, когда следует, а когда не следует использовать репози+ торий. Самое главное ++++++ научиться идентифицировать наиболее подходящие к разра+ батываемому проекту компоненты и интегрировать их в приложение.