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

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) можно найти в моем профиле

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


  1. akryukov
    26.12.2017 20:08
    +3

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


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


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


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


    1. commanderxo
      27.12.2017 01:07
      +1

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

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

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


      1. LionZXY
        27.12.2017 08:35

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


      1. allex
        27.12.2017 08:38

        Пока вы смеетесь, уже придумали название (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.


    1. sedyh
      27.12.2017 17:58

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


    1. kalininmr
      28.12.2017 03:39

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


  1. Forced2bebad
    26.12.2017 20:30
    +2

    А что означает «ВВП полосу»? Может ВПП? Тогда слово «полосу» лишнее. В Целом, идея интересная. Но вот я, к примеру, плохо мыслю абстракциями. Скажите, а вы могли бы попробовать написать сюда пример кода (скажем, на той-же Java), который бы вписывался в вашу концепцию пояснений? Мне вот, как новичку, гораздо проще было бы читать пояснения и видеть код.


    1. prohodil_mimo
      26.12.2017 20:44
      +1

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

      Автору всё равно спасибо, мне кажется, если расширить пояснения с одного абзаца до одного раздела с несколькими абзацами, с небольшими примерами кода и теми же абстрактными примерами «из жизни КБ Боинг», эта заметка могла бы реально стать «путеводной звездой».


      1. HelpOP
        27.12.2017 17:58

        Композиция. У вас есть самолет, у него есть двигатель ДС-900, его разрабатывает первая команда, вторая команда разрабатывает двигатель ДС-1500. Вы в своем чертеже можете использовать как ДС-900 так и ДС-1500.

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

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

        Скрытый текст
        — |column |
        — | param 1 |
        — | param 2 |
        — | param 3 |


        1. zoonman
          27.12.2017 19:06

          В данном случае необходимо вводить интерфейс СовместимыйДвигатель.

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


  1. Hazactam
    26.12.2017 20:37
    +1

    Одна проблема — объяснить что это. Другая — объяснить зачем всё это вообще нужно.


    1. VaalKIA
      27.12.2017 03:10

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


  1. SirEdvin
    26.12.2017 20:49

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


    1. taujavarob
      26.12.2017 21:19

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

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

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


      1. SirEdvin
        26.12.2017 22:00

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


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


        1. Lure_of_Chaos
          26.12.2017 22:47

          ООП — это ООП, а в JS ОП.


          1. SirEdvin
            27.12.2017 10:39

            JS как бы мультипарадигменный язык и там можно применять ООП, а можно и не применять. Как в Python или C++.


          1. Dimcore
            27.12.2017 17:30

            Простите, а что такое ОП? Объектное программирование? Сами придумали?

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


            1. VolCh
              27.12.2017 20:43

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


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


              1. mayorovp
                28.12.2017 09:47

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


                1. VolCh
                  28.12.2017 13:12

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


      1. Idot
        27.12.2017 07:42

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

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


        1. LionZXY
          27.12.2017 08:36

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


          1. taujavarob
            27.12.2017 21:28

            LionZXY

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

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

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

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


            1. VolCh
              27.12.2017 21:34

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


              1. taujavarob
                28.12.2017 21:39

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


      1. punkkk
        27.12.2017 11:35

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


      1. k12th
        27.12.2017 14:55

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


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


    1. areht
      26.12.2017 21:57

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


      1. mayorovp
        27.12.2017 10:24

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


        1. VolCh
          27.12.2017 13:40

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


    1. mayorovp
      27.12.2017 10:22
      +1

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


      1. SirEdvin
        27.12.2017 10:24

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

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


        1. mayorovp
          27.12.2017 10:39

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


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


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

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


          1. Neikist
            27.12.2017 20:57

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


            1. VolCh
              27.12.2017 21:06

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


              1. Neikist
                27.12.2017 22:02

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


                1. VolCh
                  27.12.2017 22:05

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


            1. Neikist
              27.12.2017 21:54

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


            1. mayorovp
              28.12.2017 09:52

              Да, контракты приходится хранить где-то еще. В документации, например. И я тоже считаю что это недостаток языка.

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


              1. Neikist
                28.12.2017 12:55

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

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

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


                1. mayorovp
                  28.12.2017 12:59
                  +1

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


                  1. Neikist
                    28.12.2017 13:33

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


                    1. VolCh
                      28.12.2017 13:37

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


        1. k12th
          27.12.2017 14:56

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


          1. third112
            27.12.2017 14:58

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


            1. VolCh
              27.12.2017 15:10

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


          1. SirEdvin
            27.12.2017 15:43

            На самом деле небольшие есть) Но это не важно, как бы, я больше возражал фразе «классы в javascript почти такие же, как в C++».


      1. Good1uckhf
        27.12.2017 12:31

        Вообще-то в js классов нет и не было. То что вы называете классами в ES2015 — это синтаксический сахар, под капотом в себе кроет старые добрые прототипы.


        1. HelpOP
          28.12.2017 19:28

          С вашей точки зрения в компьютерном мире нет вообще ничего. Это всё синтаксический сахар над нулями и единицами.


      1. VolCh
        27.12.2017 13:50

        Чего там и правда нет — так это переопределения членов, когда в базовом классе 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();
        


        1. mayorovp
          27.12.2017 13:58

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


          1. VolCh
            27.12.2017 14:17

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


            1. mayorovp
              27.12.2017 14:22

              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 засунуть? :-)
              


              1. stationfuk
                27.12.2017 18:53
                -1

                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 засунуть? :-)
                


                1. mayorovp
                  28.12.2017 13:05

                  Да где работает-то? Если исправить синтаксические ошибки, то в консоль код выведет «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 засунуть? :-)
                  


    1. wladyspb
      27.12.2017 13:33

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


      1. SirEdvin
        27.12.2017 13:35
        +1

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


        1. wladyspb
          27.12.2017 14:52

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


  1. aamonster
    26.12.2017 20:51
    +3

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


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


    1. Jef239
      26.12.2017 22:15

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

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

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


      1. symbix
        26.12.2017 23:27

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


        1. Jef239
          26.12.2017 23:55

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

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


          1. symbix
            27.12.2017 00:31

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


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


            1. Jef239
              27.12.2017 01:15
              -1

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

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

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

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


              1. symbix
                27.12.2017 01:51

                Строгого определения ООП автор 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" — по сути, полиморфизм.


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


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


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


                1. Jef239
                  27.12.2017 02:40

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

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

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

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

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

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

                  P.S. Строго по определению УК — если лицо 16 лет преступления не совершало, оно несовершеннолетним не является. :-) Хоть стой, хоть падай — но определение ровно такое.


                  1. symbix
                    27.12.2017 03:02
                    -1

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

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


                    1. Jef239
                      27.12.2017 03:32

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

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

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


                      1. symbix
                        27.12.2017 05:53

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


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


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


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


                        1. Jef239
                          27.12.2017 07:34

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

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

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

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

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

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

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

                          Ну вот вы и признали дрейф англоязычного термина.


                        1. Jef239
                          28.12.2017 02:59

                          Ну вот вам кусочек кода на Си
                          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);
                          


                          1. mayorovp
                            28.12.2017 10:02

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

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


                            1. Jef239
                              30.12.2017 04:38

                              Ну вот вам чуть урезанная цитата из @ymbix:

                              Класс — это деталь реализации конкретных языков, совершенно необязательная

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

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

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

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


                          1. symbix
                            29.12.2017 01:03

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


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


                            1. Jef239
                              29.12.2017 17:19

                              но отсутствует возможность абстрагирования от конкретного типа объекта.
                              Почему же? Абстрагирование полное. Это может быть 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, это все разные стороны одной монеты.
                              Вот-вот-вот… Вы уже очень близко. Остался маленький шаг — понять, что при отсутствии наследования полиморфизм вырождается. То есть чтобы вести речь о полиформизме — должен быть выбор хотя бы из двух реализаций. А это означает, что без наследования — нет ООП, а есть лишь «программирование, ориентированное на объекты».


                              1. symbix
                                29.12.2017 18:05

                                Вы имеете ввиду синтаксис или семантику?

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


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

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


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

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


                                1. Jef239
                                  30.12.2017 03:22

                                  Почему же? Достаточно интерфейсов. Или вообще 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); 
                                  }


              1. ksil
                27.12.2017 14:18
                +1

                Заведение — средний род.


                1. Jef239
                  27.12.2017 15:43

                  Угу, это я описался.


          1. areht
            27.12.2017 10:58

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

            Достаточно по вашей же ссылке переключить на на более употребительный английский


        1. geher
          27.12.2017 08:46

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


          1. wladyspb
            27.12.2017 14:00

            Собственно да. Композиция структур в Go вполне себе обеспечивает наследование поведения, хотя и не является наследованием классов(которых в Go, о ужас, тоже нет!) в классическом понимании)


          1. VolCh
            27.12.2017 14:13
            -1

            Прототипное программирование в, например, JS является объектным, но не является объектно-ориентированным согласно некоторым определениям, для которых важным в ООП является наличие иерархии классов.


      1. michael_vostrikov
        27.12.2017 23:26

        Объект «книга» в приложении для библиотеки должен обладать свойствами «абстрактного печатного издания», в магазине – «абстрактного товара»

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


        1. Jef239
          28.12.2017 02:42

          Рекомендую все-таки не ограничиваться одной цитатой, а почитать хотя бы главу по ссылке. А уж после этого можно будет и поспорить — эффективно ли предложенное вами решение и какие таблицы в СУБД оно порождает.

          Подсказка: цены где хранить будете? Хочу найти самый дешевый букварь, как поиск в СУБД пойдет? Хочу найти товары с наибольшей маржой, как поиск пойдет?


          1. michael_vostrikov
            28.12.2017 11:27

            Там пишут вещи, противоположные тому, о чем я сказал.


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

            У меня в книгу ничего не вкладывается, это отдельная сущность. В этой терминологии надо "вкладывать" в товар книгу.


            Подсказка: цены где хранить будете?

            Как этот вопрос связан с логической ошибкой в вашей цитате? Вы считаете, что единственный способ назначить книге цену это сделать у нее свойство "цена"? У самой книги нет такого свойства. Автор есть, название есть, а цены нет.


            Есть такое понятие SKU — stock keeping unit. На него обычно и назначается цена.


          1. VolCh
            28.12.2017 13:25

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


            Для создания интернет-магазина путь наследования объекта класса "книга" от абстрактного класса "Товар" скорее всего тупиковый. Вообще вызывает сомнения необходимость создания класса "книга".


            1. Jef239
              28.12.2017 18:32

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

              В любом случае, главное — это выводы автора

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

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

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

              Результат неизменно стабильный…


              1. Hazactam
                29.12.2017 12:02
                +2

                Как раз таки на больших проектах все достоинства ооп и проявляются. Участвую (ну как участвую — мой проект :) ) в довольно крупном проекте, больше миллиона строк, Delphi, 15+ лет. Как бы я это всё без ооп поддерживал — не представляю. Классы крайне удобны, на больших проектах особенно!


                1. Jef239
                  30.12.2017 04:21

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

                  Вы прикиньте, что будет с вашим кодом, если вместо TNotifyEvent и им подобных у вас будут две альтернативы:

                  1. Вызов функции (не метода объекта, а просто процедуры или функции).
                  2. Uses на модуль с описанием объекта и вызов конкретной процедуры. Передать при этом вы можете только ссылку на объект.


                  Чувствуете в какое спагетти превратится ваш код? Уж не говорю про то, что в дельфи нет множественного наследования, автоматического приведения типов, переопределениz операций. А оно все вносит свою лепту в ООП-хаос.

                  Ещё один важный нюанс состоит в том, что если вы пишете a la TCustomGrid — это не ООП. Это обычный процедурный код, завернутый внутрь объекта. 6 тысяч строк на модуль, из них 4 тысячи на один объект — это ни в какие рамки ООП не лезет.

                  Я тоже участвовал в больших проектах на Delphi. Сделанный моей командой — 135 тысяч строк и второй — 450 тысяч строк. В обоих — примерно как в TCustomGrid — большие объекты с завернутым в них процедурным кодом, минимум собственных виртуальных методов зато достаточно много собственных событий. Померьте сколько у вас vurtual и dynamic и сколько of object, какие типичные размеры кода классов (в строках)

                  Событийное программирование — это все-таки не ООП, а чуть иная техника. Зато — техника очень хорошая.


    1. Lure_of_Chaos
      26.12.2017 22:52

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


      1. aamonster
        27.12.2017 13:14

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


    1. potan
      27.12.2017 13:36

      Кстати, сложности с множественным наследованием пропадают, если взглянуть на него со стороны теории категорий. Наследование связано с функцией static_cast, которая не обязяна быть тривиальной. А отличие виртуального от невиртуального наследования просто выражается коммутативностью соотвествующей диаграммы.


      1. aamonster
        27.12.2017 18:04

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


  1. fzn7
    26.12.2017 21:10

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


  1. Kokto
    26.12.2017 21:14
    -1

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


    1. Quiz
      26.12.2017 21:15

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


    1. evnuh
      26.12.2017 21:36

      Что такое инстанс объекта?


      1. samizdam
        27.12.2017 10:37

        Вы будете смеяться, но это работает:
        <?php
        $foo = new Foo;
        $bar = new $foo;


        1. VolCh
          27.12.2017 14:18

          Особенность реализации, которая не факт, что всегда будет работать.


        1. Color
          27.12.2017 17:27

          Медленно, без лишних движений закройте тег!


          1. alix_ginger
            27.12.2017 17:50

            Насколько я помню, по пхпшным стайлгайдам рекомендуется этот тэг не закрывать в PHP-only файлах


            1. brzsmg
              27.12.2017 18:10

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


      1. etho0
        27.12.2017 11:00

        Добавьте метаклассы. Класс это объект метакласса. Инстанс класса это объект класса.


    1. Lure_of_Chaos
      26.12.2017 22:55

      А я бы так и сделал — объявил бы кучу примесей по функциональности и комбинировал бы из них нормальные классы


    1. VolCh
      26.12.2017 23:30

      Чертёж — это точно конкретный класс. По нему изготавливаются конкретные экземпляры — инстансы класса, они же объекты.


      1. a-tk
        27.12.2017 09:51

        А ошибки и дефекты, у каждого свои, куда записывать?


        1. F0iL
          27.12.2017 11:37

          Как вариант — изменение значений каких-либо полей («прочность пластины N», «сопротивление кабеля M», или даже «элемент, вставляемый в паз P» — помните про композицию?) с дефолтных на другие (иногда рандомные или даже nullptr).


          1. a-tk
            27.12.2017 22:36

            Слишком много сущностей сразу становится.
            А ведь ООП в частности и АТД в целом ведь совсем не об этом. Любые данные, как бы ни были они представлены, являются некоторой упрощённой моделью объектов реального мира, содержащие лишь значимые характеристики.
            Если какая-то модель проектирования реализует обмен сообщениями, то он подходит для описания взаимодействий, моделируемых обменом сообщений. Если надо иерархия вида is-a, то моделируем наследованием. Если при хождении по иерархии надо изменчивость поведения — полиморфизм. Если надо защищать приватные данные или спрятать детали — инкапсуляция. Создание по шаблону — можно прототипы поюзать, если хотим заменить детали у конкретных сущностей без изменения общего поведения большинства — сделаем делегирование. И так далее и тому подобное.
            No silver bullet.


  1. yurij_volkov
    26.12.2017 21:25

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


  1. Quiz
    26.12.2017 21:27

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

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

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

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


    1. ildarz
      26.12.2017 21:55

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

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


      1. Hazactam
        27.12.2017 00:09

        Тут как бы не избранность. Тут проблема в объяснении простым, доступным, языком. И проблема не только в том, что бы объяснить как. А и объяснить зачем. Как я уже писал. Сложно объяснять абстрактные вещи. Многое, пока сам не пощупаешь, не понятно, увы. Хоть как объясняй.


        1. ildarz
          27.12.2017 11:47

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


          Если мы говорим о "сути программирования вообще", там даже особых абстракций нет. Это просто процесс составления на специальном языке задания для вычислительной машины. :) Всё. Объясняется просто на конкретных примерах даже детям и домохозяйкам.


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


          1. VolCh
            27.12.2017 14:20
            -1

            на специальном языке

            Язык вообще, а специальный тем более — уже абстракция :)


      1. avorodis
        27.12.2017 00:12

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

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


        1. dididididi
          27.12.2017 17:58

          Гуманитарный ум — тот который получает гигансткое количество информации(«Войну и Мир» или справочник медика) и после обработки выдавая обычно более короткий результат.

          технический ум — тот который на входе принимает минимум информации((a+b)в степени n) и выдает результат зачастую длиннее входной информации.

          Как то так я вижу отличие.


          1. VolCh
            27.12.2017 20:47

            Гуманитарный ум — тот, который в условиях неполноты информации больше доверяет интуиции и контексту, а технический — либо теорверу, либо выдает "undefined" :)


  1. symbix
    26.12.2017 21:28
    +2

    Оба методы неправильные, если объясняется ООП, а не язык С++ или Java. :-)


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


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


    Понятие виртуальных методов тоже не нужно: для того, чтобы его вводить, надо, чтобы существовали не виртуальные (что, опять же, деталь реализации конкретных языков). Можно просто считать все методы виртуальными по определению. Тем более, раз мы исключили наследование, то разница вообще отсутствует.


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


    1. Lure_of_Chaos
      26.12.2017 22:58

      Собственно, здравствуй, Smalltalk?


      1. symbix
        26.12.2017 23:16

        В принципе, да. :-)


        Остается только одна вариация, на мой взгляд, существенная (в отличие от наличия-отсутствия классов или там наследования). В smalltalk я могу послать любому объекту любое сообщение и заранее не знаю, отреагирует ли он на него — и это нормально. В языках, где явно (как, скажем, в Java) или неявно (как, скажем, в Go) объявляется контракт, нарушение контракта либо не скомпилируется, либо вызовет ошибку времени исполнения. Может быть и смесь подходов — как в Ruby с method_missing или в PHP с __call().


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


    1. niko1aev
      26.12.2017 23:21

      Поэтому и хорошо бы студенту объяснять ООП на примере какого-то простого языка.
      А может быть даже фреймворка. Берешь Ruby on Rails, создаешь модельку, у нее 5-10 методов.
      Ну и просто в консоли делается
      post1 = Post.new(title: '123')
      post1.save
      post2 = Post.new(title: '234')
      post2.save
      puts post1.title
      puts post2.title
      И не важно, что на этом этапе студент не понимает, где тут инстанс, где тут класс, и то же, Post — это тоже инстанс, но другого класса, да еще унаследованный от десятка других.

      Дальше дается задание — создать 10-20-30 таких моделек + методов.

      Потом спускаемся ниже, на уровень Ruby.
      Где уже ручками надо задать initialize, ручками прописать attr_reader, attr_writer.
      Даем примеры на усвоение. Вот тут можно заикнуться, кто тут инстанс, и пару слов про наследование. Опять же примеры.

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

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


      1. symbix
        27.12.2017 02:29

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


        Под влиянием Rails и подобных ему фреймворков, а точнее, под влиянием Active Record, в массовом сознании откладывается некорректная идея о том, что модель — это такая штука, в которой есть данные, и в ответственность модели входит сохранение данных на постоянное хранилище и загрузка данных с оного. Что — при всей заслуженной популярности Active Record как средства быстрой разработки — в корне неверно. Если это так, почему модель называется "модель", а не какой-нибудь "слой работы с данными"? Мы знаем, что такое модель самолета, например. Речь идет о воспроизведении некоторого объекта в виде образца или схемы. Когда мы пишем программу, мы моделируем объекты из предметной области программным кодом. И у модели прежде всего важно ее поведение (то есть, реакция на сообщения — иными словами, реализация методов): совокупность моделей из предметной области и является в целом моделью предметной области. Персистенция же не имеет никакого отношения к моделированию предметной области, являясь вынужденной инфраструктурной задачей, которая возникает не из-за особенностей предметной области, а из-за технических ограничений (например, если SSD-накопители по цене и производительности сравняются с ОЗУ, необходимость в персистенции вообще отпадет). Обучение на примере ActiveRecord приводит к тому, что моделирование предметной области средствами ООП фактически заменяется банальным структурно-процедурным программированием: класс воспринимается как сишная структура или паскалевская запись, в которую воткнули функции load() и save(). Итогом будет такое умозаключение: "я понял: у нас была отдельно структура и отдельно функции load и save, а теперь мы объединили данные и код и получили объект!" Понял-то понял, только это не совсем ООП. Это антипаттерн "Anemic Model", совмещенный с [анти]паттерном ActiveRecord.


        Если уж брать язык Ruby, то в качестве показательного примера стоит брать не ActiveRecord, а "чистые" модели и ROM. Впрочем, если говорить об изучении ООП, проще всего вообще вопроса персистенции не касаться — там всегда костыли :-)


  1. kimisa
    26.12.2017 21:39

    Для меня более наглядным был бы пример математический. С логическими операциями. Например написать класс с работой над матрицами.


    1. akryukov
      26.12.2017 21:52

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


      1. Shtucer
        26.12.2017 22:56

        У меня такое ощущение, что второе предложение в какой-то мере опровергает первое. Вернее так: без некоторых важных уточнений — это дышло, может вильнуть хоть в ООП, хоть в ФП.


        1. akryukov
          26.12.2017 23:10

          Да хоть к процедурному. Концепция АТД существует почти везде, включая чистый С и некоторые диалекты sql.
          В ООП, насколько я его понимаю, в дополнение дается возможность строить иерархии АТД (наследование+полиморфизм).
          В полном понимании ФП я сомневаюсь, но оно вряд ли про то, что мы просто объявим четыре функции.


          1. Shtucer
            26.12.2017 23:26

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


            В ООП, насколько я его понимаю, в дополнение дается возможность строить иерархии АТД (наследование+полиморфизм).

            Полиморфизм это не прерогатива ООП. Вполне его можно встретить и в ФП.


            Я к тому, что комментарий можно переписать вот так:


            "Для работы с матрицами (ООП|ФП|ПП) излишне. Достаточно описать абстрактный тип данных с пачкой методов: транспонирования, ранга, умножения, сложения ну и еще чего-нибудь." и ничего не изменится.


          1. 0xd34df00d
            28.12.2017 00:38

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

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


      1. 0xd34df00d
        28.12.2017 00:34

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


        1. akryukov
          28.12.2017 07:15

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


          1. MikailBag
            28.12.2017 17:50

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


            1. akryukov
              28.12.2017 18:09

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

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


          1. unC0Rr
            28.12.2017 19:19

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


          1. 0xd34df00d
            28.12.2017 20:41

            Как-то так, в общем.

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

            Профит по сравнению с рантайм-проверкой внутри умножения в том, что, как правило, матрицы вы читаете из файлов не так часто, как потом их умножаете.


      1. areht
        29.12.2017 01:37

        Что значит «излишне»? Вы считаете решение этой задачи через ООП сложнее?


    1. third112
      27.12.2017 12:58

      Согласен: см. коммент ниже.


  1. pankraty
    26.12.2017 21:54

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


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


    Полиморфизмом будет, например, реализация рулей высоты. В типовом (абстрактном) проекте определен штурвал, который можно перемещать к себе/от себя, изменяя "наклон" самолёта (угол атаки?) Как конкретно происходит его изменение — в типовом проекте не описано, а описано в проектах разных моделей самолетов: В гражданском используются рули высоты, а в истребителе — изменяемый вектор тяги двигателя. Это полиморфизм.


    1. VolCh
      26.12.2017 23:44

      Бомберы должны разгрузиться перед посадкой (наверное не актуально, но пусть), пассажирские объявить «пристегните ремни, не вставайте с кресел», грузовые — просто сесть :)


  1. alex_zzzz
    26.12.2017 21:56

    инстанс (объект) класса

    экземпляр класса === объект


  1. potan
    26.12.2017 22:01

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


    1. Daniil1979
      26.12.2017 22:18

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


      ???


      1. potan
        26.12.2017 22:26

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


        1. Lure_of_Chaos
          26.12.2017 23:03

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


          1. aelaa
            27.12.2017 11:26

            Есть доклад Рича Хикки «Simple Made Easy». ФП — простое, но не лёгкое. На самом деле не лёгкое оно только когда есть опыт в других языках и какое-то устоявшееся понимание программирования, которое в ФП отличается


          1. mayorovp
            27.12.2017 13:00

            Вот как раз каррирование — не более чем стиль, пусть и широко распространенный. Все что можно сделать с каррированной функцией — можно сделать и с обычной, общие концепции ФП на каррирование не завязаны.


          1. potan
            27.12.2017 13:43

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


        1. symbix
          26.12.2017 23:35

          ФП как парадигма проста в том же смысле, в каком просты RISC-архитектуры по сравнению с CISC. :-)


          1. potan
            27.12.2017 13:44

            Кнут в своих книгах не случайно использует RISC-подобную систему команд.


        1. VolCh
          26.12.2017 23:40

          Простая, пока не доходим до мутаций.


          1. potan
            27.12.2017 13:48

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


        1. Szer
          27.12.2017 10:00

          Мне кажется человек ошибся и говорил про процедурное программирование. ФП же нифига непростое, т.к. матан.


          1. potan
            27.12.2017 13:54

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


  1. RationalBot
    26.12.2017 22:20

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


    1. Lure_of_Chaos
      26.12.2017 23:04

      А Вы не объясните ООП на примере файла? Ну пожааалуйста


      1. VolCh
        26.12.2017 23:39

        Шаблон — класс, документ на основе шаблона — объект.


        1. mayorovp
          27.12.2017 10:50

          Это вы прототипное ООП объяснили :-)


          1. VolCh
            27.12.2017 14:25

            Прототипное — скопировать документ и начать редактировать его :) Под шаблоном я имею в виду не просто "рыбу", а отдельный формат типа .ott, а не odt. :)


            1. mayorovp
              27.12.2017 14:27

              Разве ott чем-то отличается от odt принципиально?


              1. VolCh
                27.12.2017 14:53

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


                1. mayorovp
                  27.12.2017 15:11

                  Ну да, в прототипном ООП все так же.


                  1. RationalBot
                    27.12.2017 17:13

                    У файла есть не только содержимое, но и атрибуты (мета-данные), которые могут наследоваться от папок, файловых систем, протоколов доступа и носителей.
                    Например, read-only доступ к файлу на CD.


      1. alix_ginger
        27.12.2017 11:27

        Например, так:
        «Графический файл» — интерфейс, описывает методы «getHeight», «getWidth» и т. д.
        «Файл JPEG», «Файл GIF» — классы, реализующие интерфейс.
        «Я_и_моя_любимая_кошка.JPG» — экземпляр класса.
        Открытие любого графического файла программой для просмотра — пример полиморфизма


    1. lovermann
      27.12.2017 10:46

      Просто все UI-штуки в компутере берутся и брались из реального мира, чтобы человеку стало удобнее и комфортнее работать с компом («рабочий стол», «папка», «корзина», «шаблон», «документ», ...).


  1. IAKO
    26.12.2017 22:20

    Скажу как начинающий.

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


    1. akryukov
      26.12.2017 22:38

      Достаточно большое и сложное надо долго писать. Но это наверное единственный работающий способ.


      1. VolCh
        26.12.2017 23:38

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


        1. MikailBag
          27.12.2017 17:33

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


          1. VolCh
            27.12.2017 20:49

            Как минимум, все геометрические фигуры являются геометрическими фигурами :)


            1. MikailBag
              27.12.2017 21:01

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


              1. VolCh
                27.12.2017 21:17

                Он вполне может быть абстрактным классом с конкретными методами типа задания базовой точки и её перемещения. Да и не во всех языках есть компиляция как таковая, тем более из которой что-то выкидывается.


      1. michael_vostrikov
        28.12.2017 00:20

        Необязательно совсем большое. У меня было так. Нам задали сделать какой-нибудь класс со свйоствами и как-нибудь его использовать. Я решил сделать рисование формы на экране (в DOS, BGI-графика). Думаю, сделаю сначала как обычно, с переменными, потом буду разбираться как из этого класс сделать. Ну что там, x,y,w,h. Для 2 форм объявляю x1, y1, x2, y2… Блин, уже 8 переменных, как бы их объединить. И как их называть если будет неизвестное количество. Ага, наверно классы для этого и нужны. Еще метод отрисовки туда удобно запихнуть. И инициализацию в конструкторе сделать. И переопределить отрисовку в другом классе, если нужна особая форма. Какая удобная штука оказывается.


    1. phgrey
      27.12.2017 18:31

      Да, быстрее. Садитесь и пишите.


      1. IAKO
        27.12.2017 22:07

        Так и ЧТО писать? Суть же в том, чтобы ученика привести к этому.


        1. phgrey
          27.12.2017 23:04

          Ответ на вопрос «ЧТО писать» неизменен — пишите код.

          Что именно замоделировать — выбирать вам. На удивление хорошо помогает приблизить понимание ООП RTS а-ля StarCraft. А к учителю уже приходите с конкретными проблемами, e.g. «Анатолий Викторович, я тут для танка, авианосца и пехотинца общего абстрактоного предка сделал, и вот никак не могу понять...»


          1. Mishkun
            28.12.2017 15:40

            Как раз-таки в играх намного лучше подходит Entity Framework подход, нежели создание иерархии. Выделяя отдельные способы поведения в отдельные компоненты и собирая из них новых юнитов, как из лего, мы можем отказаться от метода «всех под одну гребенку» и избавляемся от огромного God Blob предка, в котором половина методов используются только в половине наследников а в остальных — заглушки.


    1. so1ov
      28.12.2017 11:41

      Отвечу с другой стороны баррикад.

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

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


  1. MrBuha
    26.12.2017 22:20

    Однажды общался с программистом одним. Так он очень хорошее определение ООП дал. (Моё оценочное мнение)

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


    1. vedenin1980 Автор
      26.12.2017 22:48

      Так можно прекрасно дать свою задачу без всякого ООП.


      1. MrBuha
        27.12.2017 18:21

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


  1. laphroaig
    26.12.2017 23:13

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

    Да, ООП хорош для описания высокоуровневых абстракций, но дальше он должен уступать место другим парадигмам, таким как функциональное программирование, метапрограмминг и пр. Поэтому мне так нравиться C++ — это мультипарадигменный язык. А попытка писать на нем исключительно в стиле ООП сводит на нет все его преимущества, для этого есть Java, C# и пр. Да и множественное наследование в C++ — это вообще не про ООП, зато мощнейший инструмент для метапрограммирования.


    1. mayorovp
      27.12.2017 13:13

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

      Кстати, C# тоже вообще-то мультипарадигменный язык. Причем куда более «мульти» чем тот же C++.


  1. guessss_who
    27.12.2017 00:00

    Ох… Люто плюсую утверджение насчет того, что примеры, с которыми «продается» ООП — никуда не годятся. Лучше ли примеры, предолженные автором? Не уверен.

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

    А пока студенту впаривают про кошек и собак с методами «мяу» и «гав» — да, на выходе будет упомянутая автором «Java головного мозга». В лучшем случае.


    1. Hazactam
      27.12.2017 00:16

      Лучше ли примеры, предолженные автором? Не уверен.


      Потому что примеры, предложенные автором, большей частью, объясняют 'как', но не объясняют 'зачем'. 'Зачем' вообще непросто объяснить.


      1. vedenin1980 Автор
        27.12.2017 00:22

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


        1. VaalKIA
          27.12.2017 03:24

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


          1. michael_vostrikov
            27.12.2017 23:57

            Все нормально, требуем в описании самолета конкретную ветку в иерархии двигателей — Д123 с подвидами Д123-А, Д123-Б, Д123-В.


        1. Oxoron
          27.12.2017 14:25
          +1

          TLDR: слишком сложная тема для одного примера.

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

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

          У наследования есть несколько проявлений и форм (класс\абстрактный класс\интерфейс\Generic), иногда его используют не по назначению (см. пример выше), иногда наследование вообще явно запрещают. И это лишь в C#, в иных языках и не так извратиться могут. Короче, для демонстрации наследования надо привести 3 разных примера как минимум. И желательно показать примеры на трех разных занятиях, а объединить картинку на четвертом занятии.

          Инкапсуляция проще, но в терминах C# там желательно уже иметь представление о наследовании (ибо методы бывают protected), что приводит нас к трем-четырем занятиям (public\private, потом internal для тестов, потом protected, потом объединение знаний).

          Наконец, полиморфизм. Навскидку, это наследование абстрактного класса, возможно использование Generic, override методы. Опять-таки 3-4 занятия.

          Короче, 12 занятий, из них 9 примеров. Чтоб примеры впитались, желательно ввести студентов в ситуацию «сначала было плохо, потом хорошо». Девять разномастных примеров на ООП-темы. Еще желательно сильно связать языковой курс и курс ООП, как минимум на уровне 9 языковых фич для примеров (и всей базы для них).

          ООП — это что-то около 19 часов в семестр. У меня нет идеи проекта на 19 часов и 9 примеров (плюс еще полезная функциональность-экзамен-резерв). Вот и получается, что простые примеры (одинаково) неэффективны, а значит можно давать любой. В процессе работы студент разберется, вектор ему задан.

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


          1. third112
            27.12.2017 14:29
            -1

            ООП слишком сложная тема для раскрытия одним примером.
            Я привел такой пример.


          1. VolCh
            27.12.2017 15:08

            Инкапсуляция проще, но в терминах C# там желательно уже иметь представление о наследовании (ибо методы бывают protected), что приводит нас к трем-четырем занятиям (public\private, потом internal для тестов, потом protected, потом объединение знаний).

            public/private/… формально к инкапсуляции не относятся, это сокрытие. Публичные члены класса так же инакпсулированы, как и приватные.


            1. Oxoron
              27.12.2017 19:06

              Вполне возможно. Никогда не задумывался как можно объяснить инкапсуляцию без упоминания сокрытия.

              Просто заявить: «класс описывает некий набор объектов, и все значимые для объекта свойства\методы хранятся в классе, а не черте где. Это облегчает тестирование, проще ориентироваться по коду, сложнее прострелить себе ногу, и инкапсуляция нарушается через методы расширения (на самом деле нет).»?

              В принципе, может сработать, но как-то сильно очевидно. Сильно теоретично.


              1. VolCh
                27.12.2017 20:50

                "Всё своё ношу с собой" :)


  1. Jon7
    27.12.2017 00:50

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


    1. mayorovp
      27.12.2017 13:17

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


  1. Samouvazhektra
    27.12.2017 01:28

    +1 насчет того что обучают ООП не особо понятно.
    Вариант с питомцами/квадратами тоже бы мог проканать, если брать его не в контексте эволюции, а в рамках разработки игры. Вообще имхо неплохо бы на этапе после блок-схем с циклами/условиями и закреплению этих основ кодингом, перед ООП, переходить к моделированию по типу UML для прокачки навыков абстракций, композиций, агрегатов и прочего


  1. F376
    27.12.2017 02:38

    Понять и объяснить что такое объектно-ориентированное программирование, лучше всего изучив историю возникновения предмета. В этом плане рекомендую классику — «some features of the simula 67 language». Когда читаете, следует обратить внимание на некоторые аспекты, остающиеся за кадром. Симула (что понятно даже исходя из названия) предназначалась для построения симуляторов некоторой предметной области. Для симуляции требовались небольшие самостоятельные взаимодействующие программки. Это и были self-containing program или как они иначе называются «объекты». Именно так. Объекты — вообще-то самостоятельные (замкнутые, самодостаточные, автономные, изолированные) программы. Это напрямую написано в документе «some features of the simula 67 language», почему я и говорю — начинать изучение чего-либо с первооснов самое благое дело.
    Внутри компьютера такого устройства конечно не было (не так оно и сейчас — данные отдельно, а методы объектов как были старыми, добрыми процедурами, так и остались). Но для программиста организация программы выглядела как отдельные, активные блоки программ, «данные с кодом» или «активные данные», «данные с активностью» (data + activity). Несмотря на внешнее представление, всегда полезно помнить — ООП это лишь способ организации текста программы и не более того (хотя найдутся те кто будет протестовать, увы, это так). ООП можно «симулировать» в процедурных языках, создавая структуры (записи, пакеты, кортежи) данных и семантически связанные с ними функции. Полезно также заметить, что Страуструп, создавая C++ был занят в большом проекте симулятора, вот почему он обратил внимание на Simula и вот почему он столкнулся с объектно-ориентированной парадигмой вообще. Прикручивать объекты к Cи они решили после того как Garbage-collected симула их не устроила. Поэтому они стали прикручивать объекты к относительному быстрому системному Си. Первое расширение языка Си так сначала и называлось «Си с классами». Также полезно знать, как они это сделали — они не писали новый компилятор вообще. Они написали транслятор расширения языка в старый добрый Си, тексты которого компилировались исходно существующим компилятором cpre. И это само по себе говорит о многом о ООП. Грубо говоря, на входе заходил синтаксис в котором у структуры были приписаны функции, а на выходе выходил обычный текст си-программы, где были старые добрые структуры (с данными) и отдельно — функции для работы с ними. Отсюда мы видим — что ООП это вовсе несколько не то, что принято объяснять. ООП в исходном своем понимании, это прежде всего объекты, где объекты это полноценные изолированные программы. Эти программы также могут пониматься как «активные данные», получающие процессорное время отведенное в оригинале на симуляцию. И, соответственно, в исходной симуле внутренний «мир» состоял из таких вот самостоятельных программ-объектов, взаимодействующих и существующих параллельно. Так я рекомендую понимать ООП-программу и вам, потому что так оно без изменений сохранилось и сейчас. Такова самая-самая суть ООП, та самая, почему оно возникло.


    1. vadimr
      27.12.2017 19:13

      К этому следует добавить, что наследование вообще не является существенной чертой объектного программирования, вопреки известной попсовой формуле “инкапсуляция+наследование+полиморфизм”. Поэтому объяснять сущность ООП через наследование – это как объяснять назначение самолёта через принцип работы двигателя внутреннего сгорания.

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


      1. F376
        27.12.2017 22:24

        К этому следует добавить, что наследование вообще не является существенной чертой объектного программирования, вопреки известной попсовой формуле “инкапсуляция+наследование+полиморфизм”. Поэтому объяснять сущность ООП через наследование – это как объяснять назначение самолёта через принцип работы двигателя внутреннего сгорания.

        Абсолютно верно! Я это выразил в одном из своих дальнейших опусов под этой статьей.

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

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

        Стоит умственно осуществить такую подмену — и все таинственные откровения, aka принципы, например LSP (Liskov substitution principle) становятся ясны по-умолчанию. Кстати, у Гослинга наследование в Java обозначается как extends…

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

        Вот так, я считаю, один неудачный термин «наследование» породил непонимание что это такое, и как следствие привел ко всей этой перманетной трагедии…


  1. shuhray
    27.12.2017 03:30

    Учился на мехмате МГУ в конце 80-х. Лекция по программированию в большой аудитории (на весь поток, половина курса). Лектор у доски невозмутимо рассказывает про «исполнитель — шагающий человечек», а в аудитории стоит шум, вой, летают бумажные самолётики, студенты-математики протестуют против изнасилования разума.
    Сейчас по необходимости пытаюсь программировать. На моё неприятие ООП Сергей Трифонов (работал в Яндексе с 90-го года с перерывами семь лет) ответил:
    «Эти языки предназначены для того, чтобы большие команды программистов могли вместе работать с обширным массивом кода. Эта задача требует введения очень серьёзной дисциплины, это ни разу не бесплатное удовольствие. Вам это ни за фигом не нужно, вот Вам и не нравится. Правильно, что не нравится, это просто не Ваше.»
    66george.livejournal.com/381121.html#comments


    1. cesare
      27.12.2017 17:59

      Лектор у доски
      — Кушниренко, поди?


      1. shuhray
        27.12.2017 18:37

        Не помню. Помню, что семинары вёл Немытов.


  1. trawl
    27.12.2017 07:24

    Не очень понял, зачем чертежу методы взлёт(), полет(), посадка()
    Или в конструкторе класса алгоритм складывания самолётика из листа?


  1. leossnet
    27.12.2017 08:04

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

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

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

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

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

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


    1. VolCh
      27.12.2017 14:44

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


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


  1. Ogra
    27.12.2017 08:19

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

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

    Начинайте с простых вещей, объясняйте их без всяких аналогий, расширяйте понятийный аппарат постепенно — и все станет гораздо понятнее.


  1. geher
    27.12.2017 09:11

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

    Метод "голос" у рыбки не делает ничего или возвращает звуковой фрагмент нулевьй длины (в зависимости от постановки задачи).


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

    Кактус очень даже ест. Только способ питания у него другой.
    Вот если был метод "глотает", пришлось бы поступить как с методом "голос".


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


    Мы уже запутались,

    Не обязательно.


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

    А Вовочке остается просто объяснить все это на примере сформированной иерархии классов.


  1. envtmp
    27.12.2017 11:02

    А потом приходят братья Райт, разработчики конвертопланов (bell v-22 osprey), конструкторы автожиров и «летабщих крыльев» и исходный чертёж летит в печку. Потому что абстрактный чертёж вашего самолёта не позволяет создать подходящюю имплементацию и интерфейсы не подходят.
    А экземпляры данных классов тем не менее похожи на экземпляры имплементаций вашего чертежа.

    И чем это лучше рыбок?


  1. red-barbarian
    27.12.2017 11:02

    Отличные аналогии, но хотелось бы уточнить «интерфейс»

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

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


  1. Sevensenn
    27.12.2017 11:02

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

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


  1. rjhdby
    27.12.2017 11:38
    +1

    Объектно-ориентированное программирование это не описание объектов реального мира (точнее не только и столько описание реального мира)

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


  1. ZhanD
    27.12.2017 11:41

    Когда я учился в университете мне довольно тяжело было понять ООП (Объектно-ориентированное программирование)… сами преподаватели не совсем понимали, в чем же суть ООП

    Ооо… вспомнил свое первое знакомство с ООП в универе. Наш «молодой» преподаватель по С++ на все вопросы отвечал одними и теми же мудренными фразами из лекции(бесконечно повторяя их на разные лады), приводил все тот же один пример с машинами. С презрением приговаривая: «Ну, что тут может быть не понятно?».
    Что прикольно, нам казалось, что мы тупые, а препод настолько умный, что ему трудно изъясняться с нами дуболомами.


  1. anprs
    27.12.2017 11:46

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

    При этом описаны абстрактные методы взлёта/полёта/посадки. В чертеже. Ну допустим :)

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

    При том что абстрактный класс ЧертежСамолета реализует интерфейс ТребованияКСамолету, спецификация требований к самолёту у нас появляется позже общего чертежа. Ну допустим.

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

    Если поначалу была какая-то неопределённость с терминологией и в первой части можно себе представить просто эскиз, а во второй части — документ «спецификация требований», составляющие вместе пакет проектной документации (вместо слова «чертёж» каждый раз), то вот это прям чертёж. Чертёж, по которому будет строиться конкретный самолёт. Чертёж, в котором описана реализация взлёта через операции «поздороваться_с_пассажирами» и «провести_инструктаж». Что в случае отсутствия пассажиров в лучшем случае может привести к потере времени на проведение инструктажа для пустого салона. А то и к NPE.

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

    Уверен? Тяговооружённость, лобовое сопротивление, тормоза, реверс тяги, интерцепторы… Не! только размах и количество двигателей.
    Я уже молчу про «вПП», «независимо от» и прочее «от того, что как»

    Вот чем вышеперечисленное принципиально лучше и понятнее этого:
    вот есть класс Домашние любимцы с методами «голос» и «есть», от него мы наследуем Кошку и Собаку и все хорошо.
    Но тут приходит Света и приносит аквариумных рыбок, которые не разговаривают, а потом приходит Вася, которые приносит любимый кактус, которые не только не разговаривает, но и не ест.
    Мы уже запутались…

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


  1. ozonar
    27.12.2017 12:26

    Уверен, что по такому объяснению можно понять ООП только если ты уже знаешь ООП, новичок вообще не поймёт о чём речь.

    Самая главная проблема объяснения ООП — мы не храним ни самолёты, ни котов, в 70% в классов мы храним просто код, который просто должен вызываться в некоторых случаях.

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

    И в таком случае смысл нестатических методов и классов совершенно неочевиден. А ещё давит огромное различие между кодом фреймворка и объясняемых и в универе и в таких постах: «Матроскиным как классом». Вообще никакого сходства.


  1. nemavasi
    27.12.2017 12:29

    Надо разделять 2 концепции ООП.
    1) Объект = Данные + Методы работы с ними
    2) Объект — это сущность, можно сказать «одушевленная», у нее главное — что она может делать, а не ее свойства.
    Пример первого варианта — любой объект который соответствует JavaBeans. Вместо того чтобы инкапсулировать он через геттеры отдает все свои основные параметры. а через сеттеры может их получить. Т.е. это даже не объект, а улучшенная структура данных. Часто такие объекты сопровождаются сервисами которые включают себя поведение этого «объекта». Именно в результате первой концепции возникают чаще всего странные сущности МенеджерЧегоТоТам, а код часто превращается в винегрет.
    А вот вторая концепция — это то что нужно, но то что плохо осознано программистами. Когда чертеж самолета печатает себя на экране сам. А не все его данные достаются кем-то и печатаются


    1. mayorovp
      27.12.2017 13:27

      Надо разделять прежде всего саму парадигму и ее языковую поддержку.

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

      Бывает и наоборот: дескриптор окна (HWND) в WinAPI с точки зрения ООП ведет себя как ссылка на объект. Но с точки зрения языка это просто число.


      1. Ogra
        27.12.2017 14:43

        дескриптор окна (HWND) в WinAPI с точки зрения ООП ведет себя как ссылка на объект. Но с точки зрения языка это просто число.


        Мой любимый пример полиморфизма без ООП — файловая система в Unix. В то время как хэндлер — это просто число, за ним может скрываться все что угодно: файл на диске, файл на удаленном компьютере, периферийное устройство, генератор случайных чисел, да хоть ничего (/dev/null).


  1. vyatsek
    27.12.2017 12:33

    Чтобы объяснить что-то, надо начинать с постановки проблемы.
    Для чего вообще интерфейсы и классы, почему нельзя обойтись без них?
    Чтобы объяснять наследование необходимо ввести понятие абстрактный тип данных. ru.wikipedia.org/wiki/%D0%90%D0%B1%D1%81%D1%82%D1%80%D0%B0%D0%BA%D1%82%D0%BD%D1%8B%D0%B9_%D1%82%D0%B8%D0%BF_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85

    Абстра?ктный тип да?нных (АТД) — это математическая модель для типов данных, где тип данных определяется поведением (семантикой) с точки зрения пользователя данных, а именно в терминах возможных значений, возможных операций над данными этого типа и поведения этих операций.

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


    1. mayorovp
      27.12.2017 13:28

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


  1. DSLow
    27.12.2017 12:34

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

    К чему есть претензии по статье:
    Аналогии то приведены, и объяснено как и что там. Теперь как это транслировать на «нормальный» язык, чтобы объясниться в профессиональной сфере. Не будет же человек говорить что-то вроде «Наследование это когда из 120 мест авиалайнера станет 130 без изменения исходного чертежа». Ну и т.д.


  1. third112
    27.12.2017 12:56

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


  1. elegorod
    27.12.2017 13:06

    Представить класс в виде чертежа — хорошая идея, но этот пример кода полностью всё портит:

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

    Самолёт = новый чертёж самолёта? Что за бред, мы сделали новый самолёт, а не нарисовали новый чертёж. И тип объекта, который получился в результате — самолёт, а никак не ЧертежСамолета. Меня бы это всё ещё больше запутало, чем в примере с котами и собаками.

    Лучше уже сделать класс Боинг737, тогда хоть код логичный будет:
    Самолет тотСамыйПотрепанныйБоингНаКоторомМыЛетелиВТурцию = new Боинг737();

    Или делать чертёж как factory:
    Самолет тотСамыйПотрепанныйБоингНаКоторомМыЛетелиВТурцию = ЧертежПассажирскогоСамолета.построить();


    1. michael_vostrikov
      28.12.2017 00:43

      Построить чертеж? Или все-таки "построить по чертежу". И тогда это ничем не отличается от "сделать новый по чертежу".


  1. maxstroy
    27.12.2017 14:12
    +2

    ИМХО, чертеж не летает. Поэтому нельзя создать чертеж самолета со свойством полета. Разве, что мы запустим бумажный самолет, сделанный из бумаги, на которой будет чертеж самолета.
    Вот смоделировать тип объектов, каждый из которых может летать, мы можем. Можем смоделировать класс (класс не равно тип). Если мы говорим о типе объектов (концепте), то можем сказать: каждый объект данного типа может летать.


    1. third112
      27.12.2017 14:22

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


      1. maxstroy
        27.12.2017 14:45
        -1

        Автор стремится к чему-то приблизиться. Это очень важно — иметь вопросы и пытаться на них ответить.


        1. third112
          27.12.2017 14:48
          -1

          А еще объекты названы инстансами.
          Где названы? Я всегда называю объекты объектами, а классы — классами :)


  1. third112
    27.12.2017 14:47

    Не та ветка


  1. Akon32
    27.12.2017 15:28

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


    Вероятно, лучше объяснять абстрактно, в ключе "объект — это данные + методы для работы с ними; класс — это описание, по которому создаётся объект; интерфейс — это список сигнатур методов; полиморфизм — возможность работать с разнотипными объектами посредством единого интерфейса", но на примерах, более близких к реальности, например на API файловой системы (есть файлы, у которых есть имя; некоторые файлы — это каталоги, у которых вдобавок к имени есть список потомков; файлы можно создавать, переименовывать, удалять; и т.д.).


  1. SendMess
    27.12.2017 15:34

    При прочтении возникаю следующие вопросы:
    1. Почему чертеж умеет летать
    2. Почему чертеж располагает информацией о количестве пассажиров (ок, если это опечатка: что если несколько мест сломается? Нужно будет наследоваться?)
    3. Какой практический толк от абстрактного класса ЧертежСамолета, если, очевидно, что «полет» будет принципиально разным не только для различных моделей, но и для различных объектов. Или мы все сводим к write(«самолет летит»);? Тогда не вижу разницы между этим и животными.
    4. Не очень понятна зона ответственности класса: пассажирский самолет умеет здороваться с пассажирами. Это как? Может, все таки, этим занимается не самолет, а ответственный за это модуль (человек).
    5. Странно, что это никто не заметил, но статическая переменная «аварийность»? Вы же в курсе должны быть, что статик переменные имеют одно значение для всех объектов класса. Плюс ко всему статик-методы и наследование работает не так, как вы ожидаете. Не знаю, как в жаве, вы на ней код приводите, но в C# нельзя вызывать статик метод у объекта, что очень логично, так как смысла в этом нет.
    6. Ваше объяснение полиморфизма сможет понять только тот, кто уже прокрутил в голове, как это будет выглядеть в коде. Но проблема даже не в этом, как вы будете определять: может ли самолет сесть на данную полосу? Пытаться кастовать вниз по иерархии? Тогда в чем полиморфизм?
    7. Просто наблюдение: студенты в университете очень ленивые и большинство вообще не знает, зачем учится. У них даже склад ума не у всех технический. Ваш пример пока от кошек и собак ушел не очень далеко, тогда вопрос: чем же он лучше, если априори более сложный для восприятия.


    1. michael_vostrikov
      28.12.2017 00:49

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


      1. SendMess
        28.12.2017 02:47

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


        1. michael_vostrikov
          28.12.2017 11:31

          В чертежах написано "самолет может летать". Здесь тоже. В чем разница?
          Если более точно аналогию проводить, то чертеж это переменные класса, его состав, а методы это проектная документация.


          1. SendMess
            28.12.2017 14:39

            «В чертежах написано „самолет может летать“» != «чертеж умеет летать». Чертеж не отвечает и не должен отвечать за сам процесс полета, это не его функция, а значит этого метода в этом классе вообще быть не должно.
            «Если более точно аналогию проводить, то чертеж это переменные класса, его состав, а методы это проектная документация.» Ну и как эта аналогия помогает понять, что вообще такое ООП. Я вот ход ваших мыслей совсем не улавливаю. А если у нас не самолеты, а люди. Что для людей такое: «проектная документация».
            Примеры автора не проходят, к примеру, принципы SOLID, это довольно весомый аргумент в пользу того, что его архитектура неверная. Если вы хотите защитить его подход, то давайте не будем размышлять о том, как это сделано в мире реальном (это ни к чему не приводит), а ближе к программированию, плз.


            1. michael_vostrikov
              28.12.2017 16:30

              а значит этого метода в этом классе вообще быть не должно.

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


              Ну и как эта аналогия помогает понять, что вообще такое ООП.

              Она помогает понять, что такое "класс" в языках программирования, зачем он используется, и как связан с объектом (экземпляром).


              1. SendMess
                28.12.2017 18:18

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


                1. michael_vostrikov
                  28.12.2017 20:01

                  вы не конструктивно ведете беседу

                  Но ведь я просто повторил ваш довод. Значит он тоже неконструктивный. Зачем вы его тогда привели? Вам можно так делать, а другие не должны?
                  Более того, я его повторил именно чтобы показать, что он неправильный.


                  перестаньте игнорировать факты и цепляться за аналогии, не относящиеся к сути вопроса.

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


                  Докажите мне на примере известных принципов проектирования свою правоту

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


                  Я вам привел конкретику на примере SOLID, вы же просто вертите словами

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


  1. geher
    27.12.2017 15:40

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


    И самое главное. ООП не догма.
    Описание некоторых аспектов поведения объекта вполне может выполняться в традициях структурного программирования. Могут быть и иные отклонения от ООП.
    Главное при этом просто понимать место таких отклонений в общей картине взаимодействия объектов.


  1. jrip
    27.12.2017 15:51
    +1

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

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


  1. Scf
    27.12.2017 15:56

    Я думаю, начинать надо было не с ООП, а с разъяснения понятий "тип" и "значение".
    Тогда концепция классов и объектов — самоочевидна.


  1. F376
    27.12.2017 16:19

    Аналогии задействованные автором и смешение их в кучу в таком виде — порочный путь. Как это ни прискорбно, следует признать — хорошего материала, проясняющего суть ООП всё же до сих пор нет. Причем, работа при написании такой статьи/книги будет весьма интересная — предстоит прояснять как бы в целом известные и даже до определенной степени представимые понятия, но в ином ключе, направляя мысль в другом направлении. Занятие это очень неблагодарное и его ценят лишь те люди, которые действительно хотят разобраться, до последней тонкости. Большинство, к сожалению, реагирует «мне это было известно», «это очевидно» а то и хуже — начинают спорить, отстаивая свою точку зрения лишь потому что она у них устоялась или была усвоена первой.

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

    Статические члены, виртуальные методы, абстрактные классы, интерфейсы — это сугубо вспомогательные приемы, к сути ООП отношения не имеющие (в C++ интерфейсы вообще отсутствуют).

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

    Тогда что такое ООП? Согласно названию — это программирование на основе объектов. Звучит тривиально и тавтологически, но это так. Что такое «объекты» в самой своей сути, как они должны пониматься — было описано выше, объекты, как нам прояснили авторы первого объектно-ориентированного языка Simula, это самостоятельные, самодостаточные (закрытые) программы (общающиеся между собой — это важно!). Программа же, и это понятно, содержит код и (опционально) состояние, т.е. данные. Понимание этой мысли, насчет «самостоятельных программ» — отправная точка для понимания всего ООП, проектирования ОО-программ, популярности ООП как таковой, а также вытекающих отсюда как само собой разумеющееся, принципов information hiding и separation of concerns. Понятие «общения» объектов-программ между собой автоматически влечет понятия протокола и интерфейса.

    Теперь о еще одной причине возникновения ООП.
    Программирование порождает проблемы не только в вычислениях и в алгоритмах, но и при самой инженерии больших программных систем. Это тот момент, который трудно понять не только начинающим, но и просто большинству людей, так как многим не довелось работать в коллективах в которых программные системы создаются годами и десятилетиями. И это тот же момент, почему в конце 80х-начале 90х ООП подход был прохладно принят в нашем отечестве. Дело в том, что масштабы систем, за редким исключением были разные, наши люди работали либо в одиночку, либо малыми коллективами из 3х-5ти человек, где наиболее насущна потребность скорее все еще в отдельных подпрограммах (процедурах и функциях), нежели чем в отдельных, самостоятельных программах (т.е. объектах). Верно и обратное, достаточно легко представить себе коллектив из 3х-5ти человек, работающий над некоторым программным решением. (Типичная ситуация большинства фирм и НИИ как в 70е-90е, так и по сей день). Но вот когда дело доходит до коллективов хотя бы из 50-ти человек (не говоря уже о смене людей за годы разработки), вот тут-то дело и становится тухлым. Стоит лишь представить себе что вся эта огромная толпа народа постоянно копошится в гигантской куче переменных, процедур и функций, как становится понятно, что все это добром не кончится. :) Приходится или прикладывать изрядные усилия, или всё достаточно быстро превращается в невообразимое месиво. Это происходит чисто статистически, по законам развития систем.

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

    Конечно, можно возразить что многие очень крупные системы написаны без объектно-ориентированного подхода. Но если очень внимательно приглядеться, то то, как они написаны напоминает ООП-подход. К примеру, код крупных операционных систем написан небольшими, отдельно компилируемыми модулями, каждый со своими приватными переменными (в точности также как объект) и каждый со своим заголовочным файлом, содержащим «интерфейс» к этому модулю, что представляет собой аналог класса с объявленными в нем методами. Существует и своего рода «наследование», только не статически, в момент компиляции, а динамически, на уровне run-time библиотек. Если требуется унаследоваться, может быть написана новая динамически загружаемая библиотека-плагин, которая содержит новую функциональность, и которая, в свою очередь, делегирует к старым, уже написанным и подгружаемым библиотекам. Это не хак и не костыль, а самое настоящее наследование, имеющее своей сутью композицию, делегацию и перенесенное на уровень исполнения. Также на ум приходит Microsoft COM, своеобразный run-time аналог ООП.

    Моя задача была привить правильное (с моей точки зрения) понимание что такое ООП в самой своей сути. Подчеркну, ООП — это метод организации программных текстов, или способ написания программ, состоящих в свою очередь из небольших изолированных, функционально самостоятельных и законченных программ. Эти программы называются «объекты» (сейчас с этим пониманием более ассоциируются классы, но как выше было замечено, классы далеко не всегда присущи ОО-системам). При этом важно обратить внимание на общение этих своеобразных «программ», откуда проистекает необходимость в определении протоколов общения, откуда, в свою очередь автоматически вытекает понятие интерфейса. Как видно, все связано, всё объяснимо, и когда мы сталкиваемся с некими абстракциями, типа data hiding — мы их воспринимаем уже не как «тайное знание», а как вполне очевидную и объяснимую вещь. Ну, если, конечно, достаточно глубоко задумываться над смыслом всех прозвучавших слов и если ознакомиться с упомянутым выше «Some features of the Simula 67 language».

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


    1. akryukov
      27.12.2017 16:59

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

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


      Когда студент хочет понять ООП, он вряд ли хочет услышать рассказ смысле ООП в языке 60-70 годов. Ему нужно объяснить то, что большинство специалистов понимают под ООП сейчас. Потому что он хочет в итоге пройти собеседование и устроиться на работу. На этом собеседовании его скорее всего спросят "какие основные принципы ООП?" и будут выяснять понимание терминов: класс, абстракция, инкапсуляция, наследование, полиморфизм и т.п.


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


      1. F376
        27.12.2017 17:23

        Благодарю за хорошее замечание. Вообще говоря, я не ставил перед собой вопроса «что и как нужно изучать студенту». Хорошим анти-примером что сегодня можно «изучить» служит вся эта статья, весь этот материал с попыткой осмысления что же такое есть ООП. При всем моем уважении к автору, его понятия все еще ммм… далеки от идеала. Скорее, это некое первичное приближение, попытка осмысления сути.

        Студенты студентам рознь.
        В общеобразовательных российских заведениях студенты готовятся именно так, как вы считаете нужным — бессмысленно натаскиваются на зубрёжку формул и определений. Ведь именно это будут у них спрашивать на экзамене, верно? Так происходит подготовка людей общей специальности. И только в ограниченном числе ВУЗ'ов, на отдельно взятых кафедрах готовят ученых-исследователей, тех кто будет открывать новые законы, устанавливать новые закономерности и т.д. У подобных студентов в курс обязательно входит литература по истории предмета. Обычным студентам это не дают или дают очень ограниченно. Также, историю развития и взгляды основоположников всегда очень тщательно и скурпулезно изучают в старейших университетах мира — Кембридже, Оксфорде, это не говоря о том порой сами лекции там можно услышать из уст самих «основоположников».

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


        1. michael_vostrikov
          28.12.2017 01:01

          А мне кажется, вполне неплохая статья. Класс — это чертеж объекта.


    1. ottepel84
      27.12.2017 19:10

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


      1. F376
        27.12.2017 21:35

        Я примерно так и мыслил ООП при изучении этой парадигмы.

        Есть подозрение, что принцип этот вы поняли, но, как это часто бывает, применить его сумели не вполне…
        Только вот меня всегда сбивала мысль о том что в обычной программе множество параллельно взаимодействующих объектов не возможно… всегда существует некая главная процедура которая которая приводит в действие иерархию объектов, причем последовательно.
        Это «инерция мышления», идущая, вероятно, от процедурной парадигмы программирования. ОО реализация предполагает что может быть вызван любой публичный метод. Некая сущность извне может вызвать публичные, т.е. заранее выставленные наружу методы этого мира, образуя event-driven систему. А вот вопрос о параллельном вызове сразу двух и более методов объектного мира — это вопрос уже не из области ОО, а из области параллелизма (конкурентного исполнения). ОО не имеет к нему отношения, вопросов этих не решает и их не касается.
        А так же я не могу понять как не запутаться меж связей объектов в реально работающей программе и связях классов от которых объекты произведены, от этой двойственности идет кругом голова.
        Это получается от того, что вы сейчас представляете полезную программу как размытую по методам различных классов, то есть игнорируете тот факт, что класс должен быть цельной «программой».
        ОО-программу надо представлять так. Надо сфокусироваться только на методах «своего» класса. Все остальные классы и их методы вас интересовать не должны. Они выполняют свою работу, и пусть, а вам должно быть достаточно только информации о их названиях, передаваемых параметрах и возвращаемых значениях, пусть это будут «черные ящики», выполняющие свою работу. Самое тяжелое — это научиться психологически «доверять» используемым типам, игнорируя код их методов.
        Этот же подход освободит (или по крайней мере релаксирует) от попытки анализа всех связей в программе. ОО-программы часто используют полиморфную подстановку типов, так что какой там будет конкретный тип и какой выполнится код заранее может быть неизвестно.

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


      1. VolCh
        27.12.2017 22:14

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

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


      1. F376
        27.12.2017 23:03

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


  1. Riod
    27.12.2017 16:49

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


  1. andreyverbin
    27.12.2017 17:55

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

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


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

    Скорее всего вам потребуется что-то посложнее, начнутся всякие паттерны и прочее. А паттерны это, в частности, о том как делить систему на части. То что кто-то части называет объектами и классами, а кто-то функциями и модулями проблемы не меняет.


  1. nikitasius
    27.12.2017 18:03

    На моей памяти старый и избитый пример телефонной книги с контактами...


  1. phgrey
    27.12.2017 18:30
    +1

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


  1. pfihr
    27.12.2017 19:52

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


  1. F376
    27.12.2017 19:53

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

    В ООП наблюдается data и code locality, образующие один объект. Но эта локальность сугубо организационно-текстового характера, или говоря иначе, психологический, организационный аспект для инженера и программиста. Также, можно сказать что она образует «системообразующий» фактор, но это снова проявляется лишь на бумаге, т.е. при взаимодействии человек-машина. Системы подчиняющиеся какой-либо симметрии и локальности обладают меньшей системной энтропией, что и обуславливает их определенные преимущества.


  1. F376
    27.12.2017 19:54

    Но я бы не советовал путать организационную locality, такую как она существует в тексте ОО-программ и реальную, физическую, ибо современные реализации ОО ЯВУ, компиляторы, операционные системы и форматы исполняемых модулей организованы так, что тела методов (код процессора) лежат в одном месте, vmt лежат во втором месте, и память для полей объектов выделяется из кучи или на стеке. Хотя в целом определенные выгоды от локальности (без предпринимаемых усилий) могут наблюдаться здесь и на физическом уровне. Хотя в большинстве случаев типичная, неконтролируемая ОО-реализация напротив, имеет худшие характеристики, если не предпринято иное (например пулирование объектов). Подчеркиваю, именно реализация, так как в самой объектной парадигме ничего такого нет, и если бы hardware было бы построено с учетом ОО, это можно было бы эффективно использовать.


  1. idlecha_tar
    27.12.2017 21:17

    А можно те же примеры, только на языке программирования С++, ибо он мне более понятен?


  1. stoune
    28.12.2017 02:03

    Недостаток всех этих подходов в оторваности от реальной практики проэктирования.
    Задача ООА и ООД моделирование предметной области.
    Так почему не взять маленькую задачу и смоделировать её от А до Я.
    Поэтому в книге Лармана один из лучших подходов www.ozon.ru/context/detail/id/3105480 для введения в ООД и ООА.


  1. Throwable
    28.12.2017 02:44

    Не существует никаких классов и сущностей, есть только интерфейсы. Абстрактный водитель может управлять всем чем угодно, что реализует стандартный интерфейс "автомобиль" и имеет руль, педали и прочие приблуды. Более того, для управления нам не нужна никакая сущность типа "абстрактный или конкретный автомобиль": водитель может ездить на тренажере по виртуальной локации еще вместе с десятком таких же водителей. Здесь "сущность", которой управляем, уже выделить сложно. Точно также с другой стороны у нас нет никакой необходимости в сущности "абстрактный водитель" — стандартным такси может управлять механизм, подключенный к единой сети, которая управляет его действиями.


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


    • собака.сожрать(сосиска)
    • сосиска.бытьСожранной(собака)
    • new ПожирательноеДействие(собака, сосиска).сделать()


    1. geher
      28.12.2017 11:26
      +1

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


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


      Если в основе модели сам процесс, то третий.


      Если у нас собака и сосиска равнозначные объекты, то можно в методе сожрать вызвать метод бытьСожранной или наоборот.


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


      Все упирается в модель. А модель зависит от решаемой задачи.


      1. VolCh
        28.12.2017 13:31

        Если у нас собака и сосиска равнозначные объекты, то можно в методе сожрать вызвать метод бытьСожранной или наоборот.

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


      1. vlreshet
        28.12.2017 16:05

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


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


        1. VolCh
          28.12.2017 16:53

          Если время пожирания имеет значение для задачи моделирования, то время физического исполнения метода не имеет значения для функциональности, время пожирания должно учитываться в методе пожирания, например, по событиям таймера, если нам нужно "онлайн пожирание" или просто храниться в состоянии и влиять на поведение методов типа "жрётЛиНаТаймстамп(таймстамп)


          1. vlreshet
            28.12.2017 17:42

            Точно ясно только одно — собака охренеет от таких раскладов


    1. shuhray
      29.12.2017 00:25

      То вечности жерлом пожрётся
      И общей не уйдёт судьбы!


  1. ilitnyexpert
    28.12.2017 11:40

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

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

    Куда важнее объяснить студентам, что ООП — это мишура. И то что это лишь одна из парадигм, а не единственный способ построения «архитектуры». То же наследование, в реальном ООП-проекте в 10-20% случаев нужно, в остальном без него будет лучше обойтись. Модификаторы доступа — инструмент для захламления кода, добавления гемора и траты кучи времени на лишнюю работу. Да, это спасает от дураков иногда, и уменьшает колво ошибок иногда, но во-первых оно и добавляет ошибок, но уже при проектировании, а во-вторых если у вас дураки пишут код, то проект уже ничего не спасет.

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

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


    1. 0xd34df00d
      28.12.2017 20:43

      Адептам функциональньщины захотевшим поддержать меня сразу скажу что область применения функциональщины в чистом виде — 5% проектов.

      Чисто из любопытства: откуда это число? В моём опыте оно существенно выше.


      1. ilitnyexpert
        29.12.2017 02:00

        С потолка.

        В моём опыте оно существенно выше.

        А в какого рода проектах вы ФП используете?


        1. 0xd34df00d
          29.12.2017 03:42

          Да всякое попадается. От всякой ерунды и мелочевки вроде реплея логов или парсинга/скрейпинга сайтов до написания всяких компилятороподобных вещей.


          1. ilitnyexpert
            29.12.2017 16:06
            -2

            Думаю использование ФП в большинстве этих кейсов явно излишне


            1. 0xd34df00d
              29.12.2017 19:13

              Окей, почему вы так думаете?


            1. Oxoron
              29.12.2017 19:37

              В вашем ответе изящно переплелись «Думаю» и «явно излишне».
              Что до ФП — в указанных кейсах оно вполне может быть оправдано.


  1. helgisbox
    28.12.2017 13:46

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

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


  1. s_suhanov
    28.12.2017 14:50
    -1

    класс — это чертеж самолета, а объект (инстанс) класса — это сам самолет

    Спасибо КЭП.


  1. saboteur_kiev
    28.12.2017 17:27

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

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

    Таким образом объект — это набор данных, с теми методами которые умеют с этими данными работать напрямую.

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

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


  1. mortimoro
    29.12.2017 16:51

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

    Для меня этот вопрос прояснила книга «PHP. Объекты, шаблоны и методики программирования», автор Мэт Зандстра. Мэт объясняет доступным языком, в удобном порядке, в качестве примеров использует приближенный к реальности проект интернет-магазина и поясняет как именно тот или иной принцип структурирования помогает решить конкретную проблему.