ФорумПрограммированиеПыхнуть хотите?F.A.Q. → Полиморфизм для начинающих

Полиморфизм для начинающих

  • sap

    Сообщения: 2701 Репутация: N Группа: Кто попало

    Spritz 15 августа 2008 г. 0:23

    Что такое полиморфизм?

    Полиморфизм — одна из трех основных парадигм ООП. Если говорить кратко, полиморфизм — это способность обьекта использовать методы производного класса, который не существует на момент создания базового. Для тех, кто не особо сведущ в ООП, это, наверно, звучит сложно. Поэтому рассмотрим применение полиморфизма на примере.

    Постановка задачи

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

    Самые простые варианты, которые приходят в голову — написать три отдельных класса и работать с ними. Или написать один класс, в которым будут все свойства, присущие всем трем типам публикаций, а задействоваться будут только нужные. Но ведь для разных типов аналогичные по логике методы должны работать по-разному. Делать несколько однотипных методов для разных типов (get_news, get_announcements, get_articles) — это уже совсем неграмотно. Тут нам и поможет полиморфизм.

    Абстрактный класс

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

    abstract class Publication
    {
    // таблица, в которой хранятся данные по элементу
    protected $table;

    // свойства элемента нам неизвестны
    protected $properties = array();

    // конструктор
    public function __construct($id)
    {
    // обратите внимание, мы не знаем, из какой таблицы нам нужно получить данные
    $result = mysql_query ('SELECT * FROM `'.$this->table.'` WHERE `id`="'.$id.'" LIMIT 1');
    // какие мы получили данные, мы тоже не знаем
    $this->properties = mysql_fetch_assoc($result);
    }

    // метод, одинаковый для любого типа публикаций, возвращает значение свойства
    public function get_property($name)
    {
    if (isset($this->properties[$name]))
    return $this->properties[$name];

    return false;
    }

    // метод, одинаковый для любого типа публикаций, устанавливает значение свойства
    public function set_property($name, $value)
    {
    if (!isset($this->properties[$name]))
    return false;

    $this->properties[$name] = $value;

    return $value;
    }

    // а этот метод должен напечатать публикацию, но мы не знаем, как именно это сделать, и потому объявляем его абстрактным
    abstract public function do_print();
    }


    Производные классы

    Теперь можно перейти к созданию производных классов, которые и реализуют недостающую функциональность.

    class News extends Publication
    {
    // конструктор класса новостей, производного от класса публикаций
    public function __construct($id)
    {
    // устанавливаем значение таблицы, в которой хранятся данные по новостям
    $this->table = 'news_table';
    // вызываем конструктор родительского класса
    parent::__construct($id);
    }

    // переопределяем абстрактный метод печати
    public function do_print()
    {
    echo $this->properties['title'];
    echo '<br /><br />';
    echo $this->properties['text'];
    echo '<br />Источник: '.$this->properties['source'];
    }
    }

    class Announcement extends Publication
    {
    // конструктор класса объявлений, производного от класса публикаций
    public function __construct($id)
    {
    // устанавливаем значение таблицы, в которой хранятся данные по объявлениям
    $this->table = 'announcements_table';
    // вызываем конструктор родительского класса
    parent::__construct($id);
    }

    // переопределяем абстрактный метод печати
    public function do_print()
    {
    echo $this->properties['title'];


    echo '<br />Внимание! Объявление действительно до '.$this->properties['end_date'];
    echo '<br /><br />'.$this->properties['text'];
    }
    }

    class Article extends Publication
    {
    // конструктор класса статей, производного от класса публикаций
    public function __construct($id)
    {
    // устанавливаем значение таблицы, в которой хранятся данные по статьям
    $this->table = 'articles_table';
    // вызываем конструктор родительского класса
    parent::__construct($id);
    }

    // переопределяем абстрактный метод печати
    public function do_print()
    {
    echo $this->properties['title'];
    echo '<br /><br />';
    echo $this->properties['text'];
    echo '<br />© '.$this->properties['author'];
    }
    }


    Теперь об использовании

    Суть в том, что один и тот же код используется для обьектов разных классов.

    // наполняем массив публикаций объектами, производными от Publication
    $publications[] = new News($news_id);
    $publications[] = new Announcement($announcement_id);
    $publications[] = new Article($article_id);

    foreach ($publications as $publication) {
    // если мы работаем с наследниками Publication
    if ($publication instanceof Publication) {
    // то печатаем данные
    $publication->do_print();
    } else {
    // исключение или обработка ошибки
    }
    }


    Вот и все. Легким движением руки брюки превращаются в элегантные шорты :-).

    Основная выгода полиморфизма — легкость, с которой можно создавать новые классы, «ведущие себя» аналогично родственным, что, в свою очередь, позволяет достигнуть расширяемости и модифицируемости. В статье показан всего лишь примитивный пример, но даже в нем видно, насколько использование абстракций может облегчить разработку. Мы можем работать с новостями точно так, как с объявлениями или статьями, при этом нам даже не обязательно знать, с чем именно мы работаем! В реальных, намного более сложных приложениях, эта выгода еще ощутимей.

    Немного теории

    • Методы, которые требуют переопределения, называются абстрактными. Логично, что если класс содержит хотя бы один абстрактный метод, то он тоже является абстрактным.

    • Очевидно, что обьект абстрактного класса невозможно создать, иначе он не был бы абстрактным.

    • Производный класс имеет свойства и методы, принадлежащие базовому классу, и, кроме того, может иметь собственные методы и свойства.

    • Метод, переопределяемый в производном классе, называется виртуальным. В базовом абстрактном классе об этом методе нет никакой информации.

    • Суть абстрагирования в том, чтобы определять метод в том месте, где есть наиболее полная информация о том, как он должен работать.



    Оригинал на моем сайте
  • pasha

    Сообщения: 1048 Репутация: N Группа: Адекваты

    Spritz 15 августа 2008 г. 13:11, спустя 12 часов 47 минут 28 секунд

    хренова у меня мля с ооп :(
  • sap

    Сообщения: 2701 Репутация: N Группа: Кто попало

    Spritz 15 августа 2008 г. 13:25, спустя 13 минут 39 секунд

    Хочешь сказать, что статья сложная? о_О
  • pasha

    Сообщения: 1048 Репутация: N Группа: Адекваты

    Spritz 15 августа 2008 г. 13:30, спустя 5 минут 19 секунд

    sap, а где можно кроме полиморфизма и абстракции почитать… по теме ?!
  • pasha

    Сообщения: 1048 Репутация: N Группа: Адекваты

    Spritz 15 августа 2008 г. 13:31, спустя 57 секунд

    Да не … скорей всего я сложный)
  • sap

    Сообщения: 2701 Репутация: N Группа: Кто попало

    Spritz 15 августа 2008 г. 15:32, спустя 2 часа 1 минуту 1 секунду

    Pasha, я бы посоветовал книжку по ООП почитать :)
  • artoodetoo

    Сообщения: 5147 Репутация: N Группа: в ухо

    Spritz 2 декабря 2008 г. 10:01, спустя 108 дней 19 часов 29 минут

    Смущает этот фрагмент:

    if ($publication instanceof Publication) {
    // то печатаем данные
    $publication->do_print();
    } else {
    // исключение или обработка ошибки
    }

    Вроде из примера следует, что сущностей иного класса, чем Publication здесь и быть не может. Такие проверки сводят положительный эффект от ООП к минимуму.
    ιιlllιlllι унц-унц
  • Trej Gun

    Сообщения: 5305 Репутация: N Группа: в ухо

    Spritz 2 декабря 2008 г. 12:52, спустя 2 часа 50 минут 42 секунды

    да в общем там вполне хватило бы

    foreach ($publications as $publication) {
       // если мы работаем с наследниками Publication
       try {
           // то печатаем данные
           $publication->do_print();
       } catch (Exeption $e){
           // исключение или обработка ошибки
       }
    }
  • sap

    Сообщения: 2701 Репутация: N Группа: Кто попало

    Spritz 2 декабря 2008 г. 15:02, спустя 2 часа 10 минут 42 секунды


    Смущает этот фрагмент:

    if ($publication instanceof Publication) {
    // то печатаем данные
    $publication->do_print();
    } else {
    // исключение или обработка ошибки
    }

    Вроде из примера следует, что сущностей иного класса, чем Publication здесь и быть не может. Такие проверки сводят положительный эффект от ООП к минимуму.

    Что смущает-то? Если мы хотим распечатать только новости, статьи и объявления, то эта проверка очень даже уместна. Зачем там иные сущности? Иные сущности пусть отдельно распечатываются.
  • artoodetoo

    Сообщения: 5147 Репутация: N Группа: в ухо

    Spritz 2 декабря 2008 г. 20:11, спустя 5 часов 9 минут 4 секунды

    вот именно. нет иных сущностей. зачем проверка?

    // наполняем массив публикаций объектами, производными от Publication

    // если мы работаем с наследниками Publication
    ιιlllιlllι унц-унц
  • phpdude

    Сообщения: 26646 Репутация: N Группа: в ухо

    Spritz 2 декабря 2008 г. 20:29, спустя 17 минут 23 секунды


    вот именно. нет иных сущностей. зачем проверка?

    // наполняем массив публикаций объектами, производными от Publication

    // если мы работаем с наследниками Publication

    ну вдруг кто нить объект mysqli передат параметром?) криворукий. я так понимаю это защита "от дурака" такая. сори я топик не читал не судите строго, только пролистал поверхностно :)
    Сапожник без сапог
  • sap

    Сообщения: 2701 Репутация: N Группа: Кто попало

    Spritz 3 декабря 2008 г. 2:52, спустя 6 часов 23 минуты 2 секунды

    вот именно. нет иных сущностей. зачем проверка?

    Ну тут может и незачем. Это же пример, нужно тему раскрывать как можно полнее. Где-то это может быть необходимым.
  • artoodetoo

    Сообщения: 5147 Репутация: N Группа: в ухо

    Spritz 3 декабря 2008 г. 9:58, спустя 7 часов 6 минут 9 секунд

    хорошо. а вариант перехвата исключения будет работать в ТАКОМ случае?
    ιιlllιlllι унц-унц
  • sap

    Сообщения: 2701 Репутация: N Группа: Кто попало

    Spritz 3 декабря 2008 г. 12:18, спустя 2 часа 20 минут 3 секунды

    Ну естественно.
  • artoodetoo

    Сообщения: 5147 Репутация: N Группа: в ухо

    Spritz 3 декабря 2008 г. 15:08, спустя 2 часа 50 минут 18 секунд

    Тогда if не нужен :) Оборачиваем вызов метода в try - catch
    Важно учесть, что непредусмотренная планом хня должна останавливать выполнение.

    МАВР, я бы вынес этот блок из цикла, вот так:

    try {
    foreach ($publications as $publication) {
    // если мы работаем с наследниками Publication
    // то печатаем данные
    $publication->do_print();
    }
    } catch (Exeption $e){
    // каким-то образом прокрался чужой или случилась иная ошибка
    // не предусмотренная генеральным планом - выводим сообщение и умираем

    // …
    die();
    }
    ιιlllιlllι унц-унц

Пожалуйста, авторизуйтесь, чтобы написать комментарий!