16297

Объектно-ориентированное программирование на РНР

Лабораторная работа

Информатика, кибернетика и программирование

ЛАБОРАТОРНАЯ РАБОТА №8 Объектноориентированное программирование на РНР PHP и ООП. Хотя РНР обладает общими объектноориентированными возможностями он не является полноценным ООязыком например таким как C или Java. В частности в РНР не поддерживаются следующие объ...

Русский

2013-06-20

44.84 KB

11 чел.

ЛАБОРАТОРНАЯ РАБОТА №8

Объектно-ориентированное программирование на РНР

PHP и ООП.

Хотя РНР обладает общими объектно-ориентированными возможностями, он не является полноценным ОО-языком (например, таким, как C++ или Java). В частности, в РНР не поддерживаются следующие объектно-ориентированные возможности: 

множественное наследование;

автоматический вызов конструкторов (если вы хотите, чтобы при конструировании объекта производного класса вызывался конструктор базового класса, вам придется вызвать его явно);

абстрактные классы;

перегрузка методов;

перегрузка операторов (это связано с тем, что РНР является языком со свободной типизацией);

закрытый и открытый доступ, виртуальные функции;

деструкторы.

Но и без всего перечисленного вы все равно сможете извлечь пользу из объектно-ориентированных возможностей, поддерживаемых РНР. Реализация ООП в РНР оказывает колоссальную помощь в модульном оформлении функциональности вашей программы. 

  1.  
    Классы, объекты и объявления методов.



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

Класс также можно рассматривать как тип данных, а объект — как переменную (по аналогии с тем, как переменная $counter относится к целому, а переменная $last_name — к строковому типу). Программа может одновременно работать с несколькими объектами одного класса как с несколькими переменными целого типа. Общий формат классов РНР приведен в листинге 1. 


Листинг 1. Объявление классов в РНР. 

class Class_name {

var $attribute_1;

//...

var $attribute_N;

function function1() {

//...

}

//...

function functionN() {

//...

}

} // end Class_name 


Подведем итоги: объявление класса должно начинаться с ключевого слова class (подобно тому, как объявление функции начинается с ключевого слова function). Каждому объявлению атрибута, содержащегося в классе, должно предшествовать ключевое слово var. Атрибуты могут относиться к любому типу данных, поддерживаемых в РНР. После объявлений атрибутов следуют объявления методов, очень похожие на типичные объявления функций. 

Методы часто используются для работы с атрибутами классов. При ссылках на атрибуты внутри методов используется специальная переменная $this. Синтаксис методов продемонстрирован в следующем примере: 



class Webpage {

var $bgcolor;

function setBgColor($color) {

$this->bgcolor = $color;

}

function getBgColor() {

return $this->bgcolor;

}

}

?> 


Переменная $this ссылается на экземпляр объекта, для которого вызывается метод. Поскольку в любом классе может существовать несколько экземпляров объектов, уточнение $this необходимо для ссылок на атрибуты, принадлежащие текущему объекту. При использовании этого синтаксиса обратите внимание на два обстоятельства: 

атрибут, на который вы ссылаетесь в методе, не нужно передавать в виде параметра функции;

знак доллара ($) ставится перед переменной $this, но не перед именем атрибута (как у обычной переменной).


3. Создание объектов и работа с ними.


Объекты создаются оператором new. Например, объект класса Webpage создается следующей командой: 

$some_page = new Webpage; 


Новый объект с именем $some_page обладает собственным набором атрибутов и методов, перечисленных в классе Webpage. Для изменения значения атрибута $bgcolor, принадлежащего этому конкретному объекту, можно воспользоваться определенным в классе методом setBgColor( ): 

$some_page->setBgColor("black"); 


Следует помнить, что РНР также позволяет явно получить значение атрибута указанием имен объекта и атрибута: 

$some_page->bgcolor="black"; 


Однако второй способ противоречит принципу инкапсуляции, и при работе с ООП поступать так не следует. 


4. Нарушение инкапсуляции.


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

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

Поскольку вы решили работать с атрибутами напрямую, вам теперь придется просматривать всю программу и везде, где происходят обращения к данным, вносить исправления в соответствии с новым интерфейсом. Задача весьма хлопотная, к тому же чревата риском внесения новых ошибок. 

А теперь давайте посмотрим, что произошло бы при работе с данными с использованием методов. Все, что вам пришлось бы сделать при переходе от массива к базе данных — перепрограммировать методы. Модификация автоматически распространяется на все точки программы, в которых присутствуют вызовы методов. 


5. Конструкторы.


Довольно часто при создании объекта требуется задать значения некоторых атрибутов. К счастью, разработчики технологии ООП учли это обстоятельство и реализовали его в концепции конструкторов. Конструктор представляет собой метод, который задает значения некоторых атрибутов (а также может вызывать другие методы). Конструкторы вызываются автоматически при создании новых объектов. Чтобы это стало возможным, имя метода-конструктора должно совпадать с именем класса, в котором он содержится. Пример конструктора приведен в листинге 2. 


Листинг 2. Использование конструктора. 


class Webpage {

var $bgcolor;

function Webpage($color) {

$this->bgcolor = $color;

}

}

// Вызвать конструктор класса Webpage

$page = new Webpage("brown");

?> 


Раньше создание объекта и инициализация атрибутов выполнялись раздельно. Конструкторы позволяют выполнить эти действия за один этап. 

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

$page = new Webpage; 


Во-вторых, объект можно создать при помощи конструктора, определенного в классе, — в этом случае вы создаете объект класса Webpage и присваиваете значение его атрибуту bgcolor: 

$page = new Webpage("brown"); 


6. Деструкторы.


Как упоминалось ранее, в РНР отсутствует непосредственная поддержка деструкторов. Тем не менее, вы можете легко имитировать работу деструктора, вызывая функцию РНР unset( ). Эта функция уничтожает содержимое переменной и возвращает занимаемые ею ресурсы системе. С объектами unset( ) работает так же, как и с переменными. Допустим, вы работаете с объектом $Webpage. После завершения работы с этим конкретным объектом вызывается функция: 

unset($Webpage); 


Эта команда удаляет из памяти все содержимое $Webpage. Действуя в духе инкапсуляции, можно поместить вызов unset( ) в метод с именем destroy( ) и затем вызвать его: 

$Webpage->destroy( ); 


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


7. Простое и иерархическое наследование.


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


class Class_name2 extends Class_name1 { 

объявления атрибутов; 

объявления методов; 

} 


Ключевое слово extends говорит о том, что класс Class_name2 наследует все характеристики класса Class_name1. 

Помимо возможности многократного использования кода, наследование обладает еще одним важным преимуществом — снижается вероятность ошибок при модификации программы. Например, в иерархии, изображенной на рис. 6.1, изменения в классе "автомобиль" никак не отразятся на коде (и данных) класса "самолет", и наоборот. 

Вызов конструктора производного класса не приводит к автоматическому вызову конструктора базового класса. 

class Class_name2 extends Class_name1 {

//объявления атрибутов;

//объявления методов;

} 


В листинге 3 приведены классы, моделирующие иерархию транспортных средств. 


Листинг 3. Представление различных типов транспортных средств при помощи наследования. 


// Транспортное средство

class Vehicle {

var $model;

var $current_speed;

function setSpeed($mph) {

$this->current_speed = $mph;

}

function getSpeed() {

return $this->current_speed;

}

}

// Автомобиль

class Auto extends Vehicle { 

var $fuel_type;

function setFuelType($fuel) {

$this->fuel_type = $fuel;

}

function getFuelType() {

return $this->fuel_type;

}

}

// Самолет

class Airplane extends Vehicle {

var $wingspan;

function setWingSpan($wingspan) {

$this->wingspan = $wingspan;

}

function getWingSpan() {

return $this->wingspan;

}

}

?> 


Объекты этих классов создаются следующим образом: 

$tractor = new Vehicle;

$gulfstream = new Airplane; 


Приведенные команды создают два объекта. Первый объект, $tractor, относится к классу Vehicle. Второй объект, $gulfstream, относится к классу Airplane и потому обладает как общими характеристиками класса Vehicle, так и уточненными характеристиками класса Airplаne. 

Ситуация, при которой класс наследует свойства нескольких родительских классов, называется множественным наследованием. К сожалению, в РНР множественное наследование не поддерживается. Например, следующая конструкция невозможна в РНР: 

class Airplane extends Vehicle extends Building... 


7. Многоуровневое наследование.


С увеличением размеров и сложности программ может возникнуть необходимость в многоуровневом наследовании. Иначе говоря, класс будет наследовать свои свойства от других классов, которые, в свою очередь, будут наследовать от третьих классов и т. д. Многоуровневое наследование развивает модульную структуру программы, обеспечивая простоту сопровождения и более четкую логическую структуру. Скажем, при использовании примера с транспортными средствами в большой программе может появиться необходимость в дополнительном разбиении на субклассы суперкласса Vehicle, продолжающем логическое развитие иерархии. Например, транспортные средства можно дополнительно разделить на наземные, морские и воздушные, чтобы суперкласс специализированных субклассов выбирался в зависимости от среды, в которой перемещается данное транспортное средство. 

Краткий пример, приведенный в листинге 4, подчеркивает некоторые важные аспекты многоуровневого наследования в РНР. 


Листинг 4. Многоуровневое наследование 


class Vehicle {

//Объявления атрибутов...

//Объявления методов...

}

class Land extends Vehicle {

//Объявления атрибутов...

//Объявления методов...

}

class Car extends Land {

//Объявления атрибутов...

//Объявления методов...

}

$nissan = new Car;

?> 


Объект $nissan содержит все атрибуты и методы классов Саr, Land и Vehicle. Как видите, программа получается исключительно модульной. Допустим, когда-то в будущем вы захотите добавить в класс Land новый атрибут. Нет проблем: внесите соответствующие изменения в класс Land, и этот атрибут немедленно становится доступным для классов Land и Саr, не влияя на функциональность других классов. Таким образом, модульность кода и гибкость относятся к числу основных преимуществ ООП. 


8. Абстрактные классы.


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

В РНР отсутствует прямая поддержка абстрактных классов, однако существует простое обходное решение — достаточно определить в "абстрактном" классе конструктор и включить в него вызов die( ). Вернемся к классам из листинга 4. Скорее всего, вам никогда не придется создавать экземпляры классов Land и Vehicle, поскольку они не могут представлять физические объекты. Для представления реальных объектов (например, автомобилей) следует создать класс, производный от этих классов. Следовательно, чтобы предотвратить возможное создание объектов классов Land и Vehicle, необходимо включить в их конструкторы вызовы die( ), как показано в листинге 5. 


Листинг 5. Создание абстрактных классов. 


class Vehicle {

//Объявления атрибутов...

function Vehicle() {

die ("Cannot create Abstract Vehicle class!");

}

//Объявления других методов...

}

class Land extends Vehicle {

//Объявления атрибутов...

function Land() {

die ("Cannot create Abstract Land class!");

}

//Объявления других методов. 

}

class Car extends Land {

//Объявления атрибутов...

//Объявления методов...

}

?> 


Попытка создания экземпляра этих абстрактных классов приведет к выдаче сообщения об ошибке и завершению программы. 


9. Перегрузка методов.


Перегрузкой методов называется определение нескольких методов с одинаковыми именами, но разным количеством или типом параметров. Как и в случае с абстрактными классами, в РНР эта возможность не поддерживается, но существует простое обходное решение, приведенное в листинге 6. 


Листинг 6. Перегрузка методов. 


class Page {

var $bgcolor;

var $textcolor;

function Page() {

// Определить количество переданных аргументов

// и вызвать метод с нужным именем

$name = "Page".func_num_args();

// Call $name with correct number of arguments passed in

if ( func_num_args() == 0 ) :

$this->$name();

else :

$this->$name(func_get_arg(0));

endif;

}

function Page0() {

$this->bgcolor = "white";

$this->textcolor = "black";

print "Created default page";

}

function Page1($bgcolor) {

$this->bgcolor = $bgcolor;

$this->textcolor = "black";

print "Created custom page";

}

}

$html_page = new Page("red");

?> 


В этом примере при создании нового объекта с именем $html_page передается один аргумент. Поскольку в классе был определен конструктор по умолчанию (Раgе( )), вызывается именно он. Однако конструктор по умолчанию всего лишь выбирает, какому из конструкторов (Page0( ) или Page1( )) следует передать управление. При выборе конструктора используются функции func_num_args( ) и func_get_arg( ), которые, соответственно, определяют количество аргументов и читают эти аргументы. 

Конечно, такое решение вряд ли можно назвать полноценной перегрузкой, но оно подойдет для тех, кто не может жить без этого важного аспекта ООП. 


10. Функции для работы с классами и объектами.


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


get_class_methods( ).


Функция get_class_methods( ) возвращает массив имен методов класса с заданным именем. Синтаксис функции get_class_methods( ): 

array get_class_methods (string имя_класса) 

Простой пример использования get_class_methods( ) приведен в листинге 7. 


Листинг 7. Получение списка методов класса. 


//...

class Airplane extends Vehicle {

var $wingspan;

function setWingSpan($wingspan) {

$this->wingspan = $wingspan;

}

function getWingSpan() {

return $this->wingspan;

}

}

$cls_methods = get_class_methods("Airplane");

// Массив $cls_methods содержит имена всех методов,

// объявленных в классах "Airplane" и "Vehicle"

?> 


Как видно из листинга 7, функция get_class_methods( ) позволяет легко получить информацию обо всех методах, поддерживаемых классом. 


get_class_vars( ).


Функция get_class_vars( ) возвращает массив имен атрибутов класса с заданным именем. Синтаксис функции get_class_vars( ): 

array get_class_vars (string имя_класса) 

Пример использования get_class_vars( ) приведен в листинге 8. 


Листинг 8. Получение списка атрибутов класса функцией get_class_vars( ). 


class Vehicle {

var $model;

var $current_speed;

}

class Airplane extends Vehicle {

var $wingspan = 200; 

}

$a_class = "Airplane";

$attribs = get_class_vars($a_class);

// $attribs = array ("model" => "",

// "current_speed" => "",

// "wingspan" => "200");

?> 


get_object_vars( ).


Функция get_object_vars( ) возвращает ассоциативный массив с информацией обо всех атрибутах объекта с заданным именем. Синтаксис функции get_object_vars( ): 

array get_object_vars (object имя_обьекта) 

Пример использования функции get_object_vars( ) приведен в листинге 9. 


Листинг 9. Получение информации о переменных объекта. 


class Vehicle {

var $wheels;

}

class Land extends Vehicle {

var $engine;

}

class car extends Land {

var $doors;

function car($doors, $eng, $wheels) {

$this->doors = $doors;

$this->engine = $eng;

$this->wheels = $wheels;

}

function get_wheels() {

return $this->wheels;

}

}

$toyota = new car(2,400,4);

$vars = get_object_vars($toyota);

while (list($key, $value) = each($vars)) :

print "$key ==> $value 
";

endwhile;

// Выходные данные:

// wheels ==> 2

// engine ==> 400

// doors ==> 2

?> 


Функция get_object_vars( ) позволяет быстро получить всю информацию об атрибутах конкретного объекта и их значениях в виде ассоциативного массива. 


method_exists( ).


Функция method_exists( ) проверяет, поддерживается ли объектом метод с заданным именем. Если метод поддерживается, функция возвращает TRUE, в противном случае возвращается FALSE. Синтаксис функции method_exists( ): 

bool method_exists (object имя_обьекта. string имя_метода) 

Пример использования метода method_exists( ) приведён в листинге 10. 


Листинг 10. Проверка поддержки метода объектом при помощи функции method_exists(). 


class Vehicle {

//...

}

class Land extends Vehicle {

var $fourWheel;

function setFourWheelDrive() {

$this->fourWeel = 1;

}

}

// Создать объект с именем $саr

$car = new Land;

// Если метод "fourWheelDrive" поддерживается классом "Land"

// или "Vehicle", вызов method_exists возвращает TRUE;

// в противном случае возвращается FALSE.

// В данном примере method_exists() возвращает TRUE.

if (method_exists($car, "setfourWheelDrive")) :

print "This car is equipped with 4-wheel drive";

else :

print "This car is not equipped with 4-wheel drive";

endif;

?> 


В листинге 10 функция method_exists ( ) проверяет, поддерживается ли объектом $car метод с именем setFourWheelDrive( ). Если метод поддерживается, функция возвращает логическую истину и фрагмент выводит соответствующее сообщение. В противном случае возвращается FALSE и выводится другое сообщение. 


get_class( ).


Функция get_class( ) возвращает имя класса, к которому относится объект с заданным именем. Синтаксис функции get_class( ): 

string get_class(object имя_объекта); 

Пример использования get_class( ) приведен в листинге 11. 


Листинг 11. Получение имени класса функцией get_class( ). 


class Vehicle {

//...

}

class Land extends Vehicle {

//...

}

// Создать объект с именем $саr 

$car = new Land;

// Переменной $class_a присваивается строка "Land"

$class_a = get_class($car);

?> 


В результате переменной $class_a присваивается имя класса, на основе которого был создан объект $саr. 


get_parent_class( ).


Функция get_parent_class( ) возвращает имя родительского класса (если он есть) для объекта с заданным именем. Синтаксис функции get_parent_dass( ): 

string get_parent_class (object имя_обьекта); 

Листинг 12 демонстрирует использование get_parent_class( ). 


Листинг 12. Получение имени родительского класса функцией get_parent_class( ). 


class Vehicle {

//...

}

class Land extends Vehicle {

//...

}

// Создать объект с именем $саr 

$car = new Land;

// Переменной $parent присваивается строка "Vehicle"

$parent = get_parent_class($car);

?> 


Как и следовало ожидать, при вызове get_parent_class( ) переменной $parent будет присвоена строка "Vehicle". 


is_subclass_of( ).


Функция is_subclass_of( ) проверяет, был ли объект создан на базе класса, имеющего родительский класс с заданным именем. Функция возвращает TRUE, если проверка дает положительный результат, и FALSE в противном случае. Синтаксис функции is_subclass_of( ): 


bool is_subclass_of (object объект, string имя_класса) 

Использование is_subclass_of( ) продемонстрировано в листинге 13. 


Листинг 13. Использование функции is_subdass_of( ). 


class Vehicle {

//...

}

class Land extends Vehicle {

//...

}

$auto = new Land;

// Переменной $is_subclass присваивается TRUE

$is_subclass = is_subclass_of($auto, "Vehicle");

?> 


В листинге 13 переменной $is_subclass( ) присваивается признак того, принадлежит ли объект $auto к субклассу родительского класса Vehicle. В приведенном фрагменте $auto относится к классу Vehicle; следовательно, переменной $is_subclass( ) будет присвоено значение TRUE. 


get_declared_classes( ).


Функция get_declared_classes( ) возвращает массив с именами всех определенных классов (листинг 14). Синтаксис функции get_declared_classes( ): 

array get_declared_classes( ) 


Листинг 14. Получение списка классов функцией get_declared_classes( ). 


class Vehicle {

//...

}

class Land extends Vehicle {

//...

}

$declared_classes = get_declared_classes();

// $declared_classes = array("Vehicle", "Land")

}

?> 

Задания




  1.  
    Создать класс, описывающий web-страницу. Класс содержит функции, отображающие различные ее части. Создать производные от этого класса классы, описывающие различные модификации страницы. Написать скрипт, выводящий страницы с помощью свойств и методов этих классов.
  2.  
    Создать класс, содержащий функции для работы с текстовым файлом (создание файла, удаление файла). Написать скрипт, использующий функции этого класса.
  3.  
    Создать класс, содержащий функции для выполнения арифметических действий (сложение, вычитание, умножение и деление) над двумя числами. Написать скрипт, использующий эти функции.
  4.  
    Создать класс, содержащий функции работы со строкой (определение длины строки; n-го символа строки; букв, входящих в строку более одного раза) . Написать скрипт, использующий эти функции.
  5.  
    Создать класс, содержащий следующие функции для функционирования гостевой книги: добавить сообщение, вывести все сообщения, проверить наличие пустых полей. Написать скрипт, использующий эти функции.


 

А также другие работы, которые могут Вас заинтересовать

67466. ТРАНСПОРТНА ЗАДАЧА. ЗНАХОДЖЕННЯ ПОЧАТКОВОГО ОПОРНОГО ПЛАНУ МЕТОДОМ МІНІМАЛЬНОГО ЕЛЕМЕНТУ 329.5 KB
  На кожному кроці роботи алгоритму з числа незаповнених клітинок таблиці транспортної задачі обереться клітинку з мінімальним значенням тарифу, заповнюємо її та виключаємо з розгляду стовпчик чи рядок в якому знаходиться ця клітинка...
67470. Исследование модели кольцевой ЛВС с маркерным доступом 1.4 MB
  Цель работы: Исследование особенностей построения и функционирования кольцевой ЛВС с маркерным методом доступа и определение основных характеристик сети. Определить основные характеристики ЛВС основе исследования аналитической модели сети.
67471. Выбор рациональной длины пакета сети ЭВМ 149.5 KB
  Исходные данные средняя длина передаваемого сообщения: l = 5000 бит; длина заголовка пакета: С = 320 бит; коэффициент учитывающий системные издержки на сборку сообщений: К1 = 15; время изменения направления передачи t1n = 0 t2n = 004; номинальная...
67472. Функционирование мостов и коммутаторов на основе протокола канального уровня STP стека протоколов TCP/IP 119.5 KB
  Изучение основных принципов работы мостов и коммутаторов в сетях ЭВМ на основе протокола STP. Получение знаний по принципам построения и алгоритмам функционирования мостов и коммутаторов в сетях ЭВМ и навыков по устранению активных петель в сети при помощи протокола STP.