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

Что такое хороший код?

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

  • Это код, который работает с минимальным количеством ошибок

  • Это код, который удобно поддерживать и расширять (не важно, будет ли это делать один человек или команда разработчиков)

Классы помогают писать хороший код

Буду рассматривать классы не как необходимый инструмент парадигмы ООП, а как инструмент проектирования системы (и стараться доказать их преимущества перед функциями)

Читаемость

<?php
$Article = new Article($articleId);
$Article->setMeta('');

Банально читается понятнее чем

<?php 
article_set_meta($articleId, '');

Даже без подсветки синтаксиса IDE. А с подсветкой - кратно понятнее.
Да, это одна лишняя строчка кода, но если речь о количестве символов - в масштабах проекта это экономия.
Пример более очевиден с увеличением масштаба

<?php
$Article = new Article($articleId);
$Article->setMeta('')->addImage('')->markAsHot();
$Article->render();

VS

<?php
article_set_meta($articleId, '');
article_add_image($articleId, '');
article_mark_as_hot($articleId);
article_render($articleId);

Организация кода

Структура с нэймспэйсами и классами визуально понятнее

<?php
/Article/Article::create();
/Article/Gallery/Gallery::create();
/* VS */
article_create();
article_gallery_create();

При соблюдении хоть каких-то стандартов код с классами сразу рисует в голове структуру проекта, просто мозг так работает.
Да, любая IDE по клику на метод или функцию найдёт её, но в случае с функциями мозгу не очевидна структура, особенно в большом проекте где 10-ки классов и методов (или сотни функций)

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

Ассоциация с реальными вещами в жизни

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

<?php
$User = new User(); //Посетитель, человек
$Article = new Article($id); // Статья на сайте, читаемый на мониторе текст
$Article->readBy($User); // Статья прочитана человеком 

$user = get_from_session(); // Что-то про сессии
$article = get_article_by_id($id); // Что-то get и id
article_read_by_user($article, $user); // Вроде понятно, но не с первого прочтения

Инкапсуляция, как инструмент скрывания внутреннего функционала

Private и Protected методы нужны не для абстрактных доктрин ООП.

  • Самое банальное - при подстановке кода IDE не будет показывать Private и Protected методы

  • Опять же, про работу мозга - такие методы помогают переключаться с объекта и его внутренних методов на более высокие уровни структуры, когда мы работаем с конкретным объектом - мы можем быстро сфокусироваться на нём, когда на высоком уровне - "забыть" какие-то внутренние методы объекта. С функциями такое просто не получится, потому что они все видны, мозгу сложно держать в памяти их все, также как сложно вспоминать конкретные наборы функций, потому что он перебирает все.

  • А самое главное - без таких методов не получится хорошо спроектировать систему, немного об этом ниже

Шаблоны проектирования

Шаблоны проектирования - набор отточенных годами практик, которые помогают решать сложные задачи, причём таким образом, чтобы было понятно всем (кто знаком с реализованным шаблоном). Без классов(и ООП) они просто не получатся. Конечно, можно сделать их на функциях... Хотя нет, нельзя.

И, немного о архитектуре

Хороший проект - это не просто код, это код, связанный архитектурой. Архитектура задаёт стандарты, помогает разным программистам работать над разными частями, повышает отказоустойчивость системы. Во многом это делается благодаря уровням.

Самый высокий уровень

<?php
$Client = new Client($clientId);
$Product = new Product($productId);
$Invoice = InvoiceBuilder::create($Client);
$Invoice->addProduct($Product, $amount, $sellPrice);
$Invoice->checkout();

На этом уровне мы не обязаны знать что там под капотом. Мы пишем понятную человеческими словами логику. Нам очевидно что мы выписали счёт клиенту. По этому и классы: чисто визуальнее они понятнее чем функции, вот есть счёт, мы что-то в него добавили, и выписали его.

Средний уровень

<?php
class Invoice
{
	public function addProduct(Product $Product, int $amount, int $sellPrice)
	{
		// Добавим товар в счёт
		$this->products[] = [
			'product' => $Product->id,
			'amount' => $amount,
			'sellPrice' => $sellPrice,
		];

		// Запомним выбор клиента, если он не оформит покупку - будем ему спамить этот товар
		$EvilTracker = new EvilTracker($this->Client);
		$EvilTracker->track($Product->id);

		// Создадим запись для отдела аналитики по добавленному товару
		ProductAnalytics::log($Product->id, $amount, $sellPrice, 'added-to-basket');
	}
}

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

  • Реализовывать инкапсуляцию, делать код "модульным" и прятать методы-функции от других классов-модулей синтаксис классов и нэймспэйсов для этого просто удобен.

  • Реализовывать наследование, это очень хорошая штука для разработки и проектирования.

  • Реализовывать шаблоны проектирования.

При хорошей архитектуре мы меняем EvilTracker() на SpareTracker() и всё работает.

Низкий уровень

<?php
class
{
	public function someLowLevelLogic(bool $boolSomething):array
	{
		$someArray = mysql::get_something();
		if ($boolSomething) {
			$someArray = array_map(function ($element) {
				return [
					'id' => $element['id'],
					'title' => $element['title'] . ' ' . $element['bankTitle'],
				];
			}, $someArray);
		}
      returm $someArray;
	}
}

Разделение на уровни и модули (под модулями я имею ввиду /Разные/Нэймспэйсы и наборы классов) помогает делать проекты, которые удобно развивать как в одиночку, так и командами. Лишние пару строк на старте разработки нового функционала помогает экономить часы доработки.

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

Прошу судить строго, так как это помогает двигаться вперёд.

UPD: я был не прав в том что не указал контекст.
Контекст - "при разработке масштабных продуктов на PHP"

Комментарии (110)


  1. rsashka
    17.06.2023 08:14
    +23

    зачем нужны классы в принципе, а именно в чём их преимущество перед функциями

    Это сравнение кислого с мягким


  1. DmitryKoterov
    17.06.2023 08:14
    +10

    Если в 2023 году нужно объяснять СОТРУДНИКУ (т.е. человеку, получающему деньги за написание кода), зачем нужны классы… то это какой-то очень странный сотрудник. Звучит примерно как «пришлось объяснять студенту, что 2+2=4».

    Ну или, либо, куда только катится наша индустрия…


    1. bodyawm
      17.06.2023 08:14
      -1

      А вдруг этим сотрудником был@forthuse или@Gudleifr?)))


    1. zatim
      17.06.2023 08:14
      +10

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


    1. dyadyaSerezha
      17.06.2023 08:14
      -1

      А куда она катится, кстати?


    1. Sadler
      17.06.2023 08:14
      +11

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


    1. SerJook
      17.06.2023 08:14

      Не знаю насчёт классов, а вот что такое юнит-тесты нужно объяснять сотруднику в 2023?


      1. shoorick
        17.06.2023 08:14

        Я как микроначальник, объяснял это коллегам вплоть до 2021-го — они их совсем не применяли (и не уверен, что хоть что-то автоматически тестируют сейчас). А до этого — системы контроля версий.


    1. funca
      17.06.2023 08:14
      +1

      Зазубрить арифметику и понимать ее это немного разные вещи. Теорию чисел, абелевы группы и все вот это.

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


  1. fire_engel
    17.06.2023 08:14
    +21

    во-первых, php -- не лучший пример для демонстрации читаемости.

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

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


    1. pilot114
      17.06.2023 08:14

      Не увидел в примерах пять поколений и перегрузки интерфейсами. И вообще не видел в реальности таких примеров, кроме специально надуманных fuzzBuzz-ов, которыми детей пугают.
      Что не так с читаемостью? Думаю, важно что php тут не выбран, а является данным контекстом. + php гораздо больше ориентирован на ООП чем на функциональщину, вот и всё


      1. Fell-x27
        17.06.2023 08:14
        +8

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

        Функциональное программирование - это совсем другое.


    1. Tony-Sol
      17.06.2023 08:14

      во-первых, php -- не лучший пример для демонстрации читаемости.

      а что не так с читаемостью в php?


  1. MaxBrain
    17.06.2023 08:14
    +5

    Шаблоны проектирования <...> Без классов(и ООП) они просто не получатся. Конечно, можно сделать их на функциях... Хотя нет, нельзя.

    Берём классическую книгу по паттернам проектирования "Приемы объектно-ориентированного проектирования", и на 18 странице читаем

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


    1. breninsul
      17.06.2023 08:14
      +1

      ну паттерн это вообще общее название каких-то общепринятых приемов/подходов, и очевиднейшим образом существуют везде.

      Другое дело, что сложно на pascal представить builder


      1. vadimr
        17.06.2023 08:14
        +2

        Если речь про C++ Builder, то он возник, как перенос Delphi с Паскаля на C++.

        А классическая Mac OS вся была написана на Object Pascal.


        1. usrsse2
          17.06.2023 08:14

          Речь про паттерн builder, а не среду разработки.


          1. vadimr
            17.06.2023 08:14
            +3

            Так это ж просто паттерн. Хоть на ассемблере можно.


            1. breninsul
              17.06.2023 08:14

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


              1. vadimr
                17.06.2023 08:14
                +1

                Да точно так же, руками VMT сделать в виде коллбеков. Хотя в паскале 30 лет как есть объекты.

                В принципе, форт-машина - это и есть процедурная реализация паттерна билдер.


                1. breninsul
                  17.06.2023 08:14

                  честно говоря, не понимаю о чем вы, даже гуглинг не нашел ничего по поводу форт-машины.

                  А Delphi все-же не Pascal, как c++ не c


                  1. aamonster
                    17.06.2023 08:14
                    +8

                    Так объекты в Паскале появились задолго до Дельфи, ещё под DOS (в Borland Pascal или даже ещё раньше, когда он назывался Turbo Pascal). И фреймворки, использующие объекты вовсю, были (Turbo Vision, я когда после него мелкомягкий MFC увидел – впал в тягостное недоумение).


                    1. geher
                      17.06.2023 08:14

                      Тут все же есть нюансы.

                      С одной стороны, из "того самого" Паскаля образовалось много диалектов с классами, вроде даже местами не до конца совместимых между собой: Turbo Pascal, Object Pascal, Delphi, Free Pascal (детали могу путать, поскольку давно это было).

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

                      Вот и получается, что С и С++ - строго разные языки, а про Паскаль и его наследников можно поспорить, что это, отдельные языки или таки просто разные диалекты.

                      Объекты, кстати, появились в Турбо Паскале с версии 5.5.

                      С версии 6.0 в поставке появилась библиотека, использующая объектное расширение языка по полной - Turbo Vision.

                      Borland Pascal 7.0 включал в поставку Turbo Pascal 7.0 (урезанная версия, которая поставлась и отдельно).


                      1. Eugeny1987
                        17.06.2023 08:14

                        мне всегда казалось что Delphi и Object Pascal тесно связаны

                        и гугл про это тоже говорит


                      1. vadimr
                        17.06.2023 08:14

                        Это другой object pascal. И вроде он у Борланда назывался не так, а Pascal with Objects.

                        Object Pascal - разработка Apple.

                        Все они похожи, так как происходят от UCSD Pascal. Но не одинаковы.


                      1. vadimr
                        17.06.2023 08:14


                      1. PuerteMuerte
                        17.06.2023 08:14

                        Delphi - это изначально всего лишь название IDE для языка Object Pascal, и лет через семь - новое маркетинговое название для всего языка в целом.

                        Причём борландовский Object Pascal, это хитрый Object Pascal, который включал в себя две объектные модели, нативную Object Pascal, и классическую из Turbo Pascal with Objects, для совместимости с существующим кодом.


                    1. breninsul
                      17.06.2023 08:14

                      ух-ты, не знал, благодарю за просвещение


                  1. vadimr
                    17.06.2023 08:14

                    Я имею в виду концепцию реализации языка Форт. Так вкратце полностью не объяснить, но смысл в том, что определение слова (т.е., в терминологии ООП, метода) добавляет его в подобную VMT структуру – словарь. И это можно рассматривать, как применение паттерна builder к реализации объекта, реализующего нашу программу. Конечно, Форт – не объектный язык, и в нём нет множественных объектов, но абсолютно ничто не мешает сделать вместо одной форт-машины много маленьких форт-машинок в целях инкапсуляции.


        1. shoorick
          17.06.2023 08:14

          Ну так и Windows когда-то на паскале писали — нам 25 лет назад в универе рассказывали про паскальное наследие в WinAPI.


          1. Videoman
            17.06.2023 08:14

            Windows писался на С. Единственное, что там могло быть от языка Pascal, тип вызова функций - pascal (когда стек зачищает сама вызываемая функция). Так отвечали на этот вопрос сами разработчики, которых спрашивали когда они приезжали на конференцию по Windows, 20 лет назад.


            1. geher
              17.06.2023 08:14

              Тип вызова функций windows api всегда был stdcall. Тип вызова pascal был добавлен в некоторых компиляторах c/c++ для савместимости с типом вызова в паскалевских программах, чтобы можно было использовать объектные модули, созданные из исходников на Паскале и с в одном проекте. В Паскале долго различные типы вызова не поддерживались, был свой, исключительный (возможно все же, что это верно не для всех компиляторов паскаля, и где-то использовался таки обычный stdcall).

              Собственно типы вызова определяют не только кто стек вычищает, но и порядок аргументов функции в стеке, всякие выверты с передачей через регистры (типы register и fastcall, например). И тап pascal вроде отличался от stdcall именно порядком аргументов на стеке (но точно не помню, могу ошибаться).


              1. Videoman
                17.06.2023 08:14

                Да, всё верно. Тип вызова pascal отличается от stdcall противоположным порядком проталкивания аргументов. Я не хотел вдаваться в дебри различных типов вызовов, тем более, что сейчас в x64 многие из них уже не актуальны и унифицированы. Я лишь хотел этим проиллюстрировать откуда могли взяться слухи, что Windows писался на Pascal-е и что разработчикам на конференции задавали уже этот вопрос. Ответ был - Windows писался на С.


              1. Kotofay
                17.06.2023 08:14

                Тип вызова функций windows api всегда был stdcall.

                Откопал код из 1992 года:

                int PASCAL WinMain(hInstance,hPrevInstance,lpCmdLine,nCmdShow) 
                HANDLE hInstance; 
                HANDLE hPrevInstance; 
                LPSTR lpCmdLine; 
                int nCmdShow; 
                { 
                   HWND hWnd; 
                   MSG msg; 
                ...
                
                long FAR PASCAL WndProc(hWnd,message,wParam,lParam)
                HWND     hWnd;
                unsigned message;
                WORD     wParam;
                LONG     lParam;
                {
                   HDC          hDC;
                   TEXTMETRIC   tm;
                   PAINTSTRUCT  ps;
                ...


        1. KosInOff
          17.06.2023 08:14

          builder (строитель) - один из паттернов проектирования


      1. sshikov
        17.06.2023 08:14

        >существуют везде

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

        Все авторы, использующие систему дизайн паттернов в области программирования, отмечают что ее придумал архитектор Кристофер Александер. В своей книге A Pattern Language, изданной в 1977 году, он впервые предложил способ описания знаний в некоторой области с помощью паттернов, каждый из которых описывает проблему-конфликт и способы ее разрешения.


        Отсюда вывод — автор вообще не понимает, что такое шаблоны проектирования в общем виде, и не отличает их от **конкретных** шаблонов, призванных решать общепринятым способом типовые проблемы, возникающие в программе с использованием ООП или там классов. Разумеется, что если нет классов, и нет ООП, то некоторых шаблонов просто не будет — потому что, сюрприз, не будет некоторых типовых проблем. А будут проблемы другие — и другие шаблоны.


  1. rukhi7
    17.06.2023 08:14
    +9

    Прошу судить строго, так как это помогает двигаться вперёд

    Человек который на полном серьезе пытается объяснить почему классы лучше чем функции вряд ли является компетентным и в программировании, и в ООП, и в Шаблонах проектирования, и в архитектуре программных решений, ...

    Есть случаи (контекст) когда нужно-удобнее в каком то смысле использовать просто функции,

    Есть случаи (контекст) когда нужно-удобнее в каком то смысле прятать функции в классы,

    Есть случаи (контекст) когда сделать то или другое просто невозможно,

    ...

    Мне кажется лучше чем аналогия с плоскогубцами и болгаркой подходит аналогия с

    плоскогубцами и металлом из которых они сделаны. Давайте объясним чем плоскогубцы лучше металла (в чурках например), кто возьмется?


    1. Dremkin
      17.06.2023 08:14

      Судить здесь можно либо материал, либо автора. Первое - сложно и долго, второе проще простого, особенно на аналогиях.


  1. GospodinKolhoznik
    17.06.2023 08:14
    +18

    Если бы передо мной стояла задача объяснить сотруднику, чем классы лучше функций, я бы сказал:

    Да ничем. Просто так исторически сложилось. В 80е, в момент активного развития GUI ООП было сильно востребовано, т.к. на ООП удобно описывать gui, а в функциональном стиле его неудобно описывать. Тогда появилось ложное ощущение, что ооп это универсальная серебряная пуля от всех проблем. Все стали напирать на ООП, изучать его и писать на нем. В итоге сейчас большинство программистов знают и практикуют ООП, большинство либ и фреймворков сделано на ООП. Поэтому его и продолжают использовать.

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

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


    1. TimsTims
      17.06.2023 08:14
      +7

      offtop: 1.4k комментариев, 102 голоса за карму, и карма в нуле) Вы господин батька прям балансируете между белым и черным, между добром и злом, огнём и водой, между любителями ООП и функциональщины)


      1. GospodinKolhoznik
        17.06.2023 08:14
        +5

        Мне карму сильно слили во время бурного и офтопного обсуждения одного судебного процесса в США. Там вопрос вообще не касался IT. Просто на эмоциях все были.


      1. aamonster
        17.06.2023 08:14
        +2

        ООП и функциональщина вообще перпендикулярны, можно сочетать)


      1. PuerteMuerte
        17.06.2023 08:14

        Любой пользователь хабра без статей имеет лишь два варианта развития - перейти на сторону зла быть изничтоженным толпой за что-то, несовместимое с общественными нравами, или иметь примерно равное количество голосов в плюс и минус кармы. Каким бы ты ни был хорошим человеком, у тебя без публикаций количество плюсов ограничено "количество минусов + 4". Поэтому данная уравниловка сугубо техническая.


      1. Fell-x27
        17.06.2023 08:14
        +3

        Процедурщина != фукциональщина


    1. aegoroff
      17.06.2023 08:14
      +1

      Да ничем. Просто так исторически сложилось. 

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

      ООП удобно описывать gui

      Тоже спорно - GUI удобнее описывать наверно на языках разметки, вроде HTML, XML, XAML и пр. Логику конечно писать на них не получится, поэтому и выдумывают всякие симбиозы языков. На ООП языках достаточно удобно моделировать ту же бизнес логику.

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

      Полностью согласен

      Как раз таки в ООП архитектура это набор костылей, связанных друг с другом синей изолентой, ну по сравнению с функциональными подходами архитектуры именно так

      тут несогласен - костыли будут или нет, зависит от того, кто это создавал, от его знаний и опыта.


    1. funca
      17.06.2023 08:14
      +1

      Рискую высказать непопулярную точку зрения. Шум вокруг ФП стоит последние лет 40. Какие-то отдельные элементы вошли в массовую культуру, но в целом парадигма так и не стала мейнстримом. Императивный код в итоге проще писать, поддерживать, отлаживать по шагам и расширять.


      1. PuerteMuerte
        17.06.2023 08:14
        +3

        Разбивка кода на функции ещё не подразумевает функциональное программирование, и не факт, что автор имел в виду ФП как альтернативу ООП. Может, он тёплое ламповое ПП подразумевал. :)


    1. PuerteMuerte
      17.06.2023 08:14

      Если бы передо мной стояла задача объяснить сотруднику, чем классы лучше функций, я бы сказал:

      Да ничем. Просто так исторически сложилось.

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


      1. vadimr
        17.06.2023 08:14
        +1

        Не согласен с тем, что классы являются способом обеспечения модульности. Модульность обеспечивают модули. А классы, наоборот, ухудшают модульность за счёт наследования.


    1. Hardcoin
      17.06.2023 08:14

      Вы путаете функции и функциональный подход


    1. fe3dback
      17.06.2023 08:14

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

      К примеру есть у вас сервис, который из абстрактного репозитория получает данные (к примеру книги)

      interface Library {
        getArticles(authorID int64) []Article
      }

      Можно ли заменить это на сигнатуру функции, к примеру: func (authorID int64) []Article? Конечно. Но функция сама по себе не чистая, ей для работы нужен какой-то коннекшен к базе. И где его взять?

      Если прокидывать коннекшен прямо в аргументы вот так:

      getArticles(conn redis.Client, authorID int64) []Article
                       ^

      то мы имеем большую проблему. Код который будет вызывать эту функцию, тоже должен знать про хранилище (т.е про redis). Но зачем бизнес логике знать про это?

      В классах мы можем без проблем разделить передачу аргументов на 2 разных штуки:

      • конструкторы (скрытую явно от остального кода)

      • функции (открытую, публичную часть)

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


      1. vadimr
        17.06.2023 08:14

        Чисто функциональный код вообще не содержит переменных, поэтому не надо ничего прокидывать.

        Но функция сама по себе не чистая

        Именно эта проблема в коде и решается в данном случае хаком в виде применения поля класса.

        Является ли коннекшен к базе атрибутом класса библиотеки, как абстракции проектирования? Вовсе нет.

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

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

        В функциональном стиле есть такой же грязный хак в виде мемоизации.


        1. fe3dback
          17.06.2023 08:14

          это не правда. Переменная класса может быть абстрактная (к примеру интерфейс). Вот тогда и будет у нас абстрактное программирование. Без всяких хаков можно писать сносный solid код на классах и ООП. Даже в том же go, где вообще нет классов, но есть некое подобие ОПП всё с той же целью - иметь возможность абстрактного программирования, внедрения зависимостей, интерфейсы и т.п.

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


  1. allter
    17.06.2023 08:14
    +3

    У меня ощущение, что хотелось подсветить некоторые моменты из DDD. Т.е. спор был не про "классы vs функции", а про "архитектура и декомпозиция vs код-лапша".


  1. nikolz
    17.06.2023 08:14
    +5

    Вообще-то, класс - это расширение понятия структура.

    Если структура - это набор данных, связанных определённым образом,

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

    Если взять указатель(и) на функцию(и) и добавить связанные с этой функцией (функциями) данные, и все это разместить в непрерывный блок памяти, то получим класс.

    ----------------------------------

    Про лучше или хуже...

    Отвечу вопросом на вопрос.

    А что лучше C или C++?


    1. Dremkin
      17.06.2023 08:14

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


  1. longtolik
    17.06.2023 08:14
    +13

    Перефразируем анекдот.

    • ну чем, чем классы лучше?!

    • чем функции!


  1. dyadyaSerezha
    17.06.2023 08:14
    +2

    Это код, который работает с минимальным количеством ошибок

    С минимальным? А три - это куча? (с)


  1. middle
    17.06.2023 08:14
    +4

    The venerable master Qc Na was walking with his student, Anton. Hoping to prompt the master into a discussion, Anton said "Master, I have heard that objects are a very good thing - is this true?" Qc Na looked pityingly at his student and replied, "Foolish pupil - objects are merely a poor man's closures."

    Chastised, Anton took his leave from his master and returned to his cell, intent on studying closures. He carefully read the entire "Lambda: The Ultimate..." series of papers and its cousins, and implemented a small Scheme interpreter with a closure-based object system. He learned much, and looked forward to informing his master of his progress.

    On his next walk with Qc Na, Anton attempted to impress his master by saying "Master, I have diligently studied the matter, and now understand that objects are truly a poor man's closures." Qc Na responded by hitting Anton with his stick, saying "When will you learn? Closures are a poor man's object." At that moment, Anton became enlightened.


  1. zkutch
    17.06.2023 08:14
    +6

    Чтобы написать фразу "Банально читается понятнее чем", это надо быть сильно субъективным.


    1. i86com
      17.06.2023 08:14
      +12

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

      Когда я вижу просто одну строчку:

      article_set_meta($articleId, '');

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

      Но вместо этого приводится пример:

      $Article = new Article($articleId);
      $Article->setMeta('');

      И вот я читаю и понятия не имею - это мы мету сейчас статье поставили или объекту, созданному на основе статьи? Оно уже записалось по месту хранения статьи или нужно ещё отдельно

      $Article->save();

      какой-нибудь дописать?

      И это я ещё после прочтения второго примера и сравнения их. С вершины понимания, так сказать. А когда я в первый раз увидел пример с объектом, там вообще было непонятно, что именно призван делать этот код - поменять существующую статью, или создать на её основе объект (но без меты), или создать чистую новую статью по тому Id.

      С функцией такого разночтения не возникает.


      1. Hardcoin
        17.06.2023 08:14

        Оно уже записалось по месту хранения статьи

        Разве в случае article_set_meta ответ известен заранее? Вы же не будете ожидать, что каждая такая функция будет лезть в базу, это как минимум неэффективно.


        1. Dremkin
          17.06.2023 08:14
          +2

          В случае отправки id сохранение в базе подразумевается в примерно 98% случаев ) Хотя конечно могут быть варианты. Но и с объектом тоже не так сложно - достаточно один раз ознакомиться с шаблоном проектирования


          1. Hardcoin
            17.06.2023 08:14
            -1

            article_set_meta - сохраняем?

            article_set_title - сохраняем?

            article_set_body - ещё раз сохраняем?

            Крайне неэффективный подход.


            1. Dremkin
              17.06.2023 08:14
              +1

              article_set($id_article, ["meta"=>'', "title"=>'', "body"=>'']);

              Ну и множество других вариантов как решить проблему. Но не в этом дело. Сохранение подразумевается по умолчанию.. не переписывать же сервер БД на клиенте :)


              1. Hardcoin
                17.06.2023 08:14
                -1

                Почти любую проблему можно решить. Даже ту, которую создал себе сам. Вы только что отказались от автодополнения в IDE, отказались от типизации, но что получили в замен? Нечего полезного.


                1. Dremkin
                  17.06.2023 08:14
                  +2

                  "Но не в этом дело" вы похоже пропустили, ну ладно. Сейчас мы дойдем до того, что ООП нужно для автодополнения в IDE. Я вот не программировал в IDE уже лет 15 - вот такая неказиста жизнь простого программиста :)


                  1. Hardcoin
                    17.06.2023 08:14

                    Типы вы тоже не использовали 15 лет? :)

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


                    1. Dremkin
                      17.06.2023 08:14

                      Ок, уволите меня, если вдруг станете моим начальником :)


                      1. Hardcoin
                        17.06.2023 08:14
                        +1

                        Подобный довод в технической дискуссии на хабре не уместен.

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


  1. aamonster
    17.06.2023 08:14
    +7

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

    И где тэг "пхп"? Извините, но есть заметная разница между языками в том, где уместно использовать класс, а где функцию или замыкание. Матёрые функциональщики вам бы вообще сказали "классы – это замыкания для бедных" (я такого мнения не придерживаюсь, но посмотреть, как инкапсуляция и т.п. делается через замыкания полезно для общего образования).


    1. Fell-x27
      17.06.2023 08:14
      +5

      Еще есть клиника, когда человек пяткой в грудь бьет, мол, какой он великий оопшник, и у него все по ооп, и ваще ооп это икона. Заглядываешь в код, а там статичный god-класс, в котором сотня статичных методов, которые даже между собой контекстно не связаны, просто "живут" там и все. Зато вызываются модно, по-оопэшному!

      Ну и да, по таким постам очень легко опрелелять джунов. Холиварность за парадигму и выставление парадигмы выше целей реализации, даже если парадигма противоречит решению, это прям маркер.

      А все потому, что на каждых первых курсах их учат, что процедуры это прошлый век, что ООП это самый смузи-современный подход, умалчивая, что он старше этих самых джунов раза в 2,а то и в 3, что ООП для пацанов, остальное для лохов и тд. ООП ИЛИ СМЕРТЬ! То же самое про монолиты vs микросервисы, то же самое про язык1 vs язык2. То же самое про фреймворки. То же самое про стандарты типа datetime vs timestamp. И тд и тп.

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

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


  1. ivankudryavtsev
    17.06.2023 08:14
    +3

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

    Инкапсуляция - это не про классы,

    Наследование переоценено и заменяется интерфейсами. Да и реализация интерфейсов куда понятнее, чем куб, отнаследованный от квадрата, потому что неофит решил, что раз есть x и y, то и z не помешает.

    Полиморфизм... ну интерфейсы решают.


    1. aegoroff
      17.06.2023 08:14

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

      Классы это объединение данных и кода, который с ними работает в одной сущности.

      Инкапсуляция - это не про классы,

      Как раз про них - Слово «инкапсуляция» происходит от латинского in capsula — «размещение в оболочке».

      Полиморфизм... ну интерфейсы решают.

      А чем абстрактный класс не угодил? в пределе, абстрактный класс в котором все члены абстрактны == интерфейс.


      1. ivankudryavtsev
        17.06.2023 08:14

        Классы это объединение данных и кода, который с ними работает в одной сущности.

        Сигнатура функции делает то же самое.

        Инкапсуляция - это про сокрытие внутренностей, которые не нужны потребителю. Это про области видимости, а не ООП и классы.

        А чем абстрактный класс не угодил? в пределе, абстрактный класс в котором все члены абстрактны == интерфейс.

        Мне без разницы. Я просто хотел подчеркнуть, что интерфейс не позволяет начудить, а наследование легко позволяет понаделать заумных топологий, которые приводят к очень сомнительному коду.


        1. aegoroff
          17.06.2023 08:14
          +1

          Сигнатура функции делает то же самое.

          Окей, можете сделать функцию, аналогичную, скажем контейнеру List<T> (линейный список) или HashSet<T> (вырожденная хэш таблица, просто для поиска ключа), где есть общие данные, с которыми можно работать используя разные методы, Add, Get, Delete, Enumerate и т.д.? Думаю что нет, с классом это сильно удобнее т.к. вы его воспринимаете как черный ящик, который делает нужные вам вещи, в описанных примерах это абстракции контейнеров с которыми удобно работать. В случае с функциями - вы будете писать пачку функций, где вам нужно будет либо таскать эти общие данные через параметры, либо держать их где-то в глобальных или неких статических переменных, видимых только в нужной вам области (если говорить про Си - в единице компиляции, т.е. конкретном файле). Это как минимум неудобно

          Инкапсуляция - это про сокрытие внутренностей, которые не нужны потребителю. Это про области видимости,

          Нет, сокрытие данных это сокрытие данных и это просто механизм, помогающий инкапсуляции, инкапсуляция это именно то о чем я сказал. Вон даже википедия согласна со мной https://ru.wikipedia.org/wiki/Инкапсуляция_(программирование)

          В сообществе C++ или Java принято рассматривать инкапсуляцию без сокрытия как неполноценную. Однако, некоторые языки (например, SmalltalkPython) реализуют инкапсуляцию, но не предусматривают возможности сокрытия в принципе. Другие (Standard ML , OCaml) жёстко разделяют эти понятия как ортогональные и предоставляют их в семантически различном виде (см. сокрытие в языке модулей ML).


          1. aamonster
            17.06.2023 08:14
            +2

            Ну, учитывая, что не во всех ОО-языках есть обращение к полям/функциям объекта без явного указания на объект (типа this->my_function) – пачка функций, принимающих объект первым аргументом, не очень-то отличается от функций объекта.


            1. aegoroff
              17.06.2023 08:14

              Хорошо, если вам для работы нужна одна сущность - синтаксис по сложности будет похожий (опустим что эти самые данные кто-то сторонний может поменять неожиданным для вас способом, т.е. не используя спроектированные вами функции). А если это не так? Навскидку можно придумать класс Date, в котором дни, месяцы и годы будут хранится в отдельных полях, и они будут закрыты от внешних пользователей, т.е. с классом можно будет работать только через его методы - добавить день, удалить день, добавить месяц, получить разницу между датами и т.д. Думаю не стоит объяснять насколько удобнее и менее подвержено ошибкам, будет работать с таким классом как с черным ящиком, нежели чем с пачкой функций, каждой из которых нужно будет передавать эти 3 или более параметра


              1. aamonster
                17.06.2023 08:14

                Откуда три или более параметра? Всегда ровно один дополнительный параметр – this. Пока у вас нет виртуальных функций – всё вообще прозрачно.

                (Я, если что, к этому не призываю. Собрать все методы для работы с объектом в одном месте – хорошая идея, да и права на доступ к внутренностям объекта удобнее ограничивать).


                1. aegoroff
                  17.06.2023 08:14

                  Откуда три или более параметра?

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

                  Собрать все методы для работы с объектом в одном месте – хорошая идея, да и права на доступ к внутренностям объекта удобнее ограничивать

                  О чем и речь - это сильно больше чем синтаксический сахар, хотя конечно можно все высокоуровневые языки называть синтаксическим сахаром по сравнению с ассемблером :)


                  1. ivankudryavtsev
                    17.06.2023 08:14
                    +1

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


                    1. aegoroff
                      17.06.2023 08:14

                      Вы почему-то меня поместили на чашу "ооп не надо"

                      Простите, но так показалось сначала :)

                      А я на чаше, "и без ооп все можно сделать"

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


          1. Ares_ekb
            17.06.2023 08:14
            +1

            Окей, можете сделать функцию, аналогичную, скажем контейнеру List<T> (линейный список) или HashSet<T> (вырожденная хэш таблица, просто для поиска ключа), где есть общие данные, с которыми можно работать используя разные методы, Add, Get, Delete, Enumerate и т.д.?

            Легко:

            function ArrayList() {
              const data = [];
              return {
                size: () => data.length,
                append: item => data.push(item),
                get: i => data[i]
              };
            }
            
            const list1 = new ArrayList();
            list1.append(1);
            list1.append(2);
            list1.append(3);
            for (let i = 0; i < list1.size(); i++) {
              console.log(list1.get(i));
            }

            Примерно то же самое будет в каком-нибудь Lisp. Классы - просто синтаксический сахар.


            1. aegoroff
              17.06.2023 08:14
              -1

              Отлично! но это Java(Type)script, а скажем если Java, C#, C++?


              1. Ares_ekb
                17.06.2023 08:14
                +4

                Это конечно такой себе пример:

                class ArrayListAsFunctionTest {
                
                    interface IList<T> {
                        int size();
                        void append(T item);
                        T get(int i);
                    }
                
                    <T> IList<T> newArrayList(int capacity) {
                        var data = new Object[capacity];
                        var size = new int[1];
                        return new IList<>() {
                            public int size() { return size[0]; }
                            public void append(T item) { data[size[0]++] = item; }
                            public T get(int i) { return (T) data[i]; }
                        };
                    }
                
                    @Test
                    void test1() {
                        IList<Integer> list1 = newArrayList(100);
                        list1.append(1);
                        list1.append(2);
                        list1.append(3);
                        for (int i = 0; i < list1.size(); i++) {
                            System.out.println(list1.get(i));
                        }
                    }
                
                }

                В Java мы вынуждены объявить интерфейс. Плюс в newArrayList() создаётся анонимный класс. Ну, это язык такой, в нём даже Hello World без классов не напишешь.

                Но это не значит, что классы - это какая-то фундаментальная штука, без которой не может быть нормального языка. В том же Lisp никаких классов на уровне синтаксиса нет, но зато есть на порядок более мощная объектная система CLOS.

                А в Java/C# классы - это неотъемлемая часть языка, без них ничего не написать. Причём часто это приносит больше проблем, чем пользы. Люди хранят временное состояние в полях объекта: в одном методе что-то пишется в поле, потом вызывается другой метод, в котором это поле читается и т.д. Т.е. поля используются не для хранения долговременного состояния объекта, а просто для обмена данными между методами, типа глобальных переменных в Си, но ограниченных классом - понять что делает такой код очень сложно, плюс гарантированы проблемы с многопоточностью. Или делают какие-то безумные иерархии классов.

                Основа ООП - это инкапсуляция, полиморфизм и наследование.

                Инкапсуляция может достигаться разными способами, для этого не обязательно наличие в языке ключевого слова class. Можно это делать и через замыкания как JavaScript, Lisp и т.д.

                Полиморфизм в принципе тоже работает и без классов.

                Наследование вопрос на сколько часто вообще нужно.


                1. aegoroff
                  17.06.2023 08:14
                  -1

                  Это конечно такой себе пример:

                  Ага, вы фактически использовали те же самые механизмы языка, что и классы (будем считать интерфейс абстрактным классом где все члены абстрактны, т.е. не имеют реализации), только завернув это все в анонимный объект использующий функциональные переменные (AKA лямбды). Я же имел ввиду классику - т.е. если говорить про C# (насчет Java я не знаю), сделать такое на чисто статических методах (которые приближенно можно считать аналогом функций в Си). Вам придется таскать состояние в виде некой структуры через сигнатуры всех методов - очень неуклюже и нет никакого сокрытия данных


            1. geher
              17.06.2023 08:14

              И получаем практически чистое ООП с классами, завернутое в функциональный синтаксис.

              Имеем класс ArrayList (даром что синтаксически он не класс) с методами append, size и get и полем data, которое само объект класса массив. Создается экземпляр класса list1, и пошло-поехало.


              1. aegoroff
                17.06.2023 08:14
                -1

                И получаем практически чистое ООП с классами, завернутое в функциональный синтаксис.

                Объясните дураку, чем это лучше чем рабоче-крестьянские (классические) классы?

                Ну и такие фокусы невозможны в тех языках что я выше упомянул.


                1. geher
                  17.06.2023 08:14
                  +3

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


                  1. aegoroff
                    17.06.2023 08:14

                    Вот! Наверно это самый точный ответ.


              1. aamonster
                17.06.2023 08:14

                Не с классами. Больше похоже на prototype-based, как в JavaScript (разница вылезет, когда решите отнаследоваться от объекта, перекрывая его методы).

                Чтобы "с классами" – надо ещё один уровень косвенности добавить.


              1. Ares_ekb
                17.06.2023 08:14
                +2

                ООП - это одна парадигма программирования из множества. Есть клевая книга "Concepts, Techniques, and Models of Computer Programming" by Peter Van Roy and Seif Haridi, в которой это очень хорошо разложено по полочкам.

                В каждом языке программирования акцент сделан на определенных парадигмах. В Java/C# - это изначально ООП, плюс немного параллельных вычислений, немного ФП, немного метапрограммирования (рефлексия) и т.д. В Prolog - основной акцент на логическом программировании. В Lisp - на метапрограммировании, языково-ориентированном программировании (макросы).

                практически чистое ООП с классами

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

                Мне лично вообще чужд этот подход смотреть на всё через призму объектов. Когда-то давно прочитал статью Пола Грэма "Programming Bottom-Up". И мне ближе всего такой подход, при котором приложение по сути строится из множества микрофреймвоков. Например, я делал инструмент моделирования, в котором были такие микрофреймвоки: 1) для работы с коллекциями, всякие специфические итераторы, чтобы перебирать элементы в обратном порядке и т.д. 2) для работы с геометрическими фигурами 3) библиотечка с разными алгоритмами типа A* 4) для рендеринга UI 5) для парсинга строк 6) для стека команд и т.д.

                Иными словами, выделяем в приложении какие-то осмысленные домены, для каждого из них пишем набор функций. Будут это просто функции или они будут завернуты в классы - без разницы. В итоге приложение состоит из понятных, отдельных, повторно используемых, тестируемых частей.

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

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

                Те же List<T>, HashSet<T> и т.д., которые упоминались выше - это скорее про алгебраические типы данных, чем про ООП.

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

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


          1. ivankudryavtsev
            17.06.2023 08:14

            Вы посмотрите с чего я начал:

            Классы - это синтаксический сахар для фунций и структур

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


            1. aegoroff
              17.06.2023 08:14

              Если не брать во внимание все что связано с наследованием - да, не добавляют.


              1. ivankudryavtsev
                17.06.2023 08:14

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


                1. aegoroff
                  17.06.2023 08:14

                  Все так, только trait-ы могут наследовать друг от друга :) но это совсем не то же самое нежели чем в том же C++


            1. KrauzerKrip
              17.06.2023 08:14

              Не могу чего-то ещё добавить, но хочу отметить, что дядя Боб с вами согласен (немного апелляции к авторитету):

              "Каждая (парадигма) накладывает какие-то дополнительные ограничения, отрицательные по своей сути. Парадигмы говорят нам не столько что делать, сколько чего нельзя делать."

              – Роберт Мартин, "Чистая архитектура"


          1. Kotofay
            17.06.2023 08:14

            можете сделать функцию, аналогичную, скажем контейнеру <...> где есть общие данные, с которыми можно работать <...> Думаю что нет ...

            Это самое простое. На С это реализуется проще чем кажется.
            Гораздо сложнее реализовать на С наследование, перегрузку и виртуальные ф-ии, и невозможно реализовать перегрузку операторов и деструктор.


            1. aegoroff
              17.06.2023 08:14
              +1

              Да, вы правы - я что-то сразу не вспомнил про структуры. Пример тому вот подобный API от APR

              https://apr.apache.org/docs/apr/1.3/group__apr__hash.html

              Все можно, но менее удобно.


              1. Kotofay
                17.06.2023 08:14
                +1

                Да именно так я тоже вспомнил С.


            1. vadimr
              17.06.2023 08:14
              +1

              Перегрузка операторов вообще никак не связана с ООП.

              На Си её сделать нельзя, но на кондовейшем процедурном Фортране, например, можно.

              Ну и не во всех языках вообще есть операторы (operators).


      1. PuerteMuerte
        17.06.2023 08:14
        +1

        А чем абстрактный класс не угодил? в пределе, абстрактный класс в котором все члены абстрактны == интерфейс.

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


    1. dsh2dsh
      17.06.2023 08:14

      Наследование переоценено и заменяется интерфейсами

      Не-а, не заменяется. Берём Go. Вот у меня есть тип А, у которого есть метод А1, который что-то делает и вызывает метод А2. Я делаю тип Б, включающий в себя типа А (грубо скажем наследующий), с методом А2 и хочу, что бы при вызове Б.А1(), который будет на самом деле А.А1(), был вызван Б.А2(), а не А.А2(). И не могу этого добиться, без применения костылей, т.к. кто-то решил, что наследование переоценено.


      1. xadd
        17.06.2023 08:14

        Можно сделать класс A с одним методом A1 и функцией A2 как параметр в конструкторе.


  1. Bigata
    17.06.2023 08:14
    +1

    Статья, где комментарии лучше содержимого. Особенно понравились комментарии от:@GospodinKolhoznik, @aamonster, @ivankudryavtsev . В статье только один плюсик использования классов - инкапсуляция, остальные даже близко не рассматриваются.


  1. myswordishatred
    17.06.2023 08:14

    Какие-то рассуждения сорта "Почему молоток лучше чем отвёртка"


  1. koldyr
    17.06.2023 08:14
    +6

    Как-то однажды знаменитый учитель Кх Ан вышел на прогулку с учеником Антоном. Надеясь разговорить учителя, Антон спросил: "Учитель, слыхал я, что объекты - очень хорошая штука - правда ли это?" Кх Ан посмотрел на ученика с жалостью в глазах и ответил: "Глупый ученик! Объекты - всего лишь замыкания для бедных."

    Пристыженный Антон простился с учителем и вернулся в свою комнату, горя желанием как можно скорее изучить замыкания. Он внимательно прочитал все статьи из серии "Lambda: The Ultimate", и родственные им статьи, и написал небольшой интерпретатор Scheme с объектно-ориентированной системой, основанной на замыканиях. Он многому научился, и с нетерпением ждал случая сообщить учителю о своих успехах.

    Во время следующей прогулки с Кх Аном, Антон, пытаясь произвести хорошее впечатление, сказал: "Учитель, я прилежно изучил этот вопрос, и понимаю теперь, что объекты - воистину замыкания для бедных." Кх Ан в ответ ударил Антона палкой и воскликнул: "Когда же ты чему-то научишься? Замыкания - это объекты для бедных!" В эту секунду Антон обрел просветление.


  1. wng
    17.06.2023 08:14

    count, substr, strpos, is_null - вот это функции.

    У функции есть одна задача - взять аргументы и на из основе вернуть ответ.

    article_set_meta, article_add_image, article_render - это процедуры. Могут и в базу данных залезть, и глобальные переменные менять, и что угодно.

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

    Группировать функции в модули (да хоть в классы, если других средств в языке нет) тоже можно, - да так и делается чаще всего.


  1. Ghost0101
    17.06.2023 08:14

    Ну давайте ещё поспорим, что лучше Windows или Linux без указания контекста применения.


    1. ildarin
      17.06.2023 08:14

      Стабильностью? Простотой использования? Наличием большинства игр?