UProLa

Неокріпші думки

Дещо про один міф ООП

with 15 comments

Я уже давно задумувався, що з цим ООП щось негаразд.

Буквально на днях я нарешті розшифрував для себе цю абревіатуру – об’єктно-орієнтоване програмування. Це було щось типу “a-ha effect”, еврика, відкриття століття. Тепер я зміг сформулювати своє перше критичне зауваження до парадигми: чому ми класс-орієнтоване програмування називаємо об’єктно-орієнтованим?

Якщо копнути глибше, то під ООП кожен розуміє щось своє або цілий комплекс властивостей:
– власне створення об’єктів і пересилка повідомлень між ними
– наслідування і поліморфізм об’єктів
– писанина типів об’єктів (класів). Клас – це тип
– писанина проміжних абстракцій-класів у мовах, які тільки їх і підтримують (С№, Java)
– організація коду (інкапусляція) у певні одиниці (класи)
– створення ієрархічних структур даних
– зручності типу властивостей, енумів і тп
– інтерфейси

Зараз добре проглядається тенденція розглядати посилання повідомлення як виклик методу. (Якщо хтось не в’їхав у цю фразу: між об’єктами є тільки один тип взаємодії – посилання конкретного повідомлення від одного об’єкта до іншого, і у виразі “abcde”.replace(“a”,”z”) назва функції з аргументами буде повідомленням, “abcde” – об’єктом отримувачем, а посилаючим об’єктом буде наша програма або той об’єкт, в якому ця програма знаходиться). Проте тоді постає питання: у чому принципова різниця між ОКОП (об’єкт-клас ОП) і структурним (модульним) програмуванням?

Правильна відповідь: у наслідуванні та поліморфізмі. Тепер таке питання: навіщо вони потрібні?
– якщо маємо кілька схожих абстракцій, то можна зменшити кількість дублюючого коду (наслідування)
– можна вживати дочірній тип замість батьківського (поліморфізм). Тобто, програмі все-рівно, з яким об’єктом працювати – якщо інтерфейси однакові, то пофігу на реалізацію логіки
– можна створювати свою логіку на основі існуючих компонент у пару кроків (наслідування)

Власне, за це і хвалять ООПшники свою парадигму. Я ж бо відповім на кожен з цих пунктів так:
1. Цей ефект досягається при правильній організації функцій, читай про рефакторинг методів
2. буде нижче
3. Модульне програмування також це забезпечує.

Щодо п 2. Він єдиний стримував мене від написання даного посту. Я думав, що альтернатив йому не існує. А вчора виявив, що по даному пункту уже давно піднято проблему Subtyping-Subclassing. Ось як я її формулюю:

Наслідування класів не еквівалентно створенню підтипів. Або, іншими словами, об’єкт наслідуваного класу (цебто підтипу) не завжди поводить себе у програмах, які очікують на вході об’єкт батьківсього класу (цебто типу), коректно.

Це здається маленькою проблемою (або не здається проблемою взагалі!), проте коли хтось думає про субклас як про субтип, то він здійснює логічну помилку. Архітектура проекту, яка виконується при такій зіпсованій логіці, обо’язково перетвориться у монструозну мішанину змістів і нам знадобляться паттерни, обмеження на гнучкість, обмеження на розширення і тд і тп. Причому шанс цього збільшується при розростанні проекту.

Про проблему я прочитав у Олега. Думаю, вам також корисно буде це зробити, якщо досі вважаєте підклас підтипом. З моєї точки зору, вся ця махінація з класами є не більш ніж синтаксичним екстрактом з цукрових буряків, точно така сама як динамічна типізація або створення змінних у будь-якому місці, а не тільки у заголовку. А ще хорошим прикладом того, як здавалось би логічні речі (представлення навколишнього світу у вигляді ієрархії об’єктів) є насправді не завжди логічним з точки зору математики.

Peace!

Written by danbst

Січень 18, 2012 at 18:23

Оприлюднено в Програмування

Відповідей: 15

Subscribe to comments with RSS.

  1. Щось останнім часом всі так люблять на ООП наїхати… Чого б це?

    bunyk

    Січень 18, 2012 at 20:31

    • Тому що у нього є недоліки, очевидно. Інакше кажучи, ООП — не панацея.

      і так, критикують ООП вже давно і досить успішно, зовсім ніяк не «останнім часом»

      ulidtko

      Січень 18, 2012 at 21:43

    • Ну, весь цей пост є префасом до статті Олега Кисельова, лінк в вверху. Він сформулював це як “one problem with OOP”, а не наїзд на всю парадигму.

      danbst

      Січень 19, 2012 at 10:04

  2. Динамічна типізація — це не синтаксичний цукор.

    ulidtko

    Січень 18, 2012 at 21:41

    • чому?

      Я думаю так: у більшості випадків змінній можна співставити тип. У тих випадках, коли цього робити не можна, потрібно зробити розділення змінної на 2 або більше і зробити відповідні заміни в коді.

      danbst

      Січень 19, 2012 at 09:56

      • Тому що поняття «синтаксичного цукру» передбачає трансформації виключно в межах синтаксису. Скажімо, оператор import в Python можна назвати синтаксичним цукром: він «розцукрюється» у виклик вбудованого методу __builtins__.__import__. Цикл for можна теж назвати цукром для i = iter(x); while true: try: obj = i.next(); body(obj); except StopIteration: break. Велику частину синтаксиса Python можна, *не заглядаючи в семантику*, звести до лукапів атрибутів у словниках і викликів об’єктів-методів.

        А от далі нам уже потрібна саме семантика. Яка в тому числі регулює і питання типів. І яка каже, що всі лукапи будуть виконуватись динамічно, у рантаймі — звідси і динамічна типізація. Весь цукор на даному етапі вже давно знятий.

        Це схоже на скорочення у формулах формальної логіки: ніякої принципіальної ролі вони не грають, оскільки не змінюють інтерпретацію, і розкриваються завчасно перед інтерпретацією всієї формули. Виключно синтаксичні речі; проте писати з ними приємніше, «солодше». Тому власне і цукор.

        ulidtko

        Січень 20, 2012 at 07:32

        • Зрозумів твою думку, я справді некоректно виразився. Треба замінити “динамічна типізація” на якусь іншу аналогію…

          danbst

          Січень 20, 2012 at 11:40

  3. Я почав сумніватись у доцільності ООП практично одразу після того, як зміг більш-менш впевнено сказати собі, що розумію його. І от чому.
    – мене завжди злегка дратувала неформалізованість патернів, і якщо певний час я мирився з цим, то зараз все більше починаю думати, що їхня неформальність — це наслідок нечіткого і брудного формулювання якихось математичних принципів, яке зловила інтуїція програмістів (як, наприклад, у Scheme було з великою нехіттю введено set!, а як з’ясувалось дещо пізніше, елегатним він стає лише через теоркат та монади); зараз ніяк не можу позбавитись спокуси розглядати класи як невдале вираження монад, а інтерфейси — множини функцій;
    – ООП вдало описує лише певні предметні області, для багатьох інших вироджуючись у синтаксичний шлак. І це б влаштовувало б мене, якби були хоча б приблизні критерії, де межі його застосовності, але їх також не існує;
    – історична ретроспектива показує, чому ООП у свій час викликав такий ентузіазм: був Сі, були проблеми з розподілом пам’яті (я все більше переконуюсь, що хороший сішник — це людина, яка досконало володіє низькорівневими техніками memory management), загал сподівався, що саме ООП вирішить це проблема, але на прикладі С++ стало ясно (застосування unique_ptr/shared_ptr та інших допомагає, але класи тут лише форма вираження методу), що це не так, тоді як garbage collection і в процедурно-модульних мовах (php/python/perl) робить це дієво;
    – найкращі як на мене зразки ОО-мов (Java і Smalltalk) чомусь тяжіють до самоізоляції, до абстрагування від оточуючого середовища, створення свого власного із авторитарним ОО-підходом;

    Dmytro Sirenko

    Січень 19, 2012 at 23:15

    • Гм, останній пункт наштовхує на роздуми: можливо, ООП — це природний спосіб організації середовища виконання, а його відображення в мовах вихідного коду вторинне?

      Dmytro Sirenko

      Січень 19, 2012 at 23:36

      • Якщо інтуіція щось підказує, то це не обов’язково так і є. Можливо саме “функтори” є “природніми” способами відображення будь-чого, а не об’єкти; не множини “керують” світом, а категорії.

        Іншими словами, ООП в цьому випадку може бути зручним (мабуть, саме це мається на увазі під природнім), але не обов’язково правильним.

        danbst

        Січень 20, 2012 at 11:58

        • Так я ж це так і висловив, що це тільки інтуїтивний здогад.
          Щодо ООП, то я, здається, намацав практичну причину, чому воно хороше для організації рантайму:
          http://dmytrish.livejournal.com/32647.html
          Коротше кажучи, об’єкти дозволяють розподіляти пам’ять набагато зручніше. І саме лінійна пам’ять породжує проблеми, які загалом вирішує ООП. Хоч це ніяким чином не пояснює теоретичні причині розумності такої організації.

          Dmytro Sirenko

          Січень 27, 2012 at 08:22

        • А щодо посилань, спасибі, сподіваюсь, нарешті дійдуть до них руки.

          Dmytro Sirenko

          Січень 27, 2012 at 08:25

    • ulidtko

      Січень 20, 2012 at 09:59

      • плюс 1! На картинці видно, що не об’єктами мислить ООП-програміст, а хер його знає чим.

        danbst

        Січень 20, 2012 at 11:37

  4. Ще одна класична проблема ООП:
    ручка.писати(папір, текст) чи папір.писати(ручка, текст)?
    Чи може навіть текст.написатися(ручка, папір)?

    ulidtko

    Січень 20, 2012 at 07:46


Залишити відповідь

Заповніть поля нижче або авторизуйтесь клікнувши по іконці

Лого WordPress.com

Ви коментуєте, використовуючи свій обліковий запис WordPress.com. Log Out / Змінити )

Twitter picture

Ви коментуєте, використовуючи свій обліковий запис Twitter. Log Out / Змінити )

Facebook photo

Ви коментуєте, використовуючи свій обліковий запис Facebook. Log Out / Змінити )

Google+ photo

Ви коментуєте, використовуючи свій обліковий запис Google+. Log Out / Змінити )

З’єднання з %s

%d блогерам подобається це: