web - tec / PHP 5 для начинающи
.pdfВведение в объектно*ориентированное программирование 513
Интерфейс объявляется с помощью синтаксиса, аналогичного объявлению класса, за исключением того, что вместо ключевого слова class используется слово interface. Как правило, интерфейс не имеет переменных экземпляра и в нем не описана реализация функций экземпляра.
Поскольку реализация не определена, такие функции объявляются как абстракт+ ные (abstract). Ключевое слово abstract сообщает PHP, что любой класс, реали+ зующий данный интерфейс, должен обеспечить реализацию соответствующих функ+ ций. Если в классе, реализующем интерфейс, не реализованы все абстрактные функции, то PHP+интерпретатор генерирует ошибку времени выполнения. Выбороч+ ная реализация некоторых абстрактных методов не допускается ++++++ необходимо реа+ лизовать все абстрактные методы.
Абстрактный метод ++++++ метод, для которого в интерфейсе не предусмотрена реализа+ ция. Если какой+либо метод класса объявлен как абстрактный, то сам класс также должен быть объявлен как абстрактный (в объявлении класса перед словом class должно при+ сутствовать ключевое слово abstract). Невозможно создать объект самого абстрактно+ го класса ++++++ абстрактный класс должен иметь подклассы, которые обеспечивают кон+ кретную реализацию абстрактных методов. Обратите внимание, что объявления абстрактных методов не содержат фигурных скобок и заканчиваются точкой с запятой.
Как это работает
Интерфейс Openable представляет собой соглашение с остальными частями при+ ложения о том, что класс, реализующий данный интерфейс, обеспечит два метода ++++++
open() и close(), ++++++ которые не принимают параметров. Данное согласованное множество методов позволяет передавать различные объекты в одну и ту же функ+ цию. При этом отношение наследования между ними не требуется.
Создайте файл class.Door.php:
<?php
require_once('interface.Openable.php');
class Door implements Openable {
private $_locked = false;
public function open() {
if($this->_locked) {
print "Дверь не открывается. Она заперта.";
}else {
print "скрип...<br>";
}
}
public function close() { print "Хлоп!!<br>";
}
public function lockDoor() { $this->_locked = true;
}
public function unlockDoor() { $this->_locked = false;
}
}
?>
514 Глава 12
и файл class.Jar.php:
<? require_once('interface.Openable.php');
class Jar implements Openable { private $contents;
public function __construct($contents) { $this->contents = $contents;
}
public function open() { print "банка открыта<br>";
}
public function close() { print "банка закрыта<br>";
}
}
?>
Для того чтобы можно было использовать эти классы, создайте новый файл testOpenable.php в том же каталоге:
<?php require_once('class.Door.php'); require_once('class.Jar.php');
function openSomething(Openable $obj) { $obj->open();
}
$objDoor = new Door(); $objJar = new Jar("желе");
openSomething($objDoor); openSomething($objJar);
?>
Так как оба класса Door и Jar реализуют интерфейс Openable, объекты обоих этих классов можно передавать функции openSomthing(). Поскольку она принимает только объекты, реализующие интерфейс Openable, можно утверждать, что внутри этой функции можно вызывать методы open() и close(). Однако пытаться использовать свойство contents класса Jar или методы lock() либо unlock() класса Door внутри функции openSomething() не следует, потому что это свойство и методы не являются частью интерфейса. Соглашение интерфейса гарантирует только то, что в реализую+ щем данный интерфейс классе будут присутствовать методы open() и close().
Используя в приложении интерфейсы, можно заставить взаимодействовать абсо+ лютно разные и несвязанные друг с другом объекты, гарантируя при этом, что их взаимодействие будет регламентировано спецификацией интерфейса.
Инкапсуляция
Как уже отмечалось в этой главе ранее, объекты позволяют скрывать подробности их реализации от своих пользователей. Например, пользователь не должен знать, где класс Volunteer сохраняет информацию, необходимую для вызова метода signup (в базе данных, в неструктурированном текстовом файле, в XML+файле), или использует
Введение в объектно*ориентированное программирование 515
иной механизм хранения данных. Аналогично, ему не обязательно знать, где внутри объекта хранится информация о добровольцах ++++++ в скалярных переменных, в массиве или даже в другом объекте. Возможность скрывать подробности реализации называ+ ется инкапсуляцией (encapsulation). Вообще инкапсуляция подразумевает две идеи: за+ щита внутренних данных класса от внешнего по отношению к данному классу кода и сокрытие деталей реализации.
Слово ‘‘инкапсулировать’’ буквально означает ‘‘помещать что+либо в капсулу или внешний контейнер’’. Хорошо организованный класс обеспечивает полноценную внешнюю оболочку вокруг своих внутренних данных и предоставляет внешнему коду интерфейс, который полностью отделен от деталей внутренней реализации класса. Это дает два преимущества: во+первых, можно в любое время изменять детали реали+ зации, не влияя при этом на код, использующий данный класс. Во+вторых, поскольку внешний по отношению к данному классу код не может случайно незаметно изменить состояние или свойства объекта данного класса, можно быть уверенным, что состоя+ ние объекта и значения его свойств являются достоверными и имеют смысл.
Выше уже говорилось о том, что переменные экземпляра класса и его функции ха+ рактеризуются видимостью (visibility) (извне класса). Частные переменные и функции экземпляра класса не доступны коду вне этого класса и используются для внутренней реализации. Защищенные переменные и функции экземпляра класса видимы только для самого класса и его подклассов. Открытые (или общедоступные) переменные
ифункции экземпляра класса могут использоваться как внутренним кодом класса, так
икодом за его пределами.
Вообще говоря, все внутренние переменные класса должны быть объявлены как ча+ стные (с ключевым словом private). Любой доступ к этим переменным извне класса должен осуществляться посредством общедоступных методов. Вы ведь не позволяете всем, кто предлагает вам попробовать новое блюдо, заталкивать его вам прямо в желу+ док. Для этого есть весомая причина ++++++ хочется самому посмотреть на это блюдо и ре+ шить, стоит ли его есть. Аналогично, если объект позволяет внешнему коду изменять его свойства или иначе влиять на его внутренние данные, то инкапсуляция доступа к этим данным в общедоступной функции (и сохранение внутренних данных частными) дает возможность оценить изменения, а затем принять или отвергнуть их.
Например, при создании банковского приложения, которое обрабатывает инфор+ мацию о счетах клиентов, можно использовать объект класса Account, имеющий свой+ ство totalBalance (итоговый баланс), а также методы makeDeposit (сделать вклад)
иmakeWithdrawal (снять деньги). Свойство totalBalance должно быть доступным только для чтения. Единственный способ повлиять на баланс ++++++ снять деньги или сде+ лать вклад. Если бы переменная totalBalance была реализована как общедоступная переменная экземпляра, то можно было бы написать код, который изменял бы значение данной переменной без фактического внесения или снятия денег со счета. Очевидно, что такая ситуация крайне нежелательна для банка. Поэтому данное свойство реализо+ вано в виде частной переменной экземпляра, а метод getTotalBalance возвращает значение этой переменной. То, что переменная, в которой хранится итоговый баланс, объявлена как частная, не позволяет манипулировать ею непосредственно. Поскольку повлиять на баланс могут только общедоступные методы makeWithdrawal и makeDeposit, пользователь вынужден внести вклад, чтобы увеличить сумму на своем счете.
Инкапсуляция внутренних данных и реализации методов позволяет объектно+ ориентированным программным системам защищать свои данные и управлять досту+ пом к ним, а также скрывать детали реализации, создавая, таким образом, гибкие
истабильные приложения.
Введение в объектно*ориентированное программирование 517
абстрактного класса, только неабстрактный подкласс данного класса может порождать объекты.
В функциях и методах можно использовать контроль типов для принимаемых па+ раметров. Теперь можно указать класс для параметров функции, которая ожидает передачи ей объекта. Например, функция function foo(Bar $objBar) {...
гарантирует, что типом данных параметра $objBar будет объект класса Bar.
Резюме
В данной главе рассматривались понятия объектно+ориентированного програм+ мирования. Класс был представлен как чертеж для создания объектов. Объекты пред+ ставляют собой существующие во время выполнения программы данные и функции, созданные из определения класса. Объекты имеют характеристики, которые называ+ ются свойствами, и поведение ++++++ методы. Свойства, по сути, являются переменными, а методы функциями.
Некоторые классы имеют общего родителя. Когда класс объявляется как подтип ро+ дительского класса, он наследует методы и свойства последнего. Существует возмож+ ность переопределять унаследованные методы. Эти методы в случае необходимости можно реализовать заново, а можно продолжать использовать их родительскую реализа+ цию, но добавить в подкласс некоторые особенности (или вообще не подменять методы).
Инкапсуляция ++++++ важное понятие объектно+ориентированного программирова+ ния. Она означает способность класса ограничивать доступ к своим внутренним пе+ ременным и ограждать детали своей внутренней реализации от пользователей. Суще+ ствует три уровня видимости данных и функций класса: частные члены, которые могут использоваться только во внутренних операциях класса; защищенные члены, которые видимы подклассам; общедоступные члены, которые могут использоваться кодом за пределами самого класса.
С введением пятой версии PHP и Zend Engine 2 поддержка объектно+ориентиро+ ванных возможностей была радикально пересмотрена. Новые функции и значительное повышение производительности делают PHP настоящим объектно+ориентированным языком программирования.
Упражнения
1.В чем разница между классом и объектом?
2.Объясните идею наследования и дайте пример того, когда его следует исполь+ зовать, не повторяя при этом примеров, рассмотренных в данной главе.
3.Опишите полезные особенности интерфейса и его практическое применение в программной архитектуре. Чем интерфейс отличается от наследуемого класса? Приведите дополнительные примеры возможного применения интерфейсов.
13
Работа с UML и классами
Одним из самых полезных инструментов для моделирования объектно+ориентирован+ ных программ является UML (Unified Modeling Language ++++++ унифицированный язык моделирования). Данная глава представляет собой введение в UML; в ней объясняет+ ся, почему этот язык стоит использовать, как различные UML+диаграммы способст+ вуют разработке PHP+приложений, а также описывается доступное программное обеспечение, позволяющее PHP+программистам создавать UML+диаграммы. Созда+ ваемые в качестве учебных примеров UML+диаграммы в конце главы составят полно+ масштабный пример приложения: диспетчера контактов.
Диспетчер контактов предназначен для управления данными об организациях и частных лицах, а также позволяет пользователям просматривать и редактировать контактную информацию этих объектов. На примере этой программы обсуждаются различные проблемы, связанные с созданием работоспособных объектно+ориенти+ рованных приложений. Попутно в ходе этой дискуссии иллюстрируются главные принципы, лежащие в основе идеи объектного ориентирования, такие как повторное использование кода, инкапсуляция и абстракция.
Унифицированный язык моделирования
Громоздкая терминология OO+программирования может сильно затруднять пись+ менное описание множества объектов. Вместе с тем тщательное планирование и четкое документирование объектов является ключевым фактором для успешного создания приложения. Это особенно справедливо в случае крупных приложений, в которых используются десятки объектов. Однако как сгенерировать такую документацию, ко+ торая была бы доступной для тех, кто должен ее читать? Как говорится, одна картина стоит тысячи слов. UML (унифицированный язык моделирования) представляет со+ бой спецификацию, описывающую стандартный процесс создания и визуализации объектно+ориентированных программных систем.
Работа с UML и классами 519
UML ++++++ язык, но не в обычном смысле этого слова. Это система для представления в визуальной форме классов и взаимодействия между ними. В данной системе исполь+ зуются стандартизированные обозначения для построения полной графической мо+ дели приложения. В UML используются изображения для очевидного обозначения комплексных взаимосвязей классов, а также создаются документы, которые впослед+ ствии помогают понять это приложение и возможности его использования и моди+ фикации (это полезно как другим людям, так и самому разработчику по прошествии некоторого времени с момента первоначального выпуска приложения). До опублико+ вания UML+спецификации существовало несколько систем, представляющих анало+ гичные идеи. В UML+спецификацию вошли лучшие черты каждой из этих систем. В результате был сформирован единый стандарт, который отверг предыдущие систе+ мы и установил обобщенный визуальный язык.
В текущей версии UML+спецификации определено 12 различных типов диаграмм, которые описывают структуру, поведение или организацию приложения. В этой главе подробно рассматривается только одна из этих диаграмм, диаграмма классов. Кратко представлены несколько других видов диаграмм. Полное описание UML вы+ ходит за рамки данной книги; доступно множество отличных ресурсов, позволяю+ щих подробнее изучить UML, включая книгу Освой самостоятельно UML 2 за 24 часа (ИД ‘‘Вильямс’’, 2005 г.)
Зачем использовать UML?
Многие разработчики программного обеспечения озабочены необходимостью написания документации. Часто документация предназначается для клиентов, спе+ циалистов по развитию бизнеса и остального нетехнического персонала, и создание такой документации отвлекает разработчиков от прямых обязанностей ++++++ написания кода. Более того, документы, ориентированные главным образом на разработчиков, такие как технические спецификации, часто остаются нетронутыми и непрочитан+ ными после создания. UML+диаграммы задуманы как работоспособное средство для описания проекта, и их создание на самом деле приносит разработчику практическую выгоду во время всего процесса разработки, а не только на стадии планирования.
UML+диаграммы позволяют разработчику перенести на бумагу идеи, касающиеся архитектуры приложения, при этом не требуется тратить время на написание пояс+ нительных текстов. Во время планирования проекта диаграммы упрощают визуаль+ ное воспроизведение крупномасштабных изменений программной архитектуры, по+ зволяя разработчику не заботиться в это время о коде. Диаграммы предоставляют ‘‘носитель’’, посредством которого архитекторы программного обеспечения могут передавать свои идеи тем, кто будет их реализовывать. Но более всего впечатляет то, что в зависимости от инструмента, который используется для создания диаграмм, на базе этих диаграмм фактически можно генерировать большой объем кода для классов; иными словами, разработчик может буквально ‘‘нарисовать’’ код.
Инструменты для создания UML-диаграмм
Существуют десятки приложений, как с открытым исходным кодом, так и коммер+ ческих, которые способны генерировать UML+диаграммы. К сожалению, поддержка автоматического создания PHP+кода в настоящий момент крайне ограничена. Ниже кратко описывается несколько стоящих внимания инструментов.