Процедурное против объектно-ориентированного?

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

Стиль решения задачи зависит от того, на сколько сложен будет алгоритм её решения. Если вы, к примеру, можете её решить выполнив одно действие потом другое, затем, если не получится сделать третье, то вы выполните четвёртое — это процедурный стиль или по-другому — процедурное программирование (ПП). Инструкции для решения задачи выполняются одна за другой, сверху вниз, иногда возникают изменения в их последовательности. Среди команд нельзя выделить приоритетные, для решения задачи они просто должны быть выполнены.

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

Для того, чтобы сравнить два стиля, мы рассмотрим конкретную задачу, и по ходу её анализа будем смотреть, как это реализуется в ООП и в ПП.

Это должен носить у сердца каждый программист

Пока вы ещё не наворотили много трудно-читаемого и понимаемого кода, запишите себе на видном месте два принципа. Без них может получится “чёпопало” в ваших программах.
Запишите себе — DRY и KISS.

DRY (Don’t Repeat Yourself) — Не Повторяйся.
KISS (Keep It Simple, Stupid) — Не Усложняй, Тупица.

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

Второй принцип — не усложняй, тупица! Звучит грубо, но довольно самокритично, так как это произносится всё-таки внутренним голосом. Действительно, зачем всё усложнять? Код должен решать задачу и всё, он не должен показывать ваши амбиции. Усложнять глупо, вы вообще учитываете что вы сами потом к этому коду вернётесь, чтобы найти в нём закравшуюся ошибку? Для написанного вами класса это означает, что каждый метод решает элементарную задачу. Если это не так, код надо реорганизовать и превратить один метод в два или больше.

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

Кто-то может спросить — а что собственно происходит? Какие такие паттерны? Обычные! Можно сказать, это образцы решения задач. Но не конкретные примеры, а схемы, которые отражают взаимоотношения внутри приложения, между классами и объектами. Примером такого паттерна можно привести широко использующийся при разработке сайтов паттерн MVC. Это архитектурный паттерн, он предписывает разделить приложение на части — пользовательский интерфейс, бизнес-логику и контроллеры (они обеспечивают дополнительную обработку данных, соединяя бизнесс-логику и интерфейс).

Не кидайтесь сразу в изучение паттернов, получите опыт ООП на простых задачах, чтобы хоть как-то представлять, что там к чему. И уже потом, тада када …, ныряйте в паттерны на здоровье.

Проведём мысленный эксперимент

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

  • Площадь
  • Количество комнат
  • Тип дома
  • Цена
  • Этаж
  • Наличие балкона

Для коттеджей практически тоже самое:

  • Площадь
  • Количество комнат
  • Тип дома
  • Цена
  • Количество этажей
  • Отопление
  • Приусадебный участок

В ПП вы сразу начинаете писать функции, для обработки форм ввода и вывода данных для квартиры и для коттеджа.
В ООП вам нужно описать базовый класс — Недвижимость, который будет хранить общие характеристики: что есть и у квартир, и у коттеджей. Класс Недвижимость, будет включать:

  • Площадь
  • Количество комнат
  • Тип дома
  • Цена

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

Далее мы создадим еще два класса: Коттедж и Квартира, оба будут наследовать все характеристики и методы класса Недвижимость и расширять его уникальными уже для себя свойствами и методами.

Класс Квартира, будет включать:

  • Этаж
  • Наличие балкона

Кроме этого, нужны будут методы для доступа к ним и методы для сохранения в базе данных.

Для класса Коттедж, будут выделены характеристики:

  • Количество этажей
  • Отопление
  • Приусадебный участок

Допустим, что мы уже написали программы как в стиле ПП, так и в стиле ООП. Теперь представим, что мы пустили их в свободное плавание, стали использовать их на практике. И, как всегда бывает с реальными программами, они чем-то не понравились заказчику, или ему захотелось большего. Рассмотрим несколько сценариев развития наших программ.

Сценарий 1

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

  • Высота потолков
  • Устройство пола
  • Электроснабжение

ПП: Для нового объекта учёта, полностью с “нуля” создаётся всё новое и функции и форма.
ООП: Мы просто создаём новый класс Склад, наследуя всё необходимое от класса Недвижимость. В классе Склад, нам нужно будет только создать новые характеристики и методы для обработки новых данных.

Сценарий 2

Начальнику захотелось чтобы цена нового объекта недвижимости, с его идентификационным номером отправлялись к нему на мобильный. Он же должен быть в курсе происходящего.
Процедурное: Скрипя сердцем, идём меняем код во всех функциях и для квартир, и для коттеджей, и для складов. Везде добавляется код отправки смс на мобильный.
ООП: Улыбаясь, добавляем код отправки смс только в классе Недвижимость. Остальное, за нас делает принцип наследования.

Сценарий 3

И опять начальнику не сидится, захотелось ему конкретики. Надо теперь отдельно учитывать квартиры в новостройках и вторичное жильё.
Процедурное: Опять двадцать пять. Он что, сразу не мог сказать все свои хотелки. Смирившись, создаем новые функции и формы для каждого нового объекта учёта, повторяя весь общий код для любой недвижимости, потом для квартир, а затем добавляем специфику отличающую новостройки и вторичку.
ООП: Смотрим на того, кто занимается применением к этой задачи ПП, думаем какой он не предусмотрительный, и идём вносить небольшие изменения в нашу иерархию классов. Берём класс Квартира и наследуем от него классы Новостройка и Вторичка. Добавляем в них те даные, что сказал начальник, и методы для их обработки.

Сценарий 4

Обнаружился баг в передачи смс с ценой, странным образом вместо шести нулей на конце, остаётся только один.
Процедурное: Ааааааа! Всё успокоились. Идём и исправляем код относящийся к каждому объекту недвижимости.
ООП: Наливаем себе любимый напиток, вдыхаем его аромат, и со спокойствием удава исправляем код одного метода в классе Недвижимость.

Заключение

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

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

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

Всё, что вам нужно знать из ПП

Давайте разберёмся в начале в понятиях. Специфических слов в процедурном программировании не так уж и много: модуль, тип данных, функция, конструкция, переменная, константа. Слова на слуху, поэтому пробежимся не особо останавливаясь:

  • Модуль — это кусок программы, чаще всего занимающий целый файл, и представляющий собой набор программных объектов (тип данных, функция, конструкция, переменная, константа), относящихся к решению определённого круга задач. Например, существуют модули, работающие с графическими возможностями компьютера, другие занимаются отправкой почты и т.д.
  • Тип данных — это способ описания множества данных похожих друг на друга по своей структуре. Например, если вы хотите описать множество всех целых чисел со знаком, которые можно хранить, выдавая каждому по четыре байта, то для этого есть тип данных int.
  • Функция — законченный кусок программы (которому дано имя), решающий определённую подзадачу в вашей программе. Существуют встроенные функции, кроме них создаются новые для решения задачи. Встроенные функции часто связаны с элементарным вводом и выводом информации, преобразованием данных и т.д.
  • Конструкция — это специальная инструкция, изменяющая поток выполнения программы. Например: ветвления и циклы.
  • Переменная — реально существующий в программе представитель некоторого типа данных. Например: переменная, хранящая целое число, относится к типу int.
  • Константа — тоже самое, что и переменная, вот только значение в такой переменной не меняется.

Всё, что вам нужно знать из ООП

Ваш минимальный словарь для ООП должен включать такие слова, как: класс, данные, методы, параметры, экземпляр, наследование, полиморфизм, инкапсуляция.

  • Класс — это прототип для создания похожих объектов, определяющий что будет в них храниться и какую функциональную нагрузку они будут нести. Данные и функции формулируются в понятиях, используемых для решения задачи. Обычно он создаётся в соответствии с некоторым паттерном, и сосуществуя в системе с другими классами должен находится с ними в слабой связи. Экземпляр класса иногда называют коротко — объект.
  • Данные — это полезная информация, характеризующая конкретного представителя данного класса, например: его цвет, размеры, форму и т.д. Их хранение осуществляется в переменных членах класса.
  • Методы — определяют функциональные возможности класса, а также выполняют служебную роль для данных класса. Реализуются в виде функций членов класса.
  • Параметры — специальные переменные, от которых зависит результат работы методов класса. Параметры появляются тогда, когда их данные нельзя передать через переменные члены класса. Например, в специальных методах, которые называются сеттеры (от слова “set” — установка), без параметров не обойтись, собственно именно через них данные попадают в экземпляр класса. Также данные могут попасть в объект через специальный метод конструктор — через его параметры.
  • Экземпляр — это конкретный представитель класса объектов, создающийся во время работы программы, с помощью конструктора (см. выше). Каждый экземпляр некоторого класса обладает одинаковым набором характеристик и поведением (набором методов). Отличием будет являться только разное значение этих характеристик.
  • Наследование — один из замечательных принципов ООП. Он описывает механизм выстраивания иерархии классов, позволяющий из одного класса создавать другой класс. Если бы его не было, представляете каким бы толстым оказался класс, в котором мы бы попытались описать множество всех компьютерных комплектующих? В результате, каждый экземпляр такого класса  тащил бы за собой ненужный шлейф из характеристик, которые принадлежат другим железкам. Например, у процессора болталось бы без надобности поле, хранящее разрешение монитора по горизонтали и вертикали. Но, у нас-таки есть принцип наследования! Мы можем создать нечто абстрактное — компьютерную железяку, которая обладает общими характеристиками и поведением всех железяк. И на основе её создать множество классов, которые уже будут содержать более специфические характеристики и поведение. Мы можем продолжать плодить классы, пока у нас не получится специальный класс, который будет описывать центральный процессор и не содержать ничего лишнего. Однако, учтите, что бы вы не сделали с родительским классом, это отразится на всех его потомках в любом “колене”.
  • Полиморфизм — ооо, это ооочень полезный принцип. Его сразу можно и не заметить, но когда вы начнёте использовать интерфейсы, вот тогдааа. Он описывает возможность создавать функции с одинаковым именем, но разным набором инструкций.
  • Инкапсуляция — вот с этим намного сложнее. Просто не совсем ясно, зачем называть то, что и так очевидно. Этот принцип говорит, что класс — это единое целое, включающее и данные и методы, которые могут обладать разным уровнем доступа из внешних ресурсов. Какие-то элементы класса доступны только внутри самого класса, до каких-то можно добраться где угодно. Главное — это “всё внутри”.
Понравилась статья? Посоветуйте другу

Количество коментариев: 14

  1. Автор предвзят и явно симпатизирует ООП. Исправлять код отправки СМС в двадцати местах никто не станет — для этого делается отдельная процедура. А если заказчику приспичит торговать велосипедами, то вся ваша стройная иерархия классов идёт нафиг — нужно уже будет делать надкласс «объект купли-продажи», а в процедурном стиле просто добавите ещё один case в оператор switch в одну процедуру, которая делает со всеми объектами купли-продажи что-то сходное. Потом заказчик затихнет года на два, а через два года вам придётся разбираться в запутанной иерархии классов.

    • Дмитрий Артанов Дмитрий Артанов

      На счёт СМС — в статье говорится о добавлении кода отправки, а не об исправлении.

      На счёт великов — этож каким гибким должен быть заказчик если он одновременно будет заниматься и недвижимостью и движимостью 🙂

      Ну а если серьёзно — тут уже попахивает паттернами проектирования.

      А про «разбираться в запутанной иерархии классов» — кто мешает нормально документировать?

  2. Сценарий 1

    Например, ваше начальство заинтересовалось продажей складов.

    — В этом случае понадобится, как минимум ещё одна таблица в БД. А при «многотабличном» приложении (типа «торговля и склад») надо правильно проектировать сайт: чтобы при добавлении новой таблицы (или при добавлении нового поля в таблицу) _вааще ничего не надо было бы больше делать_, а все новые страницы (урл в меню), формы (или новые поля в формах и таблицах) появлялись бы «автоматически». И ООП тут особо ни при чём.

    • Дмитрий Артанов Дмитрий Артанов

      Точно, полностью согласен, не бросаться сломя голову программировать, вначале — проект!

      Категорично говорить что понадобится ещё одна таблица не нужно (зависит от выбранной архитектуры).

      Я думаю ООП здесь причём, особенно под «петтерным соусом» 😉

  3. ну вот как раз нащёт петтерного (или паттерного?) соуса — меня всегда ужасно раздражает, когда mvc пришивают к ООП намертво, как пуговицы. Хотя, если разобраться, для автоматизации и mvc не самое главное…

    • Дмитрий Артанов Дмитрий Артанов

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

  4. «Вы хотите со мной поспорить?» (с) Хотя по интонации как раз видно, что не хотите… 🙂 Ладно, у вас даже ООП так привлекательно описан в соседней статье, что прямо в соблазн вводит. Нигде больше такого разумного описания не встречал. Но вот примером с новым полем в таблице как бы сами себя опровергаете (в моих глазах): у меня эта часть автоматизирована именно так, как я сказал (добавил поле или таблицу в mysql — и всё), но ООП там кот наплакал (чисто для нэймспейс).

    • Дмитрий Артанов Дмитрий Артанов

      «В спорах рождается истина!» (с)
      На задачу нужно смотреть через очки «рациональности», учитывая, что они круче у того кто за плечами несёт большой рюкзак опыта.
      Если есть небольшое подозрение что сущностей в задаче будет много, и они будут сложно категоризированы, то лучше ООП и MVC не найти. Да и к СУБД нужно будет присмотреться. Кроме реляционных ещё есть и обектные.
      На счёт добавления поля или таблицы в существующую базу данных — с этим я всегда осторожен, много полей в таблице или много таблиц в базе не есть гуд, для сервера.

  5. organmusic

    Программировал уже более 25 лет, более чем на 15 языках, и с процедурными и с ООП (С++, Java).
    Мое мнение что ООП переоценено, вокруг него больше шума и рекламы, вроде типа что это круто.
    Основной недостаток ООП — это безмерное размножение сущностей — классов, объектов, иерархий. Все это прямо противоречит KISS принципу — между прочим основополагающему принципу программирования.
    Так или иначе ООП применять можно когда это действительно уместно, но не более того.
    Меня действительно смешит в ООП (например в Java) сравнение строк:
    str1.compare(str2)
    В процедурном языке это проще, естественней, понятней:
    compare(str1, str2)
    ну и т п

    • Дмитрий Артанов Дмитрий Артанов

      Конечно, всего должно быть в меру. Если нужно прогнать текстовые файлы через регулярное выражение, то не нужно городить огород из кучи классов, достаточно небольшого скрипта. Хотя, возможно его придётся встраивать в систему автоматизации которая настраивается и запускает этот скрипт в определённое время.
      На счёт сравнения строк 🙂
      Количество символов в выражении получается такое-же, а насчёт понятности — степень понятности этого str1.compare(str2) = степени понятности этого compare(str1, str2). И степень непонятности то-же, для того кто это впервые видит.

      • Мне вариант str1.compare(str2) кажется более понятным. конечно, для самой операции compare не имеет значения, однако сравните:

        str1.find(str2);

        c

        find(str1,str2);

        в первом варианте сразу понятно, что в str1 будет произведен поиск str2. а во втором непонятно что в чем будут искать.

  6. Игорь

    Конечный результат зависит от прослойки между креслом и монитором, а не от стиля программирования.
    Можно спокойно в ПП написать отдельную функцию для отправки СМС и подключить ее в каждый вид недвижимости. Если что-то нужно будет поменять, то менять нужно будет только в одном месте, в, скажем, файле functions.php.
    Все, что можно сделать на ООП, можно сделать и на ПП, и сделать это можно качественно и с расстановкой, без постоянного переписывания половины кода. На вкус и цвет, знаете.

    • Дмитрий Артанов Дмитрий Артанов

      Согласен, всё зависит от программиста. Но для программы с большим количеством сущностей больше подходит ООП.

  7. Руслан

    спасибо. все описано правильно. до сих пор знаю компании которые в огромном количестве вставляют куски SQL кода в ООП код . со словами «так оно лучше будет». какие примеры я только не приводил.)

Добавить комментарий



[ Ctrl + Enter ]