Warning: Cannot use a scalar value as an array in /home/admin/public_html/forum/include/fm.class.php on line 757

Warning: Invalid argument supplied for foreach() in /home/admin/public_html/forum/include/fm.class.php on line 770
Форумы портала PHP.SU :: Версия для печати :: Урок №15 - Объекто ориентированное программирование
Форумы портала PHP.SU » PHP » Уроки php » Урок №15 - Объекто ориентированное программирование

Страниц (1): [1]
 

1. Champion - 26 Сентября, 2009 - 10:32:40 - перейти к сообщению
Итак, что такое Объектно ориентированное программирование. Вам скорее всего известно, что часто повторяющиеся участки кода, которые приходится выполнять могократно с различными параметрами, очень удобно оформить в виде функции. А теперь представьте себе, что вы, скажем, моделируете движение автомобилей по дороге. Каждый автомобиль движется, обладает своими характеристиками, изменяющимися в каждый момент времени. Без применения ООП, вы бы создали отдельные массивы свойств, применяли бы к ним свои функции. Терпимо. И вдруг на горизонте появляется грузовик. У него, в отличие от других, есть кузов. А вон старенький Запорожец. У него нет магнитолы. Таким образом, мы уже не можем сказать, что первый элемент каждого массива относится к первой машине, второй - ко второй и так далее. Где-то что-то своё. Да, еще и память же надо подо всё это выделить, хотя, в PHP нет этой проблемы. Мы можем придумать различные ухищрения, организовать еще кучу массивов и добиться результата, но получатеся сложно и изменения отслеживать и вносить в такой код будет неприятно и сложно. Нам гораздо удебнее сделать все эти автомобили объектами. Объет - это переменная особого типа. Этот тип называтся класом. Класс включает в себя войства (цвет, вес) и методы (передвинуться, побибикать). Свойства - это переменные; методы - функции.
Собрав свойства и методы объекта в одну структуру данных, нам не только будет удобно обслуживать такой код, мы сможем использовать наш класс и в других проектах. Но и это еще не всё. Как пишут в учебниках, ООП основано на трёх слонах: инкапсуляция, наследование и полиморфизм.
Прежде, чем дать определения, пора уже начинать четко представлять, о чем пойдет речь, поэтому пример.
PHP:
скопировать код в буфер обмена
  1. <?PHP
  2.         class my_first {
  3.                 public $prop;
  4.                 function set_prop($value) {$this->prop = $value;}
  5.         }
  6.        
  7.         $obj = new my_first;
  8.         $obj->set_prop('123');
  9.         echo $obj->prop;
  10. ?>

Здесь мы описали класс my_first. В нем свойтсво "prop" и метод set_prop, который задает значение свойству. Чтобы создать объект, в PHP используется оператор new. Из кода видно и как работает метод, и как обратиться к свойтсву. Внутри класса используется переменная $this. Через нее мы обращаемся к свойствам и методам конкретного объекта.
Интересное cлово public. Кроме него есть еще private и protected. Эти слова мы можем использовать перед описанием свойтва или метода, и означают они следующее:
public - свойство (или метод) доступен на прямую из любого места программы.
private. - свойство (или метод) доступен только методам текущего класса.
protected - свойство (или метод) доступен только классу и его наследникам (об этом чуть позже).
Если ни одно из этих слов не указано, то по умолчанию уровень доступа будет public.
Посмотрим пример:
PHP:
скопировать код в буфер обмена
  1. <?PHP
  2.         class first {
  3.                 private $priv = 1;
  4.                 protected $prot = 2;
  5.                 public $pub = 3;
  6.                
  7.                 function set_props() {
  8.                         $this->pub = 2; // OK
  9.                         $this->prot = 4; // OK
  10.                         $this->priv = 1; // OK
  11.                 }
  12.         }
  13.        
  14.         class second extends first {
  15.                 function view() {
  16.                         echo $this->pub, // 3
  17.                         $this->prot, // 2
  18.                         $this->priv; // А тут мы ничего не увидим: это свойство родительского класса приватное, и здесь оно не видно.
  19.                 }
  20.         }
  21.        
  22.         $obj = new first;
  23.         echo $obj->pub, // 3
  24.                 $obj->prot, // Ошибка: не удается получить доступ к защищенному свойтсву
  25.                 $obj->priv; // Ошибка: не удается получить доступ к приватному свойтсву
  26. ?>


Теперь те самые три слона.
Инкапсуляция - такое замечательное свойство, которое не дает менять на прямую определенные свойства объекта, а позволяет это сделать только специально написанными методами самого класса. Это позволяет избежать при грамотной реализации некорректных значений в свойствах класса. Опишем объект Автомобиль и применим в нём инкапсуляцию
PHP:
скопировать код в буфер обмена
  1. <?PHP
  2.         class avto {
  3.                 protected $color, $speed, $kind='F1-Bolid';
  4.                
  5.                 public function set_speed($value) {
  6.                         if ($value < 0) { $this->speed = 0; return}
  7.                         if ($value > 300) { $this->speed = 300; return}
  8.                         $this->speed = $value;
  9.                 }
  10.                
  11.                 public function beep() {
  12.                         echo 'Beeeep!';
  13.                 }
  14.         }
  15. ?>

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

Наследование. Новый класс представляет собой какой-либо существующий, но в него что-то добавлено или что-то изменено.
PHP:
скопировать код в буфер обмена
  1. <?PHP
  2.         class gruzovik extends avto {
  3.                 protected $kind='KamAZ', $kuzov='closed';
  4.                
  5.                 public function set_speed($value) {
  6.                         if ($value < 0) { $this->speed = 0; return}
  7.                         if ($value > 120) { $this->speed = 120; return}
  8.                         $this->speed = $value;
  9.                 }
  10.                
  11.                 public function open_close_kuzov() {
  12.                         if ($this->kuzov == 'opened') $this->kuzov = 'closed';
  13.                         else $this->kuzov = 'opened';
  14.                 }
  15.         }
  16. ?>

Объекты класса грузовик имеют свойства Цвет и Скорость родительского класса, переопределенное свойство Тип и новове свойство Кузов. Метод set_speed переопределен: теперь максимальная скорость составляет 12 км/ч. Метод beep также унаследован грузовиком у болида Формулы-1.

Полиморфизм - это такое немного необычное проявление наследования. Проявляетсяон в том, что функция, объявленная в родительском классе начинает работать с данными дочернего класса.
PHP:
скопировать код в буфер обмена
  1. <?PHP
  2.         class A {
  3.                 function f1() { echo "Это в классе А"; }
  4.                 function f2() { f1(); }
  5.         }
  6.         class B extends A {
  7.                 function f1() { echo "Это в классе В"; }
  8.         }
  9.         $a=new A();
  10.         $b=new B();
  11.         $a->f2(); // Это в классе А
  12.         $b->f1(); // Это в классе В
  13.         $b->f2(); // Это в классе В (!)
  14. ?>

В 3м примере унаследованная функция f2 из первого класса вызвала внутри себя функцию f1 дочернего класса.


Конструктор и деструктор.
Эти методы вызываются автоматически. Первый при создании объекта, второй при удалении. Явно их вызывать не нужно. Конструктор можно использовать для инициализации свойств, например. Деструктор - для освобождения ресурсов. Эти методы всегда должны иметь уровень доступа public. Констуктор описывается следующм образом: function __construct(); деструктор: function __destruct(). В начале два прочерка.


Абстрактнае классы и методы.
Абстрактные методы - такие методы, которые объявлены в класе, но описание их пустое. Класс, содержащий хотя бы один такой метод - абстрактный, и должен быть объявлен с использванием волшебного слова abstract:
PHP:
скопировать код в буфер обмена
  1. <?PHP
  2.         abstract class my_abstr {
  3.                 abstract public function func();
  4.         }
  5. ?>

Создать объект такого класса нельзя, и используются такие классы только для наследования. Не абстракнтые методы в абстрактном классе присутствовать могут.

Если в абстрактном классе нет ни одного свойства, а методы его только абстрактные, то такой класс называется интерфейсом. Для него в PHP существует специальное слово:
PHP:
скопировать код в буфер обмена
  1. <?PHP
  2.         Interface my_iface {
  3.                 function func();
  4.         }
  5. ?>

То же самое, что и в предыдущем прмере.

В PHP есть штука, похожая на множественное наследование. Класс может наследовать множество интерфейсов:
PHP:
скопировать код в буфер обмена
  1. <?PHP
  2.         class multi implements Iface11, Iface2 {
  3.                 .....
  4.         }
  5. ?>


Еще надо рассказать о слове final. Этим словом обозначаются финальные методы и классы. Финальный - это такой, который не может быть переопределен в наследниках. Ну и отсюда ясно, что наследовать от финального класса нельзя. Вот так.


Статические свойства и методы.
Чтобы сделать свойство или метод статическим, надо предварить его описание свойством static:
PHP:
скопировать код в буфер обмена
  1. <?PHP ... protected static function func();
  2.         public static $property;
  3. ?>

К статическим методам и свойствам можно обращаться, не создавая объект класса. Например, мы описали какой-нибудь класс математических методов. Объект нам не нужен, а вот функции используем.
Чтобы внутри метода класса обратиться к его статическоу члену, используется волшебное слово self::. К родительскому классу можно обратиться, использовав слово parent::. К произвольному классу можно обратиться, использовав его имя: classname::.
PHP:
скопировать код в буфер обмена
  1. <?PHP
  2.         class math{
  3.                 const PI = 3.14;
  4.                 static function sin($value) { /* Вычисление синуса */}
  5.                 static function cos($value) { /* Вычисление косинуса */}
  6.                 static function tg($value) {
  7.                         return (self::cos($value)) ? (self::sin($value)) / self::cos($value) : 'NaN'  }
  8.         }
  9.        
  10.         echo math::tg(math::PI / 2);
  11. ?>

Константы, как видно из примера, тоже являются статическими методами.


Клонирование объектов.
Иногда бывает, что вам нужно скопировать объект. И после этого часть свойств надо изменить. Причем это всегда одни и те же свойства и принимают одни и те же значения после копирования. В PHP есть для этого механизм клонирования.
Клонирование объекта выполняется так:
PHP:
скопировать код в буфер обмена
  1. <?PHP $obj2 = clone $obj ?>

А всё то, что необходимо произвести при клонировании описывется в методе класса __clone():
PHP:
скопировать код в буфер обмена
  1. <?PHP  
  2.         class my_first {
  3.                 public $prop=4;
  4.                 function __clone() {$this->prop = 3;}
  5.         }
  6.        
  7.         $obj = new my_first;
  8.         echo $obj->prop; // 4
  9.         $obj2 = clone $obj;
  10.         echo $obj2->prop, '-', $obj->prop; // 3-4
  11. ?>
2. valenok - 20 Октября, 2009 - 22:16:47 - перейти к сообщению
Версия 2
--

ООП с начала

ООП = Объектно Ориентировочное Программирование = Object Oriented Programming
Подход в программировании, рассматривающий все в виде объектов.

Объект = класс это такая единица чего нибудь. К примеру Зверь.
Объект умеет что-то делать ( издавать звук, кушать, передвигаться ) = Методы
и имеет какие-то свойства ( Количество задних ног, диаметр глазных яблок ) = Свойства

PHP:
скопировать код в буфер обмена
  1. <?PHP
  2. class Beast // Ключевое слово class и название класса.
  3. {
  4.     // Свойство "любимое_блюдо"
  5.     $favorite_meal = 'Блины с икрой';
  6.  
  7.    // Метод "ешь!"
  8.    function eat() { echo $favorite_meal, ' - ням ням.'; }
  9. }


Вероятно, когда мы заведем себе питомца крокодила данди, нам захочется его покормить.
Для этого будет достаточно вызвать метод "А ну ешь!". А сам метод должен быть для нас доступен. Простыми словами Данди должен разрешить нам -его кормить.

Здесь вступают в силу три режима видимости:

public - свойство (или метод) доступен на прямую из любого места программы.
Вызвать метод или узнать значение свойства можно где угодно.

private - свойство (или метод) доступен только методам текущего класса.
Вызвать метод или манипулировать свойством можно только из другого метода этого же класса.

protected - свойство (или метод) доступен только классу и его наследникам (об этом чуть позже).

Если режим видимости не указан - применяется значение public по умолчанию.
--
Для того, чтобы создать себе зверька = экземпляр класса, мы пользуемся ключевым словом new;

PHP:
скопировать код в буфер обмена
  1. <?PHP
  2. class Beast // Ключевое слово class и название класса.
  3. {
  4.     // Свойство "любимое_блюдо". Манипулировать им извне класса не получится
  5.     private $favorite_meal = 'Блины с икрой';
  6.  
  7.    // Метод "ешь!" . по умолчанию public
  8.    public function eat() { echo $favorite_meal, ' - ням ням.'; }
  9. }
  10.  
  11. $fox = new Beast; // Лисица экземпляр класса Зверь
  12. $fox->eat(); // Вызываем метод eat у экземпляра $fox
  13. echo $fox->$favorite_meal; // Выводим свойство лисицы (любимое блюдо)


Этот код выдаст ошибку в последней строчке. Данное свойство закрыто для внешнего мира ключевым словом private;


Другие крутые ключевые слова и знаки

Предположим у нас есть класс для работы с почтой. Он умеет читать почту, посылать всех подальше за Вас и между тем имеет классный метод, определяющий название дня по дате.
Наш метод getDateName($date) не работает ни с какими почтовыми данными.
Для того, чтобы вызвать этот метод, нам нужно создать экземпляр класса, загрузить все его методы и свойства в память, а после чего вызывать уже метод.

$mailClass = new Mail;
$mailClass->getDateName($somedate);

На самом деле здесь можно сэкономить. Выиграть в памяти и производительности поможет оператор ::. В профессиональном жаргоне называется - паамаим некудотаим.
Этот оператор позволяет обращаться к методам и свойствам класса не создавая его копию в памяти.

Mail::getDateName($somedate);
название класса::название метода или свойства.

--

Также науке известно ключевое слово static.
Слово это ставится сразу после метки видимости ( public, private, protected )
и то, не всегда.

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

Ключевое слово static указывает на то, что свойство или метод всех экземпляров будет указывать на определенное место в памяти, а не создавать копии.
3. valenok - 23 Октября, 2009 - 15:49:52 - перейти к сообщению
Авто-загрузка - Autoloading

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

Начиная с PHP5 вы можете создать функцию (не метод какого либо класса, а самую обычную функцию ) с ключевым названием __autoload, принимающая в качестве аргумента название класса.

Эта функция автоматически будет вызываться при попытке создания экземпляра доселе не объявленного класса.
PHP:
скопировать код в буфер обмена
  1. <?PHP
  2. function __autoload($class_name) {
  3.     require_once $class_name . '.php';
  4. }
  5.  
  6. $obj  = new MyClass1();

функция __autoload вызовется сама с параметром MyClass1 если ранее вы нигде не объявляли class MyClass1 {..}
Кроме того, MyClass1 можно объявить в самой функции.

Замечу что
1. Автозагрузка __autoloading не доступна в php-cli (php command line interpretator)
2. Обработка ошибок выброшенных throw не может быть поймана catch внутри данной функции

Автозагрузка Autoloading на php[dot]net

Конструкторы и деструкторы

PHP умеет вызывать определенный метод класса при создании экземпляра этого класса = Конструктор
и вызывать другой метод при удалении экземпляра класса = Деструктор
Эти функции именуются ключевыми словами __construct и __destruct

PHP:
скопировать код в буфер обмена
  1.  <?PHP
  2. class BaseClass {
  3.    function __construct($something) {
  4.       echo 'Эта функция вызовется автоматически и напишет ', $something;
  5.    }
  6.    function __destruct() { echo 'Класс удален'; }
  7. }
  8.  
  9. $my = new BaseClass('"всем привет".'); // Создаем экземпляр класса
  10. echo '<br>';
  11. unset($my); // Удаляем экземпляр
  12.  


Вы наверное заметили разницу между тем, как мы создавали экземпляры класса ранее и как мы это сделали сейчас.
Ранее мы просто вызывали new ClassName;
На этот раз мы его вызвали с параметрами в скобочках new ClassName(...);
Эти параметры и передаются конструктору, который вызывается автоматически.

Конструкторы и деструкторы на php[dot]net
4. Champion - 29 Октября, 2009 - 16:34:08 - перейти к сообщению
Магические методы.

Встречаются такие события, которые нам было-бы интересно отслеживать,
к примеру обращения к несуществующим методам и свойствам, созданиям нового экземпляра класса и несколько других событий.
Для этого PHP предоставляет пару специальных методов класса:


_call
Если вы хотите отслеживать и обрабатывать попытки вызова несуществующих методов класса,
можете использовать магический метод __call().

PHP:
скопировать код в буфер обмена
  1. <?PHP
  2. class foo
  3. {
  4.    public static function __call($name,$arguments)
  5.    {
  6.         print("Вы попытались вызвать несуществующую
  7.         функцию $name($arguments) ");
  8.    }
  9. }
  10.  
  11. foo::any_method(2, 3);
  12. ?>



__set и __get
Эти методы аналогичны предыдущему, но применяются к свойствам класса.
Первый срабатывает при попытке записи несуществующему свойству,
второй при обращении к несуществующему свойству.

PHP:
скопировать код в буфер обмена
  1. <?PHP
  2. class foo
  3. {
  4.         function __set($name,$val)
  5.         {
  6.                 print("Попытка присвоить несуществующему
  7.                 $name значение $val");
  8.         }
  9.  
  10.         function __get($name)
  11.         {
  12.                 print("Попытка обращения к несуществующему $name");
  13.         }
  14. }
  15.  
  16. $x = new foo();
  17. $x->var = 3;
  18. print($x->var2);
  19. ?>



__invoke
Этот метод срабатывает, когда мы пытаемся обратиться к объекту как к функции.
То есть со скобочками и параметрами в них.

PHP:
скопировать код в буфер обмена
  1. <?PHP
  2. class OurClass
  3. {
  4.     function __invoke($x)
  5.     {
  6.         echo "Я тучка, тучка, тучка, я вовсе не медведь.
  7.        Объект я, а не функция. Зачем мне это: ";
  8.         var_dump($x);
  9.     }
  10. }
  11.  
  12. $obj = new MyClass;
  13. $obj(5);
  14. ?>


__sleep и __wakeup (> php 5.3.0)
При сериализации объекта вызывается __sleep()
При обратной процедуре объекта вызывается __wakeup()

Предназначение функции __sleep() это очистка объекта от ненужного мусора перед его сохранением
или игнорированием свойств не предназначенных для сохранения.
Функция должна возвращать массив с именами свойств предназначенных для сохранения. Если функция ничего не возвращает - соответственно никакие свойства функции и не сохраняются.
PHP:
скопировать код в буфер обмена
  1. <?PHP
  2. class foo
  3. {
  4.         public $f = 1;
  5.         public $g = 2;
  6.  
  7.         function __sleep() { return array('f'); }
  8. }


У восстановленного класса переменная $g будет отсутствовать, так как он сохранена не была.
Штука это новая и в старых версиях PHP (до 5.3.0) отсутсвует


__toString
Этот метод определяется в классе, когда к нему планируется обращаться как к строке: вывод на экран, конкатенация и т. д..

PHP:
скопировать код в буфер обмена
  1. <?PHP
  2. class TestClass
  3. {
  4.     public $foo;
  5.     public function __construct($foo)
  6.     {
  7.         $this->foo = $foo;
  8.     }
  9.     public function __toString()
  10.     {
  11.         return $this->foo;
  12.     }
  13. }
  14. $class = new TestClass('Hello!');
  15. echo $class;
  16. ?>
  17.  
5. Gal_Sergey - 31 Декабря, 2009 - 20:13:36 - перейти к сообщению
Спасибо, очень нужная тема.
Вот только код, иллюстрирующий полиморфизм, не работает. У меня выводит
Fatal error: Call to undefined function f1() in C:\del on line 4
6. Ch_chov - 31 Декабря, 2009 - 20:45:03 - перейти к сообщению
Ага, f1() заменить на $this->f1()
7. movEAX - 31 Декабря, 2009 - 22:15:22 - перейти к сообщению
Дополнение {PHP 5.3}

Namespaces(пространства имен)

PHP:
скопировать код в буфер обмена
  1. namespace engine\modules\mysql{
  2.    class DB{
  3.    }
  4. }
  5.  
  6. namespace engine\modules\mssql{
  7.    class DB{
  8.    }
  9. }


Благодря namespaces не возникает конфликта при объявлении классов с одинаковыми именами, ибо первый класс находиться в пространстве имен mysql, а второй в mssql.

Aliasing (совмещение имен)

PHP:
скопировать код в буфер обмена
  1. // можно так
  2. $x = new engine\modules\mssql\DB();
  3. //но так удобней
  4. use engine\modules\mssql\DB as mssql;
  5. $x = mssql();


Static и Self
PHP:
скопировать код в буфер обмена
  1.  
  2. class Base{
  3.  public static function printClassName(){
  4.   self::_printClassName();
  5.   static::_printClassName();
  6.  }
  7.  
  8.  public static function _printClassName(){
  9.  echo __CLASS__;
  10.  }
  11. }
  12. Base:: printClassName();
  13.  
  14. /**
  15.  * Рузльтат:
  16.  * Base
  17.  * Base
  18.  */
  19.  
  20. class Item extends Base{
  21.  public static function _printClassName(){
  22.  echo __CLASS__;
  23.  }
  24. }
  25.  
  26. Item:: printClassName();
  27.  
  28. /**
  29.  * Рузльтат:
  30.  * Base
  31.  * Item
  32.  */
  33.  


__callStatic()

PHP:
скопировать код в буфер обмена
  1. class Example{
  2.   public static function __callStatic($name,$args){
  3.    echo $name;
  4.   }
  5. }
  6.  
  7. Example::StaticFunction();
  8.  
  9. /**
  10.  * Рузльтат:
  11.  * StaticFunction
  12.  */


Static call

PHP:
скопировать код в буфер обмена
  1.  
  2. $cls = "ClassName";
  3. $cls::method();

Многие, наверно, помнят о такой фишке, когда в переменную можно занести имя функции, а потом вызвать эту функцию добавив к переменной круглые скобки. Так вот, теперь это можно вытворять с классами!

 

Powered by ExBB FM 1.0 RC1