Объектно-ориентированное программирование (часть1) (Рейтинг: +6)

Печать RSS
Введение
Данная статья рассчитана на начинающих разработчиков в области ООП. Я работаю с пятой версией РНР, поэтому и статья рассчитана на эту версию.
Первое, что необходимо понимать - класс это не набор функций или удобный контейнер для переменных, а абстрактный тип данных (АТД). Язык РНР не является строго типизированным языком, поэтому для начала необходимо разобраться с "простыми" типами. Целые числа (1, 45, 100, 378 и т.д.) имеют целочисленный тип, integer. Массивы – тоже тип данных. Более подробно с типами данных можно ознакомиться в документации - http://www.php.net/manual/ru/language.types.php. Класс также является типом данных, а объект - своеобразная переменная этого типа.
При создании класса чётко понять задачу, которую мы хотим представить. Часто построение класса является моделированием той сущности, которую необходимо перенести в код. Объект является отражением сущности, которая описана в виде класса. При моделировании класса стоит выявить те необходимые части сущности, над которыми будут производиться необходимые действия, с помощью методов. То есть необходимые части сущности являются полями класса, они как раз отражают данные, которые составляют общий тип данных. Этим тип объекта напоминает данные типа массив.
Методы выполняют разнообразные действия над данными. Методы следует проектировать так, чтобы они работали только с теми данными, которые определены в классе. Не рекомендуется определять методы, которые как-либо влияют на внешние данные. Так называемая стратегия слабого связывания - чем меньше связей между классами и внешними данными - тем проще извлечь класс из системы и повторно использовать его вновь.
Например, класс пачки сигарет состоит из упаковки и массива самих сигарет, следует создавать методы, которые работают только с упаковкой и сигаретами. Нет необходимости использовать методы, с помощью которых мы открываем бутылку газировки или даже прикуриваем сигарету. Каждый класс должен реализовывать только те действия, которые работают с данными самого класса. Не надо пытаться засунуть в один класс всю реализацию целого приложения. Например, есть сайт, который состоит из главной страницы, гостевой книги, страницы новостей, раздела статей и ссылок на дружественные сайты. Опишите каждый раздел сайта своим классом или классами. Классы, отражающие разделы сайта, могут вступать в наследование друг с другом. Т.е. мы представляем раздел как сущность, отдельный тип данных. Снова повторю, что в классе, моделирующем раздел новостей, не надо определять элементы других разделов, если нет необходимости. Если требуется что-то представить из других разделов, используйте включение. Например, пусть на странице новостей требуется вывести заголовки последних статей. Определите в классе новостей поле типа класса статей и используйте методы этого типа. Не надо пытаться писать методы, выводящие заголовки статей в классе, моделирующем новости. Определите необходимый метод в соответствующем классе, классе, представляющем статьи.
Теперь приступим к конкретным примерам. Попробуем создать простейшую гостевую книгу.
Первое, что сделаем, это определим части необходимой нам сущности. Этими частями будут имя посетителя, его email и собственно его сообщение. Приступим.
<? 
class GuestBook 
{ 
    private $name; 
    private $email; 
    private $msg; 
     
    public function __construct($name, $email, $msg) 
    { 
        $this->name = $name; 
        $this->email = $email; 
        $this->msg = $msg; 
    } 
} 
?>
Во-первых, мы объявили необходимые части нашего типа данных. Во-вторых, поля объявлены с модификатором доступ private, который означает, что с полями класса можно работать только методам этого же класса, но про модификаторы доступа будет рассказано позже. В-третьих, мы объявили метод __construct($name, $email, $msg). Этот метод является конструктором класса. О конструкторах и деструкторах будет рассказано немного позже, сейчас же запомните, что конструкторы всегда автоматически выполняются первыми, до всех остальных методов класса, непосредственно сразу после выделения памяти под объект. Деструкторы же выполняются последними, непосредственно перед уничтожением объекта.
В-четвёртых, мы использовали ключевое слово $this. $this означает ссылку на объект, для которого реализована та или иная конструкция. В отличие от других языков программирования, при обращении к членам класса, обязательно следует использовать $this: $this->name и $name - это две разные переменные.
Теперь подумаем над тем, как нам обращаться к полям объекта. Реализуем методы, которые вернут соответствующие значения.
<? 
class GuestBook 
{ 
    private $name; 
    private $email; 
    private $msg; 
     
    public function __construct($name, $email, $msg) 
    { 
        $this->name = $name; 
        $this->email = $email; 
        $this->msg = $msg; 
    } 
     
    public function getName() 
    { 
        return $this->name; 
    } 
    public function getEmail() 
    { 
        return $this->email; 
    } 
    public function getMsg() 
    { 
        return $this->msg; 
    } 
} 
?>
Теперь данные класса можно читать из вне.
При разработке гостевой книги неизбежно встаёт вопрос, как хранить данные. Воспользуемся для этого базой данных MySQL. Добавим новое поле типа DataBase – это специальный тип для работы с БД. В конструкторе проведем инициализацию. Создадим методы для получения данных из базы и наполнения базы. Метод чтения (SELECT) реализуем так:
<?php 
public function Select() 
{ 
    $sql = "SELECT name, email, msg FROM guestbook"; 
    $dbArray = $this->db->Db2Array(); 
    foreach($dbArray as $rows) 
    { 
        $outPut[] = new GuestBook($rows['name'], $rows['email'], $rows['msg'], $this->db); 
    } 
    return $outPut; 
} 
?>
Метод добавления значений реализуем следующим образом:
<?php 
public function Insert() 
{ 
    $sql = "INSERT INTO guestbook (name, email, msg) VALUES('{$this->name}', '{$this->email}', '{$this->msg}')"; 
    if($this->db->Insert($sql) === TRUE) 
        return TRUE; 
    return FALSE; 
} 
?>
Первый метод возвращает массив объектов GuestBook, второй возвращает TRUE, если данные успешно добавлены, и FALSE - в противном случае.
В первой строке метода Select() cоздается SQL-запрос к базе на выборку всех значений.
Во второй строке из базы извлекается результирующая таблица в виде массива $dbArray. Как это происходит будет описано позже. В пятой строке мы создаем массив объектов типа GuestBook, который и возвращается в качестве результата. Напомню, чтобы обратиться к соответствующим полям мы реализовали соответствующие методы.
Для второго метода также создан SQL-запрос, но уже на добавление данных в базу (INSERT). Если запрос выполнен удачно, возвращаем истину (TRUE), иначе – ложь (FALSE).
Разрабатывая Web-приложение с использованием ООП, следует помнить о производительности, которая всегда ниже по сравнению с процедурным подходом. Метод Select, возвращающий значения каждый раз требует обращения к новому объекту типа DataBase. Если в базе хранится тысячи записей, это вызовет значительные накладные расходы - обработка каждой из записей потребует создания дополнительного объекта DataBase. Давайте придумаем выходы из подобной ситуации. Первое что приходит на ум, это не инициализировать поле типа DataBase, а написать соответствующий метод, который инициализирует поле $db.
<?php 
public function setDb($db) 
{ 
    $this->db = $db; 
} 
?>
Напомним, что параметр $db – это экземпляр класса DataBase. Можно поступить и так:
<?php 
public function setDb($db) 
{ 
    $this->db = new DataBase(‘login’,’password’,’host’,’database’); // Параметры – значения подключения к базе данных. 
} 
?>
Однако снова таки тысячу раз инициализировать объект типа DataBase не оптимально. Надо думать дальше smile
Практика показала, что более оптимально разбить подобный класс на два: в первом реализовать инициализацию сущности, а во второй объявить необходимые методы для работы с базой данных.
<? 
class GuestBook 
{ 
    private $name; 
    private $email; 
    private $msg; 
     
    public function __construct($name, $email, $msg) 
    { 
        $this->name = $name; 
        $this->email = $email; 
        $this->msg = $msg; 
    } 
     
    public function getName() 
    { 
        return $this->name; 
    } 
    public function getEmail() 
    { 
        return $this->email; 
    } 
    public function getMsg() 
    { 
        return $this->msg; 
    } 
} 
class GuestBookDb 
{ 
    private $db; 
     
    public function __construct($db) 
    { 
        $this->db = $db; 
    } 
    public function Select() 
    { 
        $sql = "SELECT name, email, msg FROM guestbook"; 
        $dbArray = $this->db->Db2Array(); 
        foreach($dbArray as $rows) 
        { 
            $outPut[] = new GuestBook($rows['name'], $rows['email'], $rows['msg']); 
        } 
        return $outPut; 
    } 
    public function Insert($obj) 
    { 
        $name = $obj->getName(); 
        $email = $obj->getEmail(); 
        $msg = $obj->getMsg(); 
         
        $sql = "INSERT INTO guestbook (name, email, msg) VALUES('$name', '$email', '$msg')"; 
        if($this->db->Insert($sql) === TRUE) 
            return TRUE; 
        return FALSE; 
    } 
} 
?>
Что же изменилось: во-первых, мы получаем также массив объектов с помощью метода Select, но теперь он содержит только необходимые данные. Теперь на тысячу объектов GuestBook, создается только один объект GuestBookDb. В памяти не будет храниться тысячу раз данные типа DataBase. Во-вторых, немного изменился метод Insert(). У него появился параметр – объект типа GuestBook – это необходимо, чтобы занести данные в базу. В результате таких преобразований нагрузка на сервер немного снизится.
Добавил:
Рейтинг: +6
Просмотры: 1729
Комментарии (6) »