Почему мне кажется, что студентов учат ООП неправильно

  • Tutorial
Когда я учился в университете мне довольно тяжело было понять ООП (Объектно-ориентированное программирование), сейчас я понимаю, что просто нас учили ООП на не совсем ясных и правильных аналогиях и вообще, кажется, сами преподаватели не совсем понимали, в чем же суть ООП.

image

Вспомните, классические аналогии ООП, вот есть класс Домашние любимцы с методами «голос» и «есть», от него мы наследуем Кошку и Собаку и все хорошо.

Но тут приходит Света и приносит аквариумных рыбок, которые не разговаривают, а потом приходит Вася, которые приносит любимый кактус, которые не только не разговаривает, но и не ест.

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

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

Теперь подумаем как объяснить ООП лучше?

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

image

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

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

Код на Java
public abstract class ЧертежСамолета {  // чертеж нашей универсальной модели самолета;
        public abstract void взлет();
        public void полет() {
            // описание как наш самолет летит
        }
        public abstract void посадка();
 }


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

Код на Java
public interface ТребованияКСамолету { // то что должен уметь наш самолет
        long getМаксимальнуюСкорость();
        double getГрузоподьемность();
        long getПрактическийПотолок();
        void взлет();
        void полет();
        void посадка();
}

public abstract class ЧертежСамолета implements ТребованияКСамолету {
    ...


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

Код на Java
public abstract class ЧертежСамолета {  // чертеж нашей универсальной модели самолета;
        public abstract void взлет(); // виртуальные метод
        public void полет() {
            // описание как наш самолет летит
        }
        public abstract void посадка(); // виртуальные метод
 }


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

Код на Java
public class ЧертежПассажирскогоСамолета extends ЧертежСамолета {  
       public int getКолВоПассажиров() {
              return 120;
        }
        public void взлет() {
            поздороваться_с_пассажирами();
            провести_инструктаж();
            получить_разрешение_на_взлет();
            взлететь():
        }
        ...
}
public class ЧертежВоенногоСамолета  extends ЧертежСамолета {  
       public int getКолВоРакет() {
              return 5;
        }
        public void взлет() {
            получить_приказ();
            доложить_командиру();
            взлететь():
        }
        ...
}


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

Код на Java
ЧертежСамолета тотСамыйПотрепанныйБоингНаКоторомМыЛетелиВТурцию = new ЧертежПассажирскогоСамолета();

тотСамыйПотрепанныйБоингНаКоторомМыЛетелиВТурцию.setПотрепаность(0.99);


image

У каждой модели самолета есть общие параметры, например, процент аварийности. Каждое происшествие с любым самолетом этой модели его может изменить. Это статические методы и переменные класса.

Код на Java
ЧертежПассажирскогоСамолета.setАварийностьМодели(0.0);


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

Код на Java
public class ЧертежПассажирскогоСамолета extends ЧертежСамолета {  
       ...
        public void посадка() {
            предупредить_пассажиров();
            проверить_все_пассажирские_места();
            получить_разрешение_на_посадку();
            сесть():
        }
        ...
}
public class ЧертежВоенногоСамолета  extends ЧертежСамолета {  
        public void посадка() {
            получить_приказ();
            доложить_командиру();
            сесть():
        }
        ...
}


Если мы делаем самолет, но двигатель разрабатывает другая команда, нам проще не указывать его на самом чертеже самолета (тем более что один двигатель может использовать на большом количестве самолетов), а только указать «смотри чертеж такого-то двигателя». Это композиция.

Код на Java
public class ЧертежПассажирскогоСамолета extends ЧертежСамолета {  
       private ЧертежДвигателя двигатель = new ДвигательМ45ФирмыАстинМартин()
        ...
}


Если мы разрабатываем трап, подходящий под этот тип самолета (и, возможно, другие подробные самолеты), пилот самолета при приземлении просит подать именно этот трап. Это будет ассоциация.

Если мы создали модель пассажирского самолета на 120 мест, а потом чуть-чуть доработали увеличив количество мест до 130 (за счет сокращения бизнес класса), нам не потребуется создавать новый чертеж, достаточно лишь указать изменения. Тут же становится легко понятно, почему если поменяется что-то в чертеже модели самолета на 120 мест, так же измениться самолет на 130 мест, так мы не делаем копию чертежей, мы только описываем, что изменилось в проекте. Это наследование.

Код на Java
public class ЧертежПассажирскогоСамолетаНа130Мест extends ЧертежПассажирскогоСамолета {  
       @Override
        public int getКолВоПассажиров() {
              return 130;
        }
        ...
}


Вопрос зачем вообще нужно ООП?

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

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

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

Спасибо за внимание!

P.S. Другие мои статьи (например, шпаргалку по Java SE) можно найти в моем профиле
Такой метод объяснения ООП вам кажется нагляднее и проще чем раньше?

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

Поделиться публикацией
Похожие публикации
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама
Комментарии 472
  • +8

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


    Как то странно объяснять абстрактные классы до того, как объяснены методы и понятие инстанса класса.


    Далее возможные вопросы от слушателей:


    1. Зачем нужно ооп?
    2. Как вот это все переносится на код?
    3. А я еще слышал термины "инкапсуляция", "абстракция", "поле (field) класса". Что это?
    • +8
      … а когда внимательные слушатели спросят:

      4. Почему это конструкторское бюро Боинга поставило заказчику чертежи CRJ-200 Bombardier?

      заодно объяснить, что копирование готового кода со stackoverflow является хоть и распространённой, но не всегда верной практикой.
      • 0

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

        • 0
          Пока вы смеетесь, уже придумали название (example-centric programming, иногда opportunistic programming), пишут научные статьи и разрабатывают инструменты, поддерживающие этот подход :)

          Using the web is integral to an opportunistic approach to programming when focusing on speed and ease of development over code robustness and maintainability.
        • 0
          По моему личному опыту объяснение вида «класс — шаблон для объекта» для новичков — плохое, лучше условно разделить все преимущества и сразу объяснять, зачем нужна та или иная вещь.
          • 0
            можно смело сказить что ООП это формальная модель.
            как в математике система доказательств и терминология.
            помогает более формально думать
          • +3
            А что означает «ВВП полосу»? Может ВПП? Тогда слово «полосу» лишнее. В Целом, идея интересная. Но вот я, к примеру, плохо мыслю абстракциями. Скажите, а вы могли бы попробовать написать сюда пример кода (скажем, на той-же Java), который бы вписывался в вашу концепцию пояснений? Мне вот, как новичку, гораздо проще было бы читать пояснения и видеть код.
            • 0
              Полностью согласен. Я тоже новичок и скажу даже проще — я из объяснений статьи понял совсем мало. Т.е. то, что уже понял до этого — понимание не углубилось, не стало ярче и крепче (инстанс, интерфейс), а то, что не понимал, не понял и сейчас (композиция, ассоциация).

              Автору всё равно спасибо, мне кажется, если расширить пояснения с одного абзаца до одного раздела с несколькими абзацами, с небольшими примерами кода и теми же абстрактными примерами «из жизни КБ Боинг», эта заметка могла бы реально стать «путеводной звездой».
              • 0
                Композиция. У вас есть самолет, у него есть двигатель ДС-900, его разрабатывает первая команда, вторая команда разрабатывает двигатель ДС-1500. Вы в своем чертеже можете использовать как ДС-900 так и ДС-1500.

                public class ЧертежПассажирскогоСамолета extends ЧертежСамолета {  
                       private ЧертежДвигателя двигатель = new ДвигательДС900()
                       ||
                       private ЧертежДвигателя двигатель = new ДвигательДС1500()
                }
                

                PS возможно более понятный пример получится. Есть класс таблицы расчета зарплаты. У нее есть ViewEngine первый вариант реализации выведет таблицу как в excel, а второй вариант выведет в текстовом формате в консоль, при этом никаких дополнительных изменений кроме выбора вариации ViewEngine вам не нужно.

                Скрытый текст
                — |column |
                — | param 1 |
                — | param 2 |
                — | param 3 |
                • 0
                  В данном случае необходимо вводить интерфейс СовместимыйДвигатель.

                  class PassengerAirCraft extends AirCraft {
                      /**
                       * При проектировании указывается совместимость с двигателями
                       */
                      private EngineInterface $engine;
                      /**
                       * Конструктор это и есть наш сборочный конвейер,
                       * который получает в требованиях модель двигателя
                       * @param $engineModel
                       */
                      public function __construct($engineModel) {
                          /**
                           * Фабрика поставляет нам необходимую модель двигателя
                           */
                          $this->engine = EngineFactory::provide($engineModel);
                      }
                  }
                  

            • +4
              Одна проблема — объяснить что это. Другая — объяснить зачем всё это вообще нужно.
              • +4
                Совершенно верно, люди не понимают что такое ООП, потому что они начинают с небольших проектов, в которых у них и так всё по полочкам, но им говорят, ваш проект вырастет, делайте сразу хорошо, они долго думают, как это должно быть, что бы было правильно в ООП, а как только проект начинает расти, внезапно вся «раскладка» на ООП, становится другой, а потом ещё раз… И тогда, начинается искреннее недопонимание, почему изначальный ООП код не развивается, а мешает развиваться, причём все говорят, что вы сделали неправильно «первую раскладку», вы не понимаете ООП. Вот, последнее непонимание и важно, а не как сделать в первый раз. И поверьте, ваши «чертежи» и самолёты в этом никак не помогут.
                • 0

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

              • +2

                Хм… а как насчет ООП в случаи, когда классов нет? То же прототипно-ориентированное (пример, javascript).
                Или вот есть пример Golang, есть ли там ООП или нет?

                • –4
                  ООП — одна из разновидностей композиции (проектирование) систем.

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

                  Развитие ООП в JS смысла не имеет вовсе. Ну, если только — да, у нас не ахти какое ООП но пусть будет хоть такое — может кому и такое может хватить по жизни его (в разработке его), — ну, а если не хватит, то уж тогда использовать стороннюю библиотеку js и там уж и оттянуться с ООП уж так как никому (другим языкам с ООП) и не снилось.
                  • +5

                    Это я к тому, что ООП — это ООП, а классо-ориентированное программирование всего лишь подвид ООП.


                    В JS ооп есть, а классов нет (не было). Вот так вот это работает.

                    • –6
                      ООП — это ООП, а в JS ОП.
                      • 0
                        JS как бы мультипарадигменный язык и там можно применять ООП, а можно и не применять. Как в Python или C++.
                        • +1
                          Простите, а что такое ОП? Объектное программирование? Сами придумали?

                          Частным случаем ООП является «Прототипное программирование». Оно и есть в JS.
                          • –2

                            Объектное программирование — подмножество объектно-ориентированного без наследования.


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

                            • 0
                              Прототипы — это разновидность наследования, а не альтернативный механизм.
                              • 0

                                Не все источники с этим согласны.

                      • 0
                        вы можете использовать… стороннюю библиотеку JS, которая позволяет вам организовать ООП и прочее гораздо круче и разнообразнее чем во всех языках где ООП объявлено на стадии описания самого языка

                        А что за библиотека?

                        • +1

                          Я ещё не гуглил, но вангую class.js object.js или oop.js :) Если какая-то библиотека может быть написана на JS, она будет написана на JS :)

                          • 0
                            LionZXY
                            Я ещё не гуглил, но вангую class.js object.js или oop.js
                            Почти, но нет! ;-)

                            «Благодаря силе прототипов JavaScript получился на редкость гибким. На волне вдохновения разработчики создали огромное количество библиотек со своими собственными объектными моделями. Популярная библиотека Stampit выжимает из прототипной системы всё возможное для того, чтобы манипулировать объектами так, как это невозможно в традиционных языках, базирующихся на классах.»

                            Ну и сама Stampit и примеры.

                            И вот как быть дальше с ООП в JS, "втянуть в себя", в стандарт JS библиотеку Stampit? — Но как?
                            JS — не модульный — в том смысле что в нём нет «пакетов» как в Java (в которой, к примеру, при обновлении версии Java, «втянули» пакет стороннего разработчика для удобного использования программирования потоков, который(пакет) фактически ничего не изменил на нижнем уровне (Thread.suspend(), Thread.resume() и прочие), но оказался удобен).

                            • 0

                              Для JS есть стандартная библиотека, как минимум. Не считая API типа WebSockets

                              • –2
                                Для JS есть стандартная библиотека, как минимум.
                                Не понял вас. В JS вообще нет такого понятия как «стандартная библиотека» — Понятие «стандартная библиотека» это из мира C, С++, Java и прочих.
                                • 0

                                  Понятия «стандартная библиотека» нет, а Standard Build-in Objects есть.

                                  • –2
                                    staticlab
                                    Понятия «стандартная библиотека» нет, а Standard Build-in Objects есть.

                                    Понимаете в «Standard Build-in Objects» нет ни намёка на слово «library» или «file».

                                    Ну нет такого понятия как «стандартная библиотека» в JS, хоть плач!
                                    • +1

                                      Ну и что? Я знаю, что вы любитель придираться именно к словам, но какая разница?

                                      • +1

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

                                        • –1
                                          staticlab
                                          Ну и что? Я знаю, что вы любитель придираться именно к словам, но какая разница?
                                          Так я же выше пояснил, так как в JS нет стандартных библиотек, так что запросто добавить в JS какую-нибудь стандартную библиотеку нельзя! Хоть плачь.


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

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

                                          • 0
                                            Так я же выше пояснил, так как в JS нет стандартных библиотек, так что запросто добавить в JS какую-нибудь стандартную библиотеку нельзя! Хоть плачь.

                                            JS — не модульный — в том смысле что в нём нет «пакетов» как в Java (в которой, к примеру, при обновлении версии Java, «втянули» пакет стороннего разработчика для удобного использования программирования потоков

                                            Пишите proposal к стандарту, продвигайте его в TC39, и будет вам счастье.


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

                                            • 0
                                              staticlab
                                              Пишите proposal к стандарту, продвигайте его в TC39, и будет вам счастье.
                                              Написано, не принято. И это хорошо! :-)

                                              ECMAScript 4
                                              В самый разгар разработки ECMAScript 4 включал такие опции как:

                                              Классы
                                              Интерфейсы
                                              Пространства имён
                                              Пакеты
                                              Опциональные аннотации типов
                                              Опциональная статическая проверка типов
                                              Структурные типы
                                              Объявления типов
                                              Мультиметоды
                                              Параметризованные типы
                                              Хвостовые рекурсии
                                              Итераторы
                                              Генераторы
                                              Интроспекция
                                              Разбор типа для обработчиков исключений
                                              Связывание констант
                                              Правильный обзор блоков
                                              Деструктуризация
                                              Сжатые функциональные выражения
                                              Поддержка массивов



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

                                              Аналогично и по идеям из иных библиотек, типа Underscore.js и прочих и прочих…

                                              Зачем всё это тащить в стандарты JS — цель? — единственная разумная цель — тащить то, что может эффективно реализовано в NATIVE функциях JS-движков, используемых в броузерах и средах типа Node.js

                                              Ну, а стандартных библиотек в JS нет. (С)
                                              • +2

                                                Тем не менее, стандартная библиотека в JS есть — это стандартные классы и методы, описанные в стандарте:


                                                The ECMAScript library of built-ins was expanded to support additional data abstractions including maps, sets, and arrays of binary numeric values as well as additional support for Unicode supplemental characters in strings and regular expressions. The built-ins were also made extensible via subclassing. The sixth edition provides the foundation for regular, incremental language and library enhancements.

                                                Clauses 17-26 define the ECMAScript standard library. It includes the definitions of all of the standard objects that are available for use by ECMAScript programs as they execute.
                                                • –1
                                                  staticlab
                                                  The ECMAScript library of built-ins was expanded to support additional data abstractions including maps, sets, and arrays of binary numeric values…
                                                  Точно, определить что стандартная библиотека — это не файл или не набор битов, который централизованно поставляется создателем (или дистрибутером) библиотеки, а это описание стандарта! — Это круто!

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

                                                  Определение — сила. Определите что ваши пожелания есть библиотека — и вы… на коне! ;-)
                                                  • +1

                                                    Что за наркомания? Какое ещё описание стандарта? Откуда вы его взяли?

                                                    • –1
                                                      staticlab
                                                      Какое ещё описание стандарта?

                                                      Обычное, на их сайте размещённое: This Ecma Standard defines the ECMAScript 2015 Language. It is the sixth edition of the ECMAScript Language Specification.

                                                      А вы бред про «The ECMAScript library...» где изволили откопать то?

                                                      • 0
                                                        Обычное, на их сайте размещённое

                                                        Вы читать по английски не умеете или только слова из контекста выдёргивать научились?


                                                        «Данный стандарт Ecma описывает язык ECMAScript 2015. Это шестая редакция спецификации языка ECMAScript.»


                                                        А вы бред про «The ECMAScript library...» где изволили откопать то?

                                                        Я же дал ссылку.

                                                        • –1
                                                          staticlab
                                                          «Данный стандарт Ecma описывает язык ECMAScript 2015. Это шестая редакция спецификации языка ECMAScript.»

                                                          Нет, не так:
                                                          «Данный стандарт Ecma описывает [спецификацию] язык[a] ECMAScript 2015. Это шестая редакция спецификации языка ECMAScript.»


                                                          staticlab
                                                          Я же дал ссылку.

                                                          Точно, ссылка ведёт на сайт www.ecma-international.org

                                                          4.4Organization of This Specification#

                                                          Clauses 17-26 define the ECMAScript standard library. It includes the definitions of all of the standard objects that are available for use by ECMAScript programs as they execute.

                                                          the definitions — определение, описание.

                                                          То есть если распечатать по функциям эту ECMAScript standard library — и каждую распечатку в коленкор и на полку то и будет вам ECMAScript standard library!

                                                          Ну, а стандартных библиотек, которые вы могли бы скачать откуда-нибудь и непосредственно использовать в своём коде в JS нет. (С)
                                                          • +1
                                                            Ну, а стандартных библиотек, которые вы могли бы скачать откуда-нибудь

                                                            Зачем её качать отдельно, если она стандартная? И множество классов из неё по определению есть "во всех реализациях языка"? Например, в составе Java есть стандартная библиотека с множеством классов, таких как java.lang.Object, java.util.ArrayList, java.util.Date, java.util.concurrent.FutureTask и т.п. В .NET это System.Object, System.Collections.ArrayList, System.DateTime, System.Threading.Tasks.Task. В Ruby это Object, Array, Time. В C++ это std::vector, std::chrono::*, std::future. В JS — Object, Array, Date, Promise.


                                                            Согласен, для такого довольно низкоуровневого языка как C++ возможна независимая стандартная библиотека, но в большинстве случаев её реализация привязана к среде исполнения. А вот совместимость как раз декларируется стандартом. На то он и стандарт.

                                                            • 0
                                                              Кстати, если на то пошло, то некоторые части этой библиотеки вполне поддаются скачиванию… Например, в виде пакета core-js.
                                                              • –1
                                                                staticlab
                                                                Зачем её качать отдельно, если она стандартная?
                                                                Чтобы использовать.

                                                                staticlab
                                                                И множество классов из неё по определению есть «во всех реализациях языка»?
                                                                Этого не требуется по определению.

                                                                @staticlab
                                                                Согласен, для такого довольно низкоуровневого языка как C++ возможна независимая стандартная библиотека, но в большинстве случаев её реализация привязана к среде исполнения.
                                                                Но вы можете все их скачать из одного источника. — В случае JS скачивать нечего и негде.

                                                                mayorovp
                                                                если на то пошло, то некоторые части этой библиотеки вполне поддаются скачиванию… Например, в виде пакета core-js.
                                                                Это не стандартая библиотека JS и не часть её — это набор полифилов — то есть "временных костылей" для некоторых функций (методов).

                                                                Стандартных библиотек в JS нет. (С)
                                                                • 0
                                                                  Этого не требуется по определению.

                                                                  Перечитайте ещё раз определение стандартной библиотеки из Википедии, которое вы же сами и процитировали.


                                                                  Но вы можете все их скачать из одного источника.

                                                                  Это не так.


                                                                  В случае JS скачивать нечего и негде.

                                                                  Реализации стандартных библиотек для движков V8 и SpiderMonkey скачать можно, но они написаны на C++. Реализации полифиллов тоже скачать можно. Опять-таки, наличие независимых реализаций для стандартных библиотек не требуется.


                                                                  это набор полифилов — то есть "временных костылей" для некоторых функций (методов).

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

                                                                  • –1
                                                                    staticlab
                                                                    Реализации стандартных библиотек для движков V8 и SpiderMonkey скачать можно, но они написаны на C++.
                                                                    Нет, неверно написано вами, более полно и правильно надо писать так:

                                                                    Библиотеки, реализующее некоторые элементы стандарта JS для движков V8 и SpiderMonkey, скачать можно.

                                                                    Стандартных библиотек в JS нет. (С)
                                                                    • 0

                                                                      То есть с остальным вы не спорите, но всё равно упираетесь?

                                                                      • –2
                                                                        То есть с остальным вы не спорите, но всё равно упираетесь?

                                                                        Мы начали с Стандартных библиотек в JS нет. (С)
                                                                        И закончили Стандартных библиотек в JS нет. (С)

                                                                        Всяческие попытки это опротестовать оказались (имхо) никчемными или отвлекающими. Не более того.

                                                                        • +2

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

                                                                          • –2
                                                                            Вашим тролльным способом можно доказать всё, что угодно.
                                                                            Не будем развешивать ярлыки.
                                                                            Вот, к примеру урл на стандартные Java библиотеки. Качайте и работайте.

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

                                                                            Если такого урла нет — вы троль. (С)

                                                                            • +1
                                                                              Мимо проходил

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

                                                                              нате
                                                                              Спойлер
                                                                              Да, это ссылка на скачивания гугла хром, но как виртуальная машина Java это намного больше, чем просто стандартная библиотека Java (какому-нибудь JPython'у может быть нафиг не нужна эта библиотека), так и хром поддерживает определенный стандарт JS. Не вижу никакой разницы в данном случае. И да, между прочим реализаций Java тоже несколько.

                                                                              В случае JS скачивать нечего и негде.

                                                                              Качайте:
                                                                              1. браузеры,
                                                                              2. nodeJs и т.п.

                                                                              И там и там есть своя реализация стандарта JS (разная реализация во многом, но Java реализации J# в Net, Java в андроиде, у Oracle и OpenJDK тоже совсем не одинаковые)
                                                                              • –2
                                                                                vedenin1980
                                                                                Да, это ссылка на скачивания гугла хром, но как виртуальная машина Java это намного больше, чем просто стандартная библиотека Java (какому-нибудь JPython'у может быть нафиг не нужна эта библиотека), так и хром поддерживает определенный стандарт JS. Не вижу никакой разницы в данном случае.

                                                                                Вот и ссылка появилась на… браузер хрома. :-(

                                                                                Меня один уверял, что между Java и JavaScript разницы он не видит. По крайней мере четыре первые буквы совпадают и оператор «new» там и там есть! ;-)

                                                                                vedenin1980
                                                                                В случае JS скачивать нечего и негде.
                                                                                — А вот staticlab как-то сомневается в этом.

                                                                                vedenin1980
                                                                                Качайте:
                                                                                1. браузеры,
                                                                                2. nodeJs и т.п.

                                                                                И там и там есть своя реализация стандарта JS (разная реализация во многом, но Java реализации J# в Net, Java в андроиде, у Oracle и OpenJDK тоже совсем не одинаковые)

                                                                                Есть некая разница между «разными (и частичными) реализациями стандарта» и «отсутствием стандартной библиотеки вообще»! — Многие её не улавливают. А она… есть. :-)
                            • 0
                              Блин, придется удалять весь код теперь. Было так удобно писать поддерживаемый и читаемый код, но вы открыли мне глаза! Спасибо!
                              • +1

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


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

                              • +7
                                ООП — оно не в языке, а в голове разработчика.
                                • +2

                                  Добавлю: ООП широко применяется в ядре Linux и "оконной" части WinAPI несмотря на полное отсутствие языковой поддержки.

                                  • 0

                                    Не не полное — есть структуры и в них можно хранить указатели на функции :)

                                    • 0
                                      Всегда думал, что классы по своей сути — синтаксический сахарок для этого.
                                      • 0
                                        Не совсем. Указатели — это виртуальные методы. Обычные — это просто функции с неявным this-аргументом, возможно, перегруженные. А ещё правила каста указателей при наследовании, и это важнее всего остального по сути.
                                        • 0

                                          Ну конкретно в Си в структуру нельзя засунуть обычную функцию, только указатель на функцию. Потому я достаточно долго думал, что "Си с классами" добавляет сахару. Но да, дело совсем в другом.

                                          • 0
                                            Я немного не о том.
                                            Вызов метода предполагает передачу this. Указатель на функцию, которая не примет объекта в качестве параметра, нельзя считать вызовом метода, потому что она не имеет контекста (this).
                                            • +1

                                              Что мешает в простом C передавать первым параметром указатель на структуру?

                                              • 0
                                                Ничего. Но тогда нет необходимости таскать за собой и указатель на функцию, если нет задачи сделать её виртуальной.
                                • 0

                                  В Javascript классы есть, причем начиная с ES2015 даже на уровне синтаксиса. Чего там и правда нет — так это переопределения членов, когда в базовом классе this.foo это одно, а в наследнике this.foo это что-то другое и друг с другом они никак не связаны. Но на уровне "чертежей и самолетов" никаких отличий между Javascript-классами, С++-классами и Java-классами не наблюдается (за исключением того факта что в Javascript можно "унаследовать" не только чертеж, но готовое изделие — однако "можно" не означает что так нужно делать).

                                  • 0
                                    Не знаю, как сделаны классы в ES2015, но до него обычно это были костыли-обертки над прототипным наследованием.

                                    Ну и да, вы бы хотя бы привели в пример тот же Python, потому что различия между классами в JavaScript и C++ катастрофические. Например, нет ограничения доступа для переменных.
                                    • +3

                                      Ограничения доступа — прежде всего в голове у программиста. Без них он напишет #define private public и будет радоваться как круто он все устроил. Или применит заклинание Звезды Пустоты. Или напишет Паблика Морозова...


                                      Что же до костылей — то костыль был только один:


                                          function temp() {}
                                          temp.prototype = Foo.prototype;
                                          Bar.prototype = new temp();

                                      Потом в ES5 его внесли в стандартную библиотеку, обозвав Object.create. Все остальное — просто реализация ООП.

                                      • 0
                                        По мне как раз возможность ограничения доступа на уровне языка важна, ведь по сути без ограничений доступа только по коду непонятно будет где интерфейс, а где реализация. Наличие и соблюдение контрактов важно тем что, имхо: 1) снижает сложность системы за счет вынесения в паблик только специально предназначенных для этого вещей; 2) без костылей в виде комментариев или памяти программиста с ходу позволяет использовать параметрический полиморфизм над объектами с одинаковым контрактом; 3) облегчает инструментальное взаимодействие с кодом и использование той же контекстной подсказки.
                                        • 0

                                          Важна она как средство принудительного дисциплинирования прежде всего.

                                          • 0
                                            Ну, как по мне наоборот расслабляет когда можешь рассчитывать на интерфейс модуля зная что вряд ли нарвешься на временную связность например при использовании его процедур и функций, или что все нужные зависимости у него установлены (либо будут получены во время вызова метода откуда нибудь) а также то что публичные методы писались хоть более менее в расчете на то что будут использоваться без понимания деталей их работы, тогда как при написании приватных можно сделать больше допущений о параметрах и вариантах поведения и дать абстракции немного «протечь»
                                            • 0

                                              Вас расслабляет, что разработчики модуля соблюдали дисциплину :)

                                          • 0
                                            Что то загнул с параметрическим полиморфизмом, в голове что то вывернулось. Любой конечно же.
                                            • 0
                                              Да, контракты приходится хранить где-то еще. В документации, например. И я тоже считаю что это недостаток языка.

                                              Но отсутствие контрактов не означает отсутствия классов или отсутствия ООП.
                                              • 0
                                                Но отсутствие контрактов не означает отсутствия классов или отсутствия ООП.

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

                                                Я как раз считаю что программист не должен помнить контракты для своих классов и предугадывать для чужих, а для этого должны использоваться средства языка.
                                                • +1
                                                  Вместо средств языка можно использовать соглашения об именовании. Например, подчеркивание в начале имени означает приватное свойство.
                                                  • 0
                                                    Но это по сути уже эмуляция средств языка путем соглашений, не лучше ли непосредственно определять интерфейс чем добавлять подчеркивания в имена свойств и методов? Конкретный пример приведенный плох еще тем что по умолчанию подразумевается что все методы и свойства публичны, что не есть хорошо.
                                                    • 0

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

                                          • 0

                                            В Python, кстати, тоже нет ограничения доступа для полей. И ничего, никто не умер.

                                            • 0
                                              В ассемблере еще меньше ограничений, и никто от этого не умирает :)
                                              • 0

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

                                              • 0
                                                На самом деле небольшие есть) Но это не важно, как бы, я больше возражал фразе «классы в javascript почти такие же, как в C++».
                                            • 0
                                              Вообще-то в js классов нет и не было. То что вы называете классами в ES2015 — это синтаксический сахар, под капотом в себе кроет старые добрые прототипы.
                                              • +1
                                                С вашей точки зрения в компьютерном мире нет вообще ничего. Это всё синтаксический сахар над нулями и единицами.
                                              • 0
                                                Чего там и правда нет — так это переопределения членов, когда в базовом классе this.foo это одно, а в наследнике this.foo

                                                Как же нет? Есть даже ключевое слово super, чтобы из наследника вызывать метод родителя:


                                                class A {
                                                    foo() {
                                                        console.log('A.foo()');
                                                    }
                                                }
                                                
                                                class B extends A {
                                                    foo() {
                                                        console.log('B.foo()');
                                                        super.foo();
                                                    }
                                                }
                                                
                                                (new B).foo();
                                                
                                                • 0

                                                  А теперь попробуйте отключить полиморфизм, чтобы получить доступ к foo() из класса A имея объект класса B :-)

                                                  • 0

                                                    Позднее связывание имеете в виду или что?

                                                    • +1
                                                      class A {
                                                          name = "world";
                                                      
                                                          hello() {
                                                              return $"Hello, {this.name}!";
                                                          }
                                                      }
                                                      class B extends A {
                                                          name = "B";
                                                      
                                                          who() {
                                                              return $"I am {this.name}";
                                                          }
                                                      }
                                                      
                                                      var b = new B();
                                                      console.log(b.hello()); // Ну и куда тут надо было super засунуть? :-)
                                                      
                                                      • 0
                                                        class A {
                                                            name = "world";
                                                        
                                                            hello() {
                                                                return $"Hello, {this.name}!";
                                                            }
                                                        }
                                                        class B extends A {
                                                            constructor() {
                                                              	super(); // В конструктор? Хотя вызов работает и без него.
                                                            }
                                                          
                                                            name = "B";
                                                        
                                                            who() {
                                                                return $"I am {this.name}";
                                                            }
                                                        }
                                                        
                                                        var b = new B();
                                                        console.log(b.hello()); // Ну и куда тут надо было super засунуть? :-)
                                                        
                                                        • 0
                                                          Да где работает-то? Если исправить синтаксические ошибки, то в консоль код выведет «Hello, B!». А надо было — «Hello, world!».

                                                          Да, вот исправленный неработающий код:

                                                          class A {
                                                              constructor() {
                                                              	this.name = "world";
                                                              }
                                                          
                                                              hello() {
                                                                  return `Hello, ${this.name}!`;
                                                              }
                                                          }
                                                          class B extends A {
                                                              constructor() {
                                                                  super();
                                                                  this.name = "B";
                                                              }
                                                            
                                                              who() {
                                                                  return `I am ${this.name}`;
                                                              }
                                                          }
                                                          
                                                          var b = new B();
                                                          console.log(b.hello()); // Ну и куда тут надо было super засунуть? :-)
                                                          
                                              • 0
                                                В Golang есть все возможности ООП, хотя и под несколько непривычным соусом. Структуры + методы структур = класс, есть интерфейсы, есть инкапсуляция, нет наследования, но есть композиция, которая даёт практически те же возможности( в терминах статьи, например, ЧертёжПассажирскогоСамолёта включал бы в себя ЧертёжСамолёта в виде композиции, по сути наследуя с возможностью переопределения все его методы). Тут на хабре было несколько хороших статей на эту тему.
                                                • +1

                                                  Проблема в том что сами авторы Go как бы не уверены. Поэтому все про это и спорят.

                                                  • 0
                                                    Ну собственно, всё зависит от того, какое определение ООП брать. На мой взгляд, Go реализует нужные возможности, называя их по другому. Но поскольку мнений о том, что считать ООП — дюже много, некоторым из определений Go не соответствует.
                                              • +11

                                                Читал и думал: как хорошо быть старпёром, который учился программировать, когда про ООП ещё толком не слышали, и все споры были "сверху вверх" vs "снизу вверх".
                                                В итоге, когда оно пришло в наш кишлак, все вопросы были — "а как оно устроено". Прочитав про VMT — успокоился и вопросов больше не имел, пока не столкнулся с множественным наследованием в C++ — ибо не понимал, как оно сделано (кстати, убедился, что мои вопросы были обоснованы, когда последующие языки забанили множественное наследование от классов, разрешив только от интерфейсов и от микс-инов, это насквозь понятно).


                                                Единственная проблема — при виде 15 слоёв абстракции начинаешь поминать "Яву головного мозга".

                                                • –7
                                                  Угу, как старпер, подтверждаю.

                                                  Мне все больше и больше нравится мнение моего бывшего коллеги "ООП – неизменно стабильный результат"

                                                  Цитата для затравки
                                                  Учебники по ООП полны примеров, как легко и красиво решается задачка отображения геометрических фигур на холсте с одним абстрактным предком и виртуальной функцией показа. Но стоит применить такой подход к объектам реального мира, как возникнет необходимость во множественном наследовании от сотни разношёрстных абстрактных заготовок. Объект «книга» в приложении для библиотеки должен обладать свойствами «абстрактного печатного издания», в магазине – «абстрактного товара», в музее – «абстрактного экспоната», в редакции, типографии, в службе доставки… Можете продолжить сами.
                                                  • +9

                                                    Цитата — классическая подмена понятий: проблемы архитектуры, построенной на наследовании, выдаются за проблемы ООП. При этом, как я уже в соседнем комментарии написал, наследование в ООП вообще необязательно. Да и в контексте языков, поддерживающих наследование, общеизвестен принцип "Composition over inheritance", и чуть менее общеизвестен, но тоже неплох, принцип "Abstract or final".

                                                    • –7
                                                      Давайте не будем спорить о терминах, а возьмем их из словаря. В общеупотребительном определении ООП наследование является обязательным признаком. Если у вас есть иное авторитетное определение — дайте на него ссылку.

                                                      А то, что вы описали в своем комментарии обычно называется модульным программированием.
                                                      • +5

                                                        Мнение человека, который придумал термин "ООП", достаточно авторитетно для вас? :-)


                                                        http://www.purl.org/stefan_ram/pub/doc_kay_oop_en

                                                        • –8
                                                          Ну примерно как мнение братьев Черепановых относительно современного локомотива. :-) Ещё больше беды в том, что мнение не является определением.

                                                          Но спорить о терминах не буду. Если вы найдете словарь или стандарт с устраивающим вас определением — пользуйтесь им.

                                                          Смешной факт
                                                          Тот, кто придумал слово ВУЗ, был твердо уверен, что ВУЗ — заведение, то есть женского рода. Но увы, русский язык решил иначе. Примерно так же и с ООП — большинство людей под ним имеют ввиду нечто с виртуальным наследованием.

                                                          Ещё можете посмотреть, что значили разные ругательства в тот момент, когда эти слова впервые появились в языке. Очень много интересного для себя откроете.
                                                          • +5

                                                            Строгого определения ООП автор Smalltalk не давал (впрочем, то, что он пишет в конце своего письма, вполне можно считать определением). Как, впрочем, такого определения и не давали авторы языка Simula. Оба языка при этом примерно одновременно ввели термин "объект". Соответственно, можно говорить о двух "школах" — "Смоллтолковской" и "Симуловской".


                                                            Появившийся позднее язык С++ был явным последователем Simula-школы, и именно в те времена — благодаря тому, что С++ был долгое время самым популярным объектно-ориентированным языком — в массовом сознании закрепился сформулированной Страуструпом триплет "инкапсуляция-последование-полиморфизм" и стал считаться чем-то вроде определения. Примерно в то же время появился и другой основанный на C язык, известный в то время в основном только немногочисленным обладателям компьютеров NeXT, следовавший принципам Smalltalk… :-)


                                                            Что касается определения.


                                                            Во-первых, предлагаю смотреть не в википедию курильщика, а в википедию здорового человека — то есть, в английскую. Никакого упоминания необходимости и достаточности свойств из того самого "триплета Страуструпа" вы там не найдете: они, несомненно, перечислены, но только в общем ряду других свойств некоторых объектно-ориентированных языков.


                                                            Во-вторых, сравним высказывание Алана Кея и Страуструповский триплет. Вот что пишет Алан Кей:


                                                            OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things.

                                                            Вызов метода можно считать частным случаем Messaging. "local retention and protection and hiding of state-process" — по сути, инкапсуляция. "extreme late-binding of all things" — по сути, полиморфизм.


                                                            Итого, общими являются инкапсуляция и полиморфизм. Наследования у Кея нет.


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


                                                            Логичный вывод: наследование не является обязательным признаком ООП-языка.

                                                            • –3
                                                              Вам очень хочется спорить о смысле слов? Ну спорьте, если найдете себе оппонента. Вы не понимаете главного — у слов нету одного единственного верного определения. А как только вы это поймете — вы придете к выводу, что никакого смысла в этом споре нет.

                                                              Даже законах, где термины максимально стандартизованы, есть разночтения. По УК "Несовершеннолетними признаются лица, которым ко времени совершения преступления исполнилось четырнадцать, но не исполнилось восемнадцати лет", а ГК подразумевает, что несовершеннолетним может быть и меньше 14 лет.

                                                              И ваша попытка поспорить о смысле терминов столь же бессмысленна, как и спор о том, возникает ли несовершеннолетие в 14 лет или нет.

                                                              То есть в смысле каких-то определений — вы правы, а в смысле других — нет.

                                                              Во-первых, предлагаю смотреть не в википедию курильщика, а в википедию здорового человека — то есть, в английскую.

                                                              Просите, вы определение какого термина ищите? Object-oriented programming или ООП? Очень многие слова при переводе меняют свой смысл. Так что отсылка к английской вики просто некорректна.

                                                              P.S. Строго по определению УК — если лицо 16 лет преступления не совершало, оно несовершеннолетним не является. :-) Хоть стой, хоть падай — но определение ровно такое.
                                                              • +3
                                                                Просите, вы определение какого термина ищите? Object-oriented programming или ООП? Очень многие слова при переводе меняют свой смысл. Так что отсылка к английской вики просто некорректна.

                                                                То есть, вы хотите сказать, что термин "Object-oriented programming" приобрел определение, соответствующее Страуструповскому триплету, только в русскоязычной традиции? Окей, я бы мог с этим поспорить, но не буду — пусть будет так, я ради смеха даже соглашусь, чтобы положить к себе в копилку еще один аргумент, почему во избежание недопонимания надо использовать только англоязычные термины :-)

                                                                • –4
                                                                  Угу, это часто бывает. Вас не удивляет, что Metropolitan означает совсем не то, что Метрополитен.Ещё смешнее со словом секс, которое на английском означает просто пол.

                                                                  во избежание недопонимания надо использовать только англоязычные термины :-)
                                                                  Ну попробуйте с английским смыслом слова «секс». Буду очень удивлен, если вас поймут. А пришло это слово в русский язык примерно тогда же, когда ООП.

                                                                  То есть, вы хотите сказать, что термин «Object-oriented programming» приобрел определение, соответствующее Страуструповскому триплету, только в русскоязычной традиции?
                                                                  Если не путаю, то термин ООП пришел в русский язык вместе с книгой Страустрапа. Не знаю, как сейчас, но лет 25 назад даже считалось, что без множественного наследования — это не ООП.
                                                                  • +3

                                                                    Ох! Окей, продолжу, предполагая, что вы не троллите. Если что, покажите табличку "сарказм", пожалуйста. :-)


                                                                    Ваша аналогия некорректна. Иностранные слова в русский язык заимствуются, после чего, как правило, живут сами по себе. Научные термины же международны по своей природе — ученые и инженеры бОльшую часть информации получают на принятом в их профессиональной области международном языке, а в нашем веке эту роль совершенно однозначно выполняет английский язык. Английские термины и их определения — это, в терминах DDD, ubiquitous language программистов. В связи с общедоступностью информации на этом самом ubiquitous language никакого самостоятельного развития и ответвлений не возникает; русскоязычные термины (которые, за исключением давным-давно (до 90-х) сложившихся терминов, либо являются прямым переводом, либо вообще англицизмами) в русской речи программиста используются только по той простой причине, что иначе было бы проще вообще все говорить по-английски.


                                                                    А с тем, что считалось 20 лет назад — я не спорю: тогда на фоне С++ всех остальных объектно-ориентированных языков и видно не было. И считалось "так" не только "у нас", но и "у них". С популяризацией же таких языков, как Javascript и Ruby, вспомнили, что не все так просто.


                                                                    Предлагаю сойтись на том, что "И-Н-П" является определением ООП-языков семейства Simula. :-)

                                                                    • –2
                                                                      Вообще-то смысл терминов дрейфует в любом языке, а не только в русском. Как пример — дрейф смысла слова hacker. При этом английский дрейфует побыстрее русского.

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

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

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

                                                                      Но в любом случае — лучше как в физике — «для начала введем несколько определений». И спорить не о смысле терминов, а в рамках уже введенных определений.

                                                                      Что касается вашей трактовки ООП… Мне она не нравится тем, что тогда получается, что ООП возможен на любом языке, где есть структуры. Берем структуру, пишем набор методов для неё — и получаем собственно все, что вы имеете ввиду под ООП. Роль класса у нас исполняет модуль, но все нужные свойства вашей трактовки ООП вполне есть. Ну а в моем понимании ООП возможно лишь там, где есть VMT или его аналог.

                                                                      А с тем, что считалось 20 лет назад — я не спорю: тогда на фоне С++ всех остальных объектно-ориентированных языков и видно не было. И считалось «так» не только «у нас», но и «у них».

                                                                      Ну вот вы и признали дрейф англоязычного термина.
                                                                      • –2
                                                                        Ну вот вам кусочек кода на Си
                                                                        struct usart_port;
                                                                        bool usart_driver_initialize (const struct usart_port *port, int rx_buffer_size, int tx_buffer_size);
                                                                        void usart_driver_set_bps (const struct usart_port *port, int bps);
                                                                        void usart_driver_set_parity (const struct usart_port *port, enum USART_DRIVER_PARITY parity);
                                                                        int usart_driver_send_byte_with_timeout (const struct usart_port *port,  uint8_t bt, int timeout_ticks);
                                                                        uint8_t usart_driver_receive_byte_with_timeout (const struct usart_port *port, uint8_t *dst, int timeout_ticks);
                                                                        



                                                                        Вы готовы признать, что это ООП? С моей точки зрения это модульное программирование. Можно назвать его программированием, ориентированным на объекты, но это не ООП. Ну хотя бы потому, что такой стиль написания придуман задолго до ООП.
                                                                        • +2
                                                                          Модульное программирование — это отдельная характеристика, которая не конфликтует с ООП. Программа может быть одновременно ОО и модульной, также как может не быть ни ОО ни модульной. Кстати, в приведенном вами куске кода разбиения на модули не видно, хотя и подразумевается :)

                                                                          То что вы привели — это в таком виде обычное процедурное программирование. Кстати, не могли бы вы пояснить каким образом из слов вашего оппонента следует что этот кусок кода надо классифицировать как ООП?
                                                                          • 0
                                                                            Ну вот вам чуть урезанная цитата из @ymbix:
                                                                            Класс — это деталь реализации конкретных языков, совершенно необязательная

                                                                            Понятие наследования вообще не является необходимым — любое наследование заменяется композицией.

                                                                            Понятие виртуальных методов тоже не нужно:

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

                                                                            Ну вот «объект, взаимодействующие с другими в соответствии с контрактом» я и представил. :-) Но я тоже согласен, что это не ООП. Хотя инкапсуляция и некий полиморфизм тут есть.
                                                                            • 0
                                                                              Инкапсуляции тут нет: методы и данные отделены друг от друга. Полиморфизма тут тоже нет.

                                                                              Вот если бы структура `usart_port` была объявлена как-то так:

                                                                              struct usart_port {
                                                                                  struct vmt_t { // Я не помню можно ли так делать в Сях, предположим что можно. Если нет - эту структуру без проблем можно вынести наружу.
                                                                                      bool (*usart_driver_initialize) (const struct usart_port *port, int rx_buffer_size, int tx_buffer_size);
                                                                                      void (*usart_driver_set_bps) (const struct usart_port *port, int bps);
                                                                                      void (*usart_driver_set_parity) (const struct usart_port *port, enum USART_DRIVER_PARITY parity);
                                                                                      int (*usart_driver_send_byte_with_timeout) (const struct usart_port *port,  uint8_t bt, int timeout_ticks);
                                                                                      uint8_t (*usart_driver_receive_byte_with_timeout) (const struct usart_port *port, uint8_t *dst, int timeout_ticks);
                                                                                  } *vmt;
                                                                              
                                                                                  // ...
                                                                              }
                                                                              


                                                                              То получилось бы уже то самое ООП в языке Си.
                                                                              • 0
                                                                                ООП тут нет, разумеется. А инкапсуляция (в модуле) есть. Мой оппонент верно заявил, что классы не обязательны для ООП, вместо них могут быть модули или пространства имен. Полиморфизм есть, но куцый — времени линковки. То есть строго по определению — один вызов функции обрабатывает данные разных типов, ибо внутренняя структура типа фиксируется лишь при линковке.
                                                                                Вот если бы структура `usart_port` была объявлена как-то так:

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

                                                                                Но вам +1 в карму за верную догадку. Кое-что в этом духе в потрохах есть:
                                                                                  void (*init_pins_and_clocks) (void);
                                                                                  void (*set_rs485_tx_enable_pin) (int enable);
                                                                                
                                                                                Правда и это — ну совсем не ООП, ибо все структуры формируются статически.
                                                                                • 0
                                                                                  А вызывающий код и вызываемый совершенно не обязаны быть написаны в одной парадигме!

                                                                                  В данном случае, в вызываемом коде (драйвере UART), видимо, от ООП все же что-то есть. А вызывающий как выглядел процедурным, так и выглядит…
                                                                                  • 0
                                                                                    Да оба процедурные, просто по формальным критериям, названным symbix это ООП. Вот и захотелось доказать, что его критерии не верны.

                                                                                    Максимум это код, ориентированный на объекты (struct usart_driver — вполне себе объект). Но не ООП.
                                                                          • 0

                                                                            Модульность необходима, но не достаточна. Тут сделан один шаг в сторону ООП, но отсутствует возможность абстрагирования от конкретного типа объекта. Можно говорить об этом с точки зрения отсутствия разделения контракта и конкретной реализации, можно — с точки зрения отсутствия полиморфизма, можно — с точки зрения отсутствия late binding, это все разные стороны одной монеты.


                                                                            Чтобы появилось ООП, в этом коде надо добавить указатели на функции в структуру и договориться, что снаружи модуля мы "не видим" никаких членов структуры, кроме этих указателей на функции. Похожим образом устроены модули в nginx: там, конечно, много performance hacks, но в целом — вполне себе ООП.

                                                                            • –1
                                                                              но отсутствует возможность абстрагирования от конкретного типа объекта.
                                                                              Почему же? Абстрагирование полное. Это может быть RS323, а может быть RS485, RS422 или радиомодем. Если две функции сделать пустыми — это может быть USB, SPI или UDP. Если добавить пару функций — то можно и TCP/IP.

                                                                              Уж не говорю о том, что конкретная реализация ком-порта тут не определена. А она бывает совсем разная на STM32, LPC, Atmel или 80x86.

                                                                              Чтобы появилось ООП, в этом коде надо добавить указатели на функции в структуру
                                                                              Вы имеете ввиду синтаксис или семантику? Если синтаксис, то отличие между port1.usart_driver_initialize(200, 300) и usart_driver_initialize(&port1, 200, 300) не существенно. Ну да, первый вариант красивее, но не более того. Если речь о семантике, то во многих (если не во всех) реализациях в VMT включаются только виртуальные функции. Таким образом, у класса без виртуальных функций нет ни VMT, ни указателей на функции.

                                                                              и договориться, снаружи модуля мы «не видим» никаких членов структуры, кроме этих указателей на функции.
                                                                              Гм, на таком уровне полезно язык Си знать. Вы тут видите хоть один член структуры? Не видите. И никто его не видит, кроме реализации функций и фабрики, выдающей указатели на структуру.

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

                                                                                Семантику, разумеется.


                                                                                Почему же? Абстрагирование полное.

                                                                                Ок, давайте, чтобы не углубляться в ненужные детали, считать, что у нас только initialize, send и receive. Как будет выглядеть код, который создаст массив из N портов разного типа, и шлет в цикле во все порты строку "Hello"?


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

                                                                                Почему же? Достаточно интерфейсов. Или вообще duck typing.

                                                                                • –1
                                                                                  Почему же? Достаточно интерфейсов. Или вообще duck typing.
                                                                                  Интерейсы изоморфны множественному наследованию от абстрактных базовых классов без статических членов и методов. Утиная типизация — это те же интерфейсы, просто имя интерфейса не пишется явно, а вычисляется компилятором или рантаймом.

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

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

                                                                                  Как будет выглядеть код, который создаст массив из N портов разного типа, и шлет в цикле во все порты строку «Hello»?
                                                                                  Не понимаю, в чем у вас проблемы? Хотите через массив — ну ловите через массив.

                                                                                  Абсолютно очевидный код
                                                                                  #define N_PORTS 10
                                                                                  struct usart_port *ports[N_PORTS];
                                                                                  for (int i=0; i <N_PORTS; i++)
                                                                                       ports[i] = usart_driver_get_driver_by_number(i);
                                                                                  for (int i=0; i <N_PORTS; i++) {
                                                                                      usart_driver_initialize(ports[i],64,64);
                                                                                      usart_driver_set_bps(ports[i],  115200);
                                                                                      const char *str = "Hello";
                                                                                      while (*str)
                                                                                          usart_driver_send_byte_with_timeout
                                                                                              (ports[i], *str++, 1); 
                                                                                  }


                                                                                  В свою очередь прошу объяснить, зачем вам массив? Экономия микросекунды на медленных операциях с портом? Не, конечно, как бы дитя не вешалось, лишь бы потешилось, но все-таки, зачем?
                                                                                  • 0

                                                                                    Массив был прямым намеком на late binding и LSP, на классический пример про геометрические фигуры. Полиморфизм времени линковки не считается, конечно же.


                                                                                    В общем, я понял, в чем у нас ключевое расхождение. Вы считаете систему контрактов вариацией механизма наследования. Но это вам так кажется под влиянием языка С++, в котором интерфейсы реализуются через pure abstract classes, а функцию "implements" выполняет множественное наследование. На самом деле это концептуально разные вещи: C extends A, B означает "C является A и является B", а C implements A, B означает "C поддерживает протоколы A и B". Да, наследование (множественное) позволяет реализовать интерфейсы. Но для реализации интерфейсов наследование не является необходимостью — существует масса иных способов, когда наследования нет, а интерфейсы есть (например, в Go). Да и на той же Джаве я могу написать объектно-ориентированную программу без единого extends, пользуясь только implements.

                                                                                    • 0
                                                                                      Чтоб вам было понятней, рассмотрим два примера.

                                                                                      1. Классический С++ или Delphi, объекты с развитым наследованием, но без виртуальных методов. Наследники вовсю пользуются методами базового класса, но никаких виртуальных методов нет. Назовете ли вы этот стиль ООП?
                                                                                      2. Классическая связь двух приложений через COM/DCOM. Клиент написан в процедурном стиле, сервер — тоже. Интерфейс — наш собственный. Назовете ли вы это ООП?
                                                                                      • –1

                                                                                        1 — не знаю, что там в Delphi, но в С++ точно нет — без виртуальных методов не получится реализовать late binding (хаки с прямым доступом к памяти по оффсету не рассматриваю, конечно).


                                                                                        2 — зависит от архитектуры приложений, вполне может быть, что и ООП. По крайней мере, сам COM-вызов это ООПшная штука.

                                                                                        • 0
                                                                                          Ага, то есть late binding для вас обязательное условие. Хорошо, пусть в программе есть виртуальные методы и их перекрытие, но эти методы не вызываются. Будет ли такая программа ООП?

                                                                                          Ещё усложним. Пусть при одних настройках late binding происходит, а при других — нет. Означает ли это, что программа становится ООП при изменении настроек?
                                                                                          • 0
                                                                                            В Delphi (как и в boost/C++) возможен late binding без виртуальных методов. Делается это через события — это такие указатели, указывающие на метод конкретного объекта. То есть семантически «событие» — это два указателя, один на объект другой на метод, но синтаксически — это единое целое. Вроде бы в С++17 эта фича уже добавлена.

                                                                                            Так что late binding возможен и без ООП.

                                                                                            Я уж не говорю про самые обычные указатели на процедуры, которые были ещё в Си. Они по сути — тоже late binding.
                                                                                            • 0

                                                                                              Late binding в прямом смысле слова, конечно, необязателен. Разумеется, может быть любой другой механизм — такой, как вы описываете, или диспетчер сообщений в Objective-C. Реализация вообще не имеет значения. Важно, что там, где мне надо знать только контракты, мне действительно достаточно знать только контракты, конкретный тип (как и вообще наличие в языке конкретных типов) меня абсолютно не волнует.


                                                                                              Псевдокодом:


                                                                                              interface Fooable {
                                                                                                  void foo();
                                                                                              }
                                                                                              class FooHandler {
                                                                                                  void handleFoo(Fooable fooable) {
                                                                                                      fooable.foo();
                                                                                                  }
                                                                                              }
                                                                                              
                                                                                              Fooable fooable = getSomeFooableAnyhow();
                                                                                              FooHandler fooHandler;
                                                                                              fooHandler.handleFoo(fooable);

                                                                                              При этом абсолютно не имеет значения, какого конкретно типа тут foo. Что именно делает getSomeFooableAnyhow() — не имеет значения, за исключением того, что возвращает нечто, реализующее интерфейс Fooable. Конкретная реализация всегда может быть изменена на другую, код завязан на контракты а не не конкретную реализацию.

                                                                                              • 0
                                                                                                Разумеется, может быть любой другой механизм — такой, как вы описываете, или диспетчер сообщений в Objective-C.
                                                                                                Вы сейчас договоритесь до того, что любая GUI-программа для windows — это ООП. Там как раз есть диспетчеризация сообщений и late binding, сделанный на switch и if. Ну и объекты (окна) и их классы.
                                                                                                Что именно делает getSomeFooableAnyhow() — не имеет значения, за исключением того, что возвращает нечто, реализующее интерфейс Fooable
                                                                                                Так ровно это и делает usart_driver. Вы же не знаете, что именно возвращает фабрика, указатель на usart_driver или указатель на иную структуру (наследника), в начале которой сидит usart_driver.
                                                                                                Конкретная реализация всегда может быть изменена на другую, код завязан на контракты а не не конкретную реализацию.
                                                                                                Вот-вот-вот. Ровно это и есть в usart_driver. Может вы зря решили, что это не ООП? :-)
                                                                                  • –1
                                                                                    Это может быть RS323, а может быть RS485, RS422 или радиомодем.

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

                                                                                    • 0
                                                                                      Да ну? У RS485 есть особенность: надо отдельно включать и выключать передатчик. Ибо если на линии два передатчика передают разные сигналы, то минимум один из них может сгореть. Так что код для RS485 чуть иной, чем для RS232.
                                                                                      • 0
                                                                                        Код для RS485 отличается от кода в RS232 в драйвере или в прикладной части программы?

                                                                                        Если второе — то тут вообще нет никакой ни абстракции, ни полиморфизма, одна лишь видимость.

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

                                                                                          Но принципиально я могу написать код для USB с тем же интерфейсом и слинковаться с ним. Или на SPI. Или на UDP.

                                                                                          У меня в соседнем участке кода — 4 разных реализации на один H-файл. Это такой вот полиморфизм времени линковки.
                                                                                          • 0
                                                                                            Хм, а как вы с таким подходом будете делать плату с двумя передатчиками разного типа?
                                                                                            • 0
                                                                                              Это вы про что? Про 4 реализации? Ну если вы мне покажите SoC, в котором одни порты под linux, другие под FREERTOS на STM32F4/F7, третьи на LPC, а четвертые на К1879ВЯ1Я — то я подумаю. :-)

                                                                                              А если про usart_driver, то там все довольно просто. Он состоит из трёх частей: обработчик прерывания, процедура старта передачи при появлении символа в очереди и универсального чтения-записи в очередь. Как видите, обе не универсальные части — не имеют публичных имен.

                                                                                              Обработчики прерывания — вообще ставятся при инициализации. А процедура старта передачи… Ну тут лучше указатель на функцию. Хотя можно и без него — ветвление по значению поля, но это сильно хуже.
                                                                                              • 0
                                                                                                Нет, погодите. У вас если уже готовые модули usart_driver. И два порта, один RS232, а другой RS485. Что и с чем вы будете линковать чтобы все заработало?
                                                                                                • 0
                                                                                                  Линкуется с двумя модулями. Один -универсальный с кодом драйвера, второй уникальный для платы usart_hw, содержащий фабрику. Там статически заданы структуры портов, а в них — прерывания, пины и так далее. Для RS485 там будет и код процедуры, управляющей пином TX_ENA.
                                                                                                  • 0
                                                                                                    То есть управление пином TX_ENA пишется заново (или копи-пастится) для каждой новой платы содержащей порт RS485?
                                                                                                    • 0
                                                                                                      Да, разумеется, это намного удобнее и компактнее, чем «универсальная» процедура, которой надо передавать адрес регистра на шине и бита в этом регистре. И которая к тому же не универсальна, ибо управление TX_ENA может идти не только одним битом GPIO.
                                                                                                      • 0
                                                                                                        Нет, в ООП универсальность достигается другими средствами: через указатель на функцию/интерфейс, которая уже управляет пином. Похоже на то как сделано у вас. Но я не могу понять что именно у вас сделано.

                                                                                                        Управление пином TX_ENA у вас пишется в каких процедурах? В процедуре «вызвать перед передачей» или в процедуре «установить пин TX_ENA»?
                                                                                                        • 0
                                                                                                          Фактически у нас компонентно-ориентированное программирование. Ну или что-то близкое к нему.

                                                                                                          Управление пином TX_ENA у вас пишется в каких процедурах?
                                                                                                          Уже писал:

                                                                                                           void (*set_rs485_tx_enable_pin) (int enable);


                                                                                                          Мелкое замечание. Точно так же как шитый код бывает прямым, косвенным, индексным и так далее, точно так же позднее связывание — это не только вызов процедуры по указателю. В конкретной реализации мы можем иметь и switch, выбирающий исполнение в зависимости от типа объекта, и указание в объекте индекса в таблице процедур и много много иных способов. Для кода, предполагающего расширение — это неудобно. Но если расширение не предполагается, то можно и так. А возможность расширения системы классов — не является определяющим свойством ООП.
                                                                    • +3
                                                                      Заведение — средний род.
                                                                      • 0
                                                                        Угу, это я описался.