LINUX.ORG.RU

Зачем нужно ООП

 


2

3

Далёк я буду от правды если скажу, что единственная причина появления ООП - нельзя было сказать draw(circle) и draw(rectange) в одной программе (где rectange и circle - переменные с различными структурами)?

★★★★★
Ответ на: комментарий от anonymous

Так каждый из методов - тоже объект, со своими методами(по крайней мере <call>) и т.д. Так?

<call> это синтаксическая конструкция. То, что внутри call - превратится в код (байт-код, нативный - не важно). То что снаружи call - это декларация контекста. Функцией это становится когда создается экземпляр контекста. Вызов функции - сохранение контекста в стеке и передача управления на cоответствующий код.

no-dashi ★★★★★
()

клуб сомневающихся тружеников мозга

anonymous
()
Ответ на: комментарий от no-dashi

Что-то как-то перлом и шеллом потянуло.

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

потянуло

отвернись. какие проблемы. не отвечай на это сообщение.

pef-secure
()
Ответ на: комментарий от iZEN

Одна из ключевых концепций ООП — наследование — распадается

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

pef-secure
()
Ответ на: комментарий от iZEN

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

Ну почему же? CLOS я уже упомянул. там проблема правильных классов стоит не так жёстко. Ещё есть обобщённые функции без классов на типах-предикатах, например fast-generics, GLS. Там вообще всё хорошо: круг при изменении масштаба становится эллипсом, так как перестаёт удовлетворять предикату для круга.

monk ★★★★★
()
Ответ на: комментарий от anonymous

А вот отнаследоваться от абстрактного класса в racket нельзя.

При большом желании можно

(module interface racket
  (provide foo bar)
  (define (foo x y) (error "Not implemented"))
  (define (bar x y z) (error "Not implemented")))

(module implementation racket
  (provide foo bar)
  (define (foo x y) ...)
  (define (bar x y z) ...))

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

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

именно это (возможность «уточнить» зависимости «задним числом», в потомке) и есть основная фишка

Вот это не понял. Можно пример?

monk ★★★★★
()
Ответ на: комментарий от no-dashi

А почему функция не объект?

Это недоработка языка. Вот в яваскрипте даже null объект.

Vit ★★★★★
()
Ответ на: комментарий от monk

Вот это не понял. Можно пример?

Определяем абстрактный метод в потомке, собственно.

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

Ну у нас тогда получается сделать инстанс интерфейса :)

когда нужно выбирать модуль в зависимости от каких-либо условий.

А я что сказал? :)

anonymous
()
Ответ на: комментарий от anonymous

Да, согласен. Хотя мне почти всегда хватало тех возможностей наследования, что дают модули.

monk ★★★★★
()
Ответ на: комментарий от anonymous

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

Фактически, есть 5 уровней:

1) Типы известны. Используются только модули

2) Типы зависят от внешних условий (например, UI может быть text или gtk) — юниты.

3) В одной коллекции объекты разных типов, но с одинаковым API — классы (racket/class)

4) Взаимодействие нескольких коллекций с объектами разных типов. Функция определяется типами нескольких аргументов — CLOS и аналоги.

5) API объектов может меняться после создания объекта — объекты на прототипах (как в JS).

Начиная с п. 3 появляются дополнительные расходы на каждый вызов метода. С увеличением пункта эти расходы растут.

monk ★★★★★
()
Последнее исправление: monk (всего исправлений: 1)
Ответ на: комментарий от no-dashi

Это ты про реализацию сейчас. Что делаем с семантикой-то? <call> - метод. Метод - это объект(как минимум с методом <call>. Выхода отсюда два - или call не метод или метод - не объект(как в твоих примерах на крестах).

anonymous
()
Ответ на: комментарий от jtootf

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

jtootf ★★★★★
()
Ответ на: комментарий от anonymous

Что делаем с семантикой-то? <call> - метод

Что ткое ";" в С/C++/Java/C#/Pascal? Это синтаксическая конструкция, разделитель операторов. Также и <call> - это синтаксическая конструкция, объясняющая компилятору где условно начинается код и стековые переменные, а где контекстные переменные. Как ";" в каком-нибудь C или C# - просто разграничитель операторов.

no-dashi ★★★★★
()
Ответ на: комментарий от no-dashi

т.е. <call> - не метод? Или метод, но не объект? Напомню, в нашем контексте все методы являются объектами.

anonymous
()
Ответ на: комментарий от qulinxao

наследование не обязано упрощать - надежда на экономии от повторного использования

наследование нужно для обобщения, оно, в первую очередь, означает родство типов и обеспечивает контракт, скрывая детали реализации. Яркие примеры: Stream, Form, List

anonymous
()
Ответ на: комментарий от anonymous

означает родство типов и обеспечивает контракт

Яркие примеры: Stream, Form, List

Ага, Runnable, Enumerate, файлы в UNIX. Для контракта нужен интерфейс. Наследование скорее мешает.

monk ★★★★★
()

ООП - просто еще 1 метод декомпозиции кода.

anonymous
()

Зачем нужно ООП

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

anonymous
()
Ответ на: комментарий от monk

Файлы - хороший пример, допустим есть интерфейс IFile у него есть что-то типа Open, Read, Lock и тп. Но содержимое у файлов разное, и вот ООП позволяет реализовать этот контракт один раз в классе File и наследовать свои FileFormat1, FileFormat2 от него не реализовывая каждый раз контракт IFile. Ну и конечно ООП не панацея, современные языки все больше «мультипарадигменны».

anonymous
()
Ответ на: комментарий от anonymous

Файлы - хороший пример, допустим есть интерфейс IFile

Интерфейсы и классы — скорее противоположны по смыслу. Интерфейсы нужны, классы (почти всегда) вредны.

и вот ООП позволяет реализовать этот контракт один раз в классе

Это только в C++ (и его потомках) модули являются подвидом ООП. И API в случае модуля просто в документации. В случае Haskell есть классы типов — они как раз интерфейсы.

А дальше FileFormat1::Open(...), FileFormat2::Open(...) и т.д.

ООП было бы, если бы у тебя реализация open для каталога вдруг использовала бы реализацию open для файла (и так далее).

monk ★★★★★
()
Ответ на: комментарий от anonymous

но это вкусовщина чистой воды.

Это миллионы человеколет практики.

anonymous
()
Ответ на: комментарий от monk

классы (почти всегда) вредны.

С наследование классов нужно быть осторожнее, а так, в общем-то, чем они вредны?

anonymous
()

Далёк я буду от правды если скажу

нет, будешь недалёк. это будет ADT с полиморфизмом и наследованием. подробности Б. Мейер «Основы объектно-ориентированного программирования», а также "... конструирования" и «Почувствуй класс с Эйфель».

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

это позволяет Мейеру заявлять о том, что существует «объектный метод проектирования и разработки». то есть, он с точки зрения системной инженерии, декомпозиции систем на функцию-конструкцию-назначение,композицию,распределение ответственности,сборку — применяет её к программной инженерии (man институт ISE Eiffel и модель CMM).

в которой система это язык-метод и инструменты, утилиты-библиотеки(и/или, фреймворки). в поделках типа С++ или Java эти элементы мозаики системы вырастали исторически — от языка или от библиотек.

Мейер же говорит, что метод — ООП метод, или метод BON (business object notation) главнее. а язык — это DSL в поддержку метода, а библиотека — это развитие языка другим DSL. а фреймворки хрень ибо вводят свои DSL. нужны не фреймворки с костылеподобным DSL препроцессором и макросами и поделками типа moc, — а нормальные библиотеки, языки, и DSL-и, с нормальными семантиками.

тогда метод может быть reversible и seamless, что есть круто. один метод to rule em all, and in the darkness bind them ;-)

anonymous
()
Ответ на: комментарий от anonymous

а суть этого всего — в некоторой годной семантике метода, и в модульности, интегрированной с системой типов (класс это модуль) и в итоге — в облегчении code reuse, повторной используемости.

anonymous
()
Ответ на: комментарий от monk

Это только в C++ (и его потомках) модули являются подвидом ООП. И API в случае модуля просто в документации.

ну Мейер в манускриптах явно говорит, что класс — это одновременно и тип, и модуль. в С++ как раз модульность хреновая, ибо пространство имён на модуль 1:1 не отображается (оно 1:N)

anonymous
()
Ответ на: комментарий от anonymous

общем-то, чем они вредны?

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

no-such-file ★★★★★
()
Последнее исправление: no-such-file (всего исправлений: 1)
Ответ на: комментарий от anonymous

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

наследование — слишком жёсткий тип связи, и маловато контроля. а в аггрегации вместо наследования — полный контроль, и ТВОЙ тип связи. так что более гибко — поэтому и лепят в С++ фреймвори и паттерны костыли типа этого и типа того же pimpl.

anonymous
()
Ответ на: комментарий от no-such-file

но для моделирования объектов предметной области (пример философов с хабра) не очень подходит.

хотя какие-то странные ему аналитики попались, с Аристотелевой логикой: «В какой парадигме ... ?» => в объектно-ориентированной же!

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

сюда же всякие rule-based term rewriting engine, которые на ООП (и наследование) не очень хорошо ложатся.

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

anonymous
()
Ответ на: комментарий от anonymous

а так, в общем-то, чем они вредны

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

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

monk ★★★★★
()
Ответ на: комментарий от anonymous

rule-based term rewriting engine, которые на ООП (и наследование) не очень хорошо ложатся

RTFM про visitor pattern

anonymous
()
Ответ на: комментарий от monk

Семантикой. Если для интерфейса характерно утверждение «имеет свойства того-то», то для класса «является тем-то».

С этим трудно не согласиться. Но не понятно полное неприятие ООП. По крайней мере у меня вот так. 1) Есть большая задача, которую не понятно, как решать. 2) Изучаешь предметную область, бьешь большую задачу на мелкие блоки, блоки оформляешь, как классы. 3) Очевидно, что хочется модули с низкой связанностью между собой. Для возможности тестов и изменения внутренней реализации. Т.е. оформляем публичные методы/поля и внутренние. 4) Если очевиден общий алгоритм разных блоков кода (классов), то можно использовать полиморфизм 5) В каких-то случаях наследование для переиспользования кода (можно и множественное) 6) Быстрые фиксы, если унаследовал старый класс и поменял реализацию каких-то методов (да лапша детектед, но скрестили пальцы, что порефакторим) 7) Лямбды и динамические типы массово стали появляться в ООП языках, некоторые вещи стали быстрее и лаконичнее. Как мне кажется для С++ и С#. А как в других? Как например программят большие вещи в ФП языках без ООП?

anonymous
()
Ответ на: комментарий от anonymous

Как например программят большие вещи в ФП языках без ООП?

Я могу сказать за Racket.

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

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

Переиспользование возможно просто по аналогии. Если «собака — почти как кошка, но лает, а не мяукает», то

; cat.rkt
(provide walk run meow (struct-out cat))
...

; dog.rkt
#lang racket

(require (prefix-in cat: cat))
(provide walk run bark (struct-out dog))

(define (walk dog)
  (cat:walk (present-dog-as-cat dog)))
...

Таким образом имеем общий алгоритм реализации, но без паразитического наследования «собака — это кошка» (не во всех контекстах справедливо).

monk ★★★★★
()
Ответ на: комментарий от anonymous

А как в других? Как например программят большие вещи в ФП языках без ООП?

а что означают например хаскелевские «type class», классы типов чего именно?

я б сказал: разные виды полиморфизмов. одним из которых является объектный полиморфизм : в смысле, полиморфизм наследования классов.

Семантикой. Если для интерфейса характерно утверждение «имеет свойства того-то», то для класса «является тем-то».

С этим трудно не согласиться. Но не понятно полное неприятие ООП.

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

эта логика и описывает онтологию «Х имеет свойства Y», «класс Y является тем-то Y».

а хотят описать «гипермножество X является качеством Z для гипермножеством Y». естественно, описать гипермножество множеством, логикой множеств — ведёт к парадоксам теории множеств. а к парадоксам теории гипермножеств, или их подклассов: объектов  — не ведёт.

критикуют «универсальную теорию ООП» когда в системе типов — появляется дырка.

anonymous
()

нельзя было сказать draw(circle) и draw(rectange) в одной программе (где rectange и circle - переменные с различными структурами)?

А когда это нельзя был сказать? В си том же для этого были указатели на функции.

anonymous
()
Ответ на: комментарий от anonymous

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

anonymous
()
Ответ на: комментарий от anonymous

легко.

читаю например lkmpg, смотрю пример с хелловордом.

там для 2.6, но не суть.

init_module(...) — это конструктор модуля ядра
cleanup_module(...) — деструктор

пока не совсем ООП — скорее, похоже на модульное программирование в том же Oberon-2/Modula-2.

затем, появляется инкапсуляция — статические методы для неэкспортированных, и /proc/kallsyms для символьной таблицы ядра.

major, minor для версионирования.

затем, появляется наследование и полиморфизм наследования в API уровня ядра.

вот как раз пример объектно-ориентированного API Linux kernel: глава 4.1.1. #include <linux/fs.h>

struct file_operations {
   struct module *owner; /** это this, self, Current -- ссылка на сам объект в ООП */
 loff_t (*llseek) (struct file *, loff_t, int); /** это метод llseek
// ...
 int (*open) (struct inode *, struct file *); /** это конструктор объекта. берёт 1 параметр и возвращает 2 значения. */
 int (*release) (stuct inode *, struct file *); /** а это деструктор, один из параметров -- ссылка на self (this) */
//...
}
/** далее это же поподробнее: */
struct file_operations fops = {
  .read=device_read,
  .write=device_write,
  .open=device_open,
  .release=device_release
};

/** это инициализация инстанса объекта */

глава 4.1.6 написание модулей для нескольких версий ядра — это полиморфизм и наследование, в одном флаконе.

далее, глава 5 , модуль для procfs. явно выделены конструктор, деструктор, методы модуля. example 5-2. static методы и структуры — инкапсуляция.

anonymous
()
Ответ на: комментарий от anonymous

10.1 replacing printk — полиморфизм наследования с перекрытием метода.

anonymous
()
Ответ на: комментарий от anonymous

. Тут нет ни объектов,

в рантайме

ни классов,

в компайлтайме и системе типов

ни методов, ни сообщений..

есть. вот полиморфизм наследования и диспетчеризация костылями — но не суть.

возьми например, на Vala хелловорд на GTK окошко с кнопкой . собери с ключом -C , и посмотри на сгенерированый C.

anonymous
()
Ответ на: комментарий от anonymous

При чем тут GTK? При чем тут Vala или даже Си? В приведенном примере кода нет ООП, есть набор указателей на функции. Я сам так пишу, когда мне удобно иметь такие наборы, но ООП не люблю. Тут даже к тайпклассам haskell и то ближе, чем к ООП.

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.