[Из песочницы] В защиту ООП. 7 несостоятельных аргументов его противников

habr.png

Когда я, так сказать, прошёлся по Интернету, я заметил одну интересную особенность. Все парадигмы программирования, где-либо обсуждаемые, воспринимаются людьми совершенно спокойно. Если, например, говорят про процедурное программирование, то говорят про него абсолютно спокойно. То же самое — про модульное программирование. Декларативное программирование — никаких бурь, волнений или холиваров. Функциональное программирование — то же самое.

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

Возможно, вы только что подумали, что я — скорее противник, чем сторонник ООП. Это абсолютно не так (впрочем, вы это можете понять из заголовка). Нет. Я — скорее противник «серебряных пуль», хайпа, возведения какой-либо методологии или человека на престол и всяческого вождения хороводов. Вы же не водите хороводы вокруг, скажем, гаечного ключа или газонокосилки. И не пишете, я надеюсь, публикаций, почему дрель или молоток — отстой.
Но на сегодняшний день весь Интернет кишит именно напыщенными, гиперэмоциональными, радикалистскими статьями по поводу ООП — если один говорит, что ООП — «зер гут» и вообще всем гутам гут — то другой обязательно вещает, что ООП необходимо срочно выкинуть на помойку (если только он не разделяет взгляды первого). Третьего не дано.

Я же хочу именно привнести третий элемент. Спокойно, без хайпа и ругани рассказать, почему ООП — не эликсир от всех болезней, но также, как и ПП, ФП или ЛП имеет право на существование.

Итак, спокойная статья в защиту ООП. В ней я попытаюсь рассмотреть основные доводы противников ООП и обосновать их несостоятельность.

1. Всё, что есть в ООП, уже давно есть в других парадигмах


Почти все языки программирования являются тьюринг-полными, за исключением языков разметок, как то: HTML, XML, CSS и т.д. Если говорить крестьянским языком, тьюринг-полный язык — язык, на котором можно написать абсолютно любую мыслимую программу. Из этого следует довольно-таки всеобщий тезис: то, что есть в любом наудачу выбранном языке, есть во всех остальных языках. То же можно сказать и про парадигмы. Все отличия языков (и парадигм) — это разные способы реализации тех или иных команд, не считая отдельных лексических особенностей.

Кстати, этот же тезис (всё, что есть в N, есть и в M, и в K, и в R и т.д.) можно сформулировать так: молоток уже состоит из железа да дерева, зачем же нам ещё и пассатижи? Но ведь так никто не станет утверждать.

2. ООП смешивает данные и действия над ними. Это плохо


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

3. Наследование закрепощает программу, делает трудным внесение изменений


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

Если не предполагается расширять родительский класс третьим классом — такое наследование попросту бессмысленно. Если вы создаёте магазин спиртных напитков, то классы Beer, Vodka и Vine можно унаследовать от класса Alcohol, но совершенно не нужно создавать ещё и класс Drinks, если только вы не хотите продавать ещё и, скажем, парагвайский чай.

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

4. Инкапсуляция не имеет смысла


Вот тут я частично согласен. С точки зрения работы программы инкапсуляция действительно ни на что не влияет. Если я закрою переменную с помощью private — ну и что, я всё равно смогу её открыть, просто убрав private, а потом менять там всё, что душе заблагорассудится.

Но это верно лишь чисто технически. Философия ООП гласит: правильно организованный и инкапсулированный класс можно рассматривать как чёрный ящик. Представьте себе коробку, на одной стороне которой разнообразные кнопки, слоты для подачи данных, а на другой — выходной слот, который возвращает информацию. Возьмём, к примеру, стек. Представьте коробку, на одной стороне которой есть один слот для вставки данных и кнопка push рядышком. На обратной стороне — кнопка pop. Вы подаёте туда записку с числом 8 и давите кнопку push. Затем подаёте ещё бумажку и второй раз давите push. И так N раз, а затем жмёте pop. Из ящика вылетает бумажка с числом 76 (или другое, в общем, то, которое вы подали). Нужно ещё число? Второй раз давите pop. И так до морковкина заговенья тех пор, пока ящик не опустеет. А если вы продолжите давить pop, механизм из ящика завоет: стек пуст! Именно так и выглядит объект.

Но после того, как вы создали и настроили класс, вам уже фиолетово, как он там работает — он просто правильно работает, а большего и желать не нужно. А инкапсулируя все эти структуры, вы не держите всё подряд в памяти. Они (множество ящиков) просто общаются между собой так, как вы настроите.

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

Хотя, вряд ли это «увы» здесь вообще уместно.

5. В реальном мире нет иерархий отношения, повсюду лишь иерархии включения


Да разве? Но ведь никто не мешает создать, например, иерархию, где все реки мира (Конго, Сена, Темза, Амазонка, Колыма и т.д.) являются объектами одной всеобъемлющей «Реки», которой присущи свойства (например, состоит из воды) и действия (например, течёт), а уже она будет наследоваться от «Водоёма», который тоже состоит из воды, а от «Водоёма» можно унаследовать ещё и «Озеро», объектами которого будут отдельные озёра (Байкал, Каспийское море, Титикака и т.д.). Схема довольно грубая. Но иерархии отношения — это тоже абстракция. Что-то а-ля платоновской идеи, если хотите. В реальном мире их нет, они существуют только в уме, это обобщение, и не более того. Но ведь именно так человек очень часто мыслит. Мы ведь можем сказать «носок», без уточнения, каков у него цвет, из какого материала соткан и т.д., но существует ли этот «носок» в действительности?

И всё же нас не должно смущать, что нет ни «объекта», ни «носка».

6. Методология ООП изначально ошибочна


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

7. Но даже миллионы мух не убедят нас, что навоз — это вкусно


Самый популярный аргумент против ООП. Мол, массы в большинстве своём глупы (всё же я не думаю, что это относится и к программистам), бегают по «модным шмоткам» и восхищаются ими.
Но задумайтесь, а если бы на пьедестал взошло не ООП, а, скажем, ЛП? Думаете, было бы всё по-другому? Ничего подобного! Нашлись бы и фанаты, и злостные противники, а на ООП смотрели бы как на инструмент (к этому я, вообще-то, и призываю), а не как на таблетку, сотворённую самим Богом и потому незаменимую.

Почему эта статья — в защиту ООП?


Все современные разговоры про парадигмы программирования, как мне видится, сводятся к двум диаметральным посылкам: оставим ООП и выкинем всё остальное, или же выкинем ООП и… ну, вы поняли меня.

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

Если вам не нравится ООП


Кому — ООП, кому — ФП, кому — ПП. А кому-нибудь, может быть, вообще более всего мил свиной хрящик. Если вы не любите кошек — наверное, вы просто не умеете их готовить.

© Habrahabr.ru