LINUX.ORG.RU

[philosophy] В чем заключается революционность перехода от функциональщины к ООП?


1

0

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

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

Почему появились языки, которые взяли ООП за главенствующую идею (java, c#, етц)?

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

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

Было бы интересно без срачей услышать компетентное мнение.

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

Ну вон ты завел переменную user :)
А так у меня один объект, а там много полей: user, APIKey, еще что-то. Для каждого change заводить новую переменную? Так получается эмуляция базисного ООП :)

Можешь объяснить в чем разница между compile-time ad-hoc и runtime ad-hoc, и где что использовать?

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

Тут согласен, особенно с последним пунктом. Почему популярность получила именно жаба, а не CL + CLOS или Smalltalk?

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

>Почему популярность получила именно жаба, а не CL + CLOS или Smalltalk?

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

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

>В Хаскеле есть всё.

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

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

А так у меня один объект, а там много полей: user, APIKey, еще что-то. Для каждого change заводить новую переменную?

google://C+struct

Можешь объяснить в чем разница между compile-time ad-hoc и runtime ad-hoc, и где что использовать?

Что значит «полиморфизм»? Что мы одинаково работаем с объектами РАЗНЫХ типов. В частности, вызывая одну и ту же функцию (= метод).

Что такое «параметрический полиморфизм»? Это значит, что при вызове одной и той же функции с разными типами объектов код выполняется одинаковый. Скажем - при подсчёте длины массива нам наплевать, какого типа объекты в нём лежат. Какие бы ни были.

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

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

Хаскельные классы производят такой выбор на стадии компиляции. Гарантированно.

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

Почему популярность получила именно жаба, а не CL + CLOS или Smalltalk?

Гы. Правильно сделала. Во-1, статическая типизация очень сильно помогает, во-2, в нынешней жабе есть хоть и ущербный, но всё же параметрический полиморфизм (в CL/Smalltalk невозможный, опять же, из-за динамической типизации).

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

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

Да. А что?

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

> google://C+struct
Ну, «эмуляция» ООП получается, обрезанная жутко.

Хаскельные классы производят такой выбор на стадии компиляции. Гарантированно.


А если тип объекта узнается в рантайме?

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

> (в CL/Smalltalk невозможный, опять же, из-за динамической типизации).
Ну так он и не нужен там, получается?

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

Ну, «эмуляция» ООП получается, обрезанная жутко.

Эмуляция ООП начнётся, когда у тебя будут объекты разных типов.

А если тип объекта узнается в рантайме?

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

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

Ну так он и не нужен там, получается?

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

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

Можешь про замыкания рассказать, пожалуйста?

А может, тебе почитать что-нибудь?

В применении к данному случаю. Вот на C++:

class Base {
public:
  virtual void methodA(int arg) = 0;
  virtual int methodB() = 0;
};
class DerivedU : public Base {
private:
  int something;
public:
  virtual void methodA(int arg) {
    something = arg;
  }
  virtual int methodB() {
    return something;
  }
};
class DerivedV : public Base {
private:
  int sum;
public:
  DerivedV() : sum(0) {}
  virtual void methodA(int arg) {
    sum += arg*arg;
  }
  virtual int methodB() {
    return sum/2;
  }
};
Base *object = new DerivedV;

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

struct Base {
  void(int arg) *methodA;
  int() *methodB;
};
Base *derivedU() {
  int something;
  void m_A(int arg) {
    something = arg;
  }
  int m_B() {
    return something;
  }
  Base *b = new Base;
  b->methodA = mA;
  b->methodB = mB;
  return b;
}
Base *derivedV() {
  int sum = 0;
  void mA(int arg) {
    sum += arg*arg;
  }
  int mB() {
    return sum/2;
  }
  Base *b = new Base;
  b->methodA = mA;
  b->methodB = mB;
  return b;
}
Base *object = derivedV();
Писанины больше, так как я старался сохранить синтаксис плюсов.

То есть, локальные функции mA и mB «замыкают» переменную sum, она будет жить и после выхода из функции derivedV. За этим проследит сборщик мусора. Когда же object будет выкинут, выкинеться и «замкнутая» sum. При следующем вызове derivedV будет создана новая переменная sum, никак не связанная со старой, и новые функции mA и mB замкнут уже её.

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

Я имеею в виду, почему это лучше/хуже compile-time ad-hoc?

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

> Что твой подход медленнее? Сравни объём кода.

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

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


А в какой момент обнаружится что ты подсунул _не_ту_ бумажку? Рантайм? Бида-бида, что же тебе не помогли статические проверки?

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

Если заранее известно, что отображать ее не надо, и что не надо различать разных писателей (и вообще факт существования писателя не важен), то можно сделать как предложил Love5an: http://www.linux.org.ru/jump-message.jsp?msgid=5034750&cid=5035977 [code]писать(бумага, ручка, текст)[/code]. Все трое выступают в пассивной роли.

Ну так с чего тебе вдруг стало известно, что взятие карандаша, блокнота и написание текста - одно большой атомарное действие?


Сделал анализ предметной области.


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

А если в твоей предметной области оно атомарно, то почему ты с самого начала об этом (не слишком-то естественном) ограничении не сообщил? Потому, что допридумал ограничение по ходу беседы?

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

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

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

А в какой момент обнаружится что ты подсунул _не_ту_ бумажку? Рантайм? Бида-бида, что же тебе не помогли статические проверки?

Ну да, а то, что всех микробов не смоешь, повод не мыться вообще.

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

Ручкой.

Кто там про анализ предметной области говорил?

можно сделать как предложил Love5an:

Ну да. И понять, что к ООП это отношения, считай, не имеет. Чистая процедурщина.

А если в твоей предметной области оно атомарно, то почему ты с самого начала об этом (не слишком-то естественном) ограничении не сообщил? Потому, что допридумал ограничение по ходу беседы?

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

Miguel ★★★★★
()

>Неужели те преимущества, которые предлагает ООП (полиморфизм, инкапсуляция, наследование), дают прирост в эффективности, скорости написания программ, понимания их работы и поддержке?

да

А неужели это так меняет дело и делает разработку более удобной?


да

JFreeM ★★★☆
()
Ответ на: комментарий от SV0L0CH

>Почему популярность получила именно жаба

низкий порог вхождения.

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


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

JFreeM ★★★☆
()
Ответ на: комментарий от Miguel

> Ручкой.

Ты никогда не видел надписей на стенах/партах/подоконниках ручкой?

Ну да. И понять, что к ООП это отношения, считай, не имеет. Чистая процедурщина.


С чего бы это? Обоснуй.

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


За то не-атомарность подразумевает предметная область, которую ты выбрал.

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

С чего бы это? Обоснуй.

А какие там признаки ООП?

За то не-атомарность подразумевает предметная область, которую ты выбрал.

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

Miguel ★★★★★
()
Ответ на: комментарий от cvs-255

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

Только это к OOD относится, а не к OOP.

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

>Я хоть лисп не очень хорошо знаю, но подозреваю что это делал бы на шаблонах. И по-моему нечто подобное даже есть в STL.

Повторяю, работающее для произвольного числа контейнеров =)

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

>> Напишешь на плюсах аналог лиспового #'mapcar?

Ну, вообще-то, написать, я думаю, вполне можно, даже с нуля.

Я и не говорю, что нельзя =)

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

>>Я хоть лисп не очень хорошо знаю, но подозреваю что это делал бы на шаблонах. И по-моему нечто подобное даже есть в STL.

Повторяю, работающее для произвольного числа контейнеров =)

Переменное число аргументов? А зачем?

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

в первом приблежении мне виден только один вариант - рассмотреть K частных вариантов для количества аргументов передаваемого функтора от 1 до K (где K - не сильно большое число). Так сделано у Александреску, например, в его реализации Command из Loki.

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

Ну, плюсы умеют (хотя и плохо) работать с переменным числом аргументов.

Хотя засада будет: собрать аргументы в массив плюсы могут, а подставить аргументы из массива - вроде нет.

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

>рассмотреть K частных вариантов для количества аргументов передаваемого функтора от 1 до K

Что-то очень знакомое, в Prelude есть несколько функций, в которых количество аргументов записано в самом имени. Но это наследие Haskell98 в котором есчё не появились шаблоны.

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

Но это наследие Haskell98 в котором есчё не появились шаблоны.

Нет, это наследие тех времён, когда не было аппликативных функторов.

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

> С чего ты взял? Анализ делал я.

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

А какие там признаки ООП?


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

Важно количество сущностей и количество элементарных операций.


Хех. Хеллоуворлд от попыток изолировать сущности друг от друга и вместо каши выделить какую-то структуру только усложнится. Это как бы очевидно.

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

>> Но это наследие Haskell98 в котором есчё не появились шаблоны.

Нет, это наследие тех времён, когда не было аппликативных функторов.

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

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

Write() не знает о бумаге, ручке и тексте ничего, кроме их публичных интерфейсов.

Во-1, это ниоткуда не следует.

Во-2, это признак не ООП, а хорошей модульности.

Хеллоуворлд от попыток изолировать сущности друг от друга и вместо каши выделить какую-то структуру только усложнится.

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

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

Лучше пусть монады соорудят на Си с таким же удобным синтаксисом. Для начала можно без нотации do. Это будет уже ближе к функциональному стилю :)

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

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

ZipList называется.

f :: Int -> Int -> Int
f x y z = x * y + z
zipList3 f [1,2,3] [4,5,6] [7,8,9] -- [11, 18, 27]
getZipList $ f <$> ZipList [1,2,3] <*> ZipList [4,5,6] <*> ZipList [7,8,9] -- [11, 18, 27]
Miguel ★★★★★
()
Ответ на: комментарий от dave

>Лучше пусть монады соорудят на Си с таким же удобным синтаксисом. Для начала можно без нотации do. Это будет уже ближе к функциональному стилю :)

На плюсах где-то было, погугли...

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

>Язык Си располагает к написанию программ как последовательности пошаговых инструкций

Мосье ратует за декларативщину? А какие языки ее поощряют? На ум приходит только Prolog. SQL полноценным языком назвать сложно, а PL/SQL по степени декларативности вполне сравним хоть с C, хоть с асмом.

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

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

наследование и виртуальные методы - основа создания гуёвых интерфейсов

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

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

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

это признак не ООП, а хорошей модульности


Ох, объекты - это не признак ОО подхода? Инкапсуляция - тоже не признак ОО подхода? Ну, как скажешь...

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

Вырождение? Я сам сталкивался с случаем, когда переход от C++ и ООП к чистому C давал 100 кратное ускорение при совершенно том же алгоритме. Думаю что я не один сталкивался с такими случаями. Т.е. до вырождения далеко.

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

> Любое. Если ты про бесконечное - сделай рекурсию.

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

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

> Любое. Если ты про бесконечное - сделай рекурсию.

man несчётное множество

cvs-255 ★★★★★
()
Ответ на: комментарий от oami

> Но интересно, как выглядит рекурсия для несчетного множества.

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

Manhunt ★★★★★
()
Ответ на: комментарий от cvs-255

>Я сам сталкивался с случаем, когда переход от C++ и ООП к чистому C давал 100 кратное ускорение при совершенно том же алгоритме.

Трололо-трололо, а я то дурак всегда думал, что C++ - это почти также быстро, как и С. Ну давай сюда свой пример, заценим дружно.

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

>наследование и виртуальные методы - основа создания гуёвых интерфейсов

Как бы я сам то согласен, но вот gtk'шники успешно извращаются.

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

наследование и виртуальные методы - основа создания гуёвых интерфейсов

покажи мне наследование и виртуальные методы в Tcl/Tk, REBOL GUI, XPCE или QML

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

>Но интересно, как выглядит рекурсия для несчетного множества.

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

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

> Ну давай сюда свой пример, заценим дружно.

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

struct IPixel
{
  virtual ~IPixel() {}

  virtual int GetR() = 0;
  virtual int GetG() = 0;
  virtual int GetB() = 0;

  virtual void SetR(int) = 0;
  virtual void SetG(int) = 0;
  virtual void SetB(int) = 0;
};
И дальше возня с битмапами из таких «пикселов».

Дай дураку стекляный *уй, он и лоб себе разобьет, и руки порежет :D

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