LINUX.ORG.RU

Проблема хрупкого базового класса

 ,


0

1

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

Есть такой пример: http://stackoverflow.com/questions/2921397/what-is-the-fragile-base-class-pro... Здесь имеется в виду, что при вызове Sub::m() x увеличится на единицу дважды? Но чем это отличается от любого другого языка? (я опущу разделение на h и cpp)

namespace Base {
  int x;
  void m() { x++; }
  void n() { x++; m(); }
}
#include "base.h"
namespace Sub {
  void m() { Base::n(); }
}

upd: простой тест показал, что проблема не в инкрементах (а ведь я хотел написать про рекурсию, но комметарий defect меня смутил, да и я подумал, что джава не настолько хренова, чтобы вызывать неабстрактные методы вот так вот).

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

★★★★★

Последнее исправление: korvin_ (всего исправлений: 1)

Здесь имеется в виду, что при вызове Sub::m() x увеличится на единицу дважды?

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

Но чем это отличается от любого другого языка?

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

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

а самой парадигмы ООП

С одной стороны имеем то же самое без всякого ООП: http://ideone.com/V3fVlo

С другой стороны объекты на замыканиях в SICP нее имеют такой проблемы, если только не создать ее явно, что опять же можно сделать на любом языке с любой парадигмой. Или это автоматически становится ООП?

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

Разработчик класса Base плохо спроектировал класс

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

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

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

vertexua ★★★★★
()

а в чем проблема-то ? что java эмулирует ООП и вы не понимаете принципа эмуляции ??

не волнуйтесь - этого никто не понимает..:) ОО - это вообще абстракция как и любая парадигма...Забей.

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

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

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

Или это автоматически становится ООП?

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

auto12884839
()

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

Ничего не мешает, хуже того это штатня ситуация при проектировании GUI - например когда два компонета отображают одно состояние: флажок в меню и какойнить чекбокс - тут даже пресловутый MVC в конце концов обретает костыль

class SomeModel {
  private fired = false;

  void setValue(V Val) {
    if(this.fired) {
      return;
    }
    this.value = val;
    this.fired = true;
    try {
      listeners.listen(new ModelEvent());
    } finally {
      this.fired = false;
    }
  }
}

как видишь суть трабла таже

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

почему эту проблему называют фундаментальной (как-то громковато звучит)

Потому что ещё на этапе проектирования проектировщик должен охватить всё дерево объектов. Это сложно.

и почему именно ООП?

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

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

В процедурном программировании есть функции, у которых есть аргументы и есть результаты

Как будто сложно зациклить функции, передав указатели друг на друга.

http://ideone.com/HvOzh9

http://ideone.com/PWSaMX

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

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

Как будто сложно зациклить функции, передав указатели друг на друга.

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

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

А что ты будешь делать, когда от твоего класса зависят мегабайты кода других классов?

А если от моей функции зависят мегабайты других функций?

korvin_ ★★★★★
() автор топика

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

anonymous
()

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

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

И очень редко — потому, что модель реально навязывает отношение родитель-потомок.

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

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

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

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

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

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

А если от моей функции зависят мегабайты других функций?

С функцией намного проще, я же только что объяснил. Для функции есть аргументы и результат. Глянь код ffmpeg/libav. Там постоянно происходят изменения функций. avcodec_alloc_context2, avcodec_alloc_context3 выглядит по-уродски конечно, но такие изменения обходятся без глобальных перестроек зависимого от них кода. В ООП если меняется базовый класс, меняются все наследники, а это больше одной функции, и всех их придётся менять. Вот тебе ООП наглядно. Если исходная кошка делается ГМО, то ГМО и все её потомки. Это приводит к печальке в тех местах где подразумевается использование не-ГМО дочерней кошки.

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

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

Один пример не показывает неадекватности всех замен. Например, что плохого в наследовании в рамках классов-типов, a-la haskell / mercurial / etc.?

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

Сам этот принцип — это своеобразный костыль к ООП

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

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

Например, что плохого в наследовании в рамках классов-типов, a-la haskell

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

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

а это больше одной функции, и всех их придётся менять

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

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

Это проблемы совсем другого рода. С ООП тоже можно было бы: vcodec2.alloc_context(), vcodec3.alloc_context() и т.п. - даже меньше нужно было бы править.

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

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

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

Это проблемы совсем другого рода. С ООП тоже можно было бы: vcodec2.alloc_context(), vcodec3.alloc_context() и т.п. - даже меньше нужно было бы править.

Это только кажется так. alloc_context одна функция, а class vcodec2 это много функций и данных, особенно если считать все его дочерние тоже.

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

делают по нему паттерн-матчинг?

Не совсем та степь. Класс - подмножество типов, которое задается тем, что на нем определены «классовые» (==, compare, etc.) операции.

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

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

То есть, если я правильно помню кресты, «методы класса» - виртульны. + нет внутреннего состояния => предсказуемость вроде на высоте.

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

Ой, тема наследование не совсем расрыта:

Есть Class Eq (в нем определена (==)), есть Ord (определена операция сравнения, которая может быть задана при инстанцировании, но умеет реализации по умолчанию через (==) и заданную в экземпляре (<=)). Он «наследуется» от Eq в том смысле, что задает то подмножество типов, которое в свою очередь находится внутри Eq. (наследование в смысле ограничения, включенности).

В каком-то модуле определен тип, мы хотим сделать его экземпляром Ord, для этого сначала нужно определить экземпляр Eq. А интерфейсы классов доступны снаружи и связывают реализаци Ord и Eq (при сравнении над Оrd могут дернуть (==) для Eq). Код же в одном месте.

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

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

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

Miguel ★★★★★
()

Фундаментальная проблема программирования: код может быть говном.

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

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

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

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

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

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

Это только кажется так. alloc_context одна функция, а class vcodec2 это много функций и данных, особенно если считать все его дочерние тоже.

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

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

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

везде заменить.

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

Я вообще не пойму, о чем тут плачутся, ну поменяли что-то в структуре программы, ну понятно, что код нужно дописать/переписать. Код сам не пишется? Ай-ай-ай, как страшно жить.

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

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

Тут вообще много факторов конечно, но валить все на некую ущербность языка или парадигмы могут только ущербные личности. Разруха - она в головах.(c)

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

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

Кажется. Если внимательно посмотришь, версии alloc_context отличаются количеством аргументов. Это то, что является аналогом интерфейса класса. Если бы alloc_context была членом класса, это повлияло бы на все дочерние классы.

auto12884839
()

Надуманная проблема теоретиков.

Нет тут никакого фундаментального недостатка, вопрос исключительно в нормальном проектировании.

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

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

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

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

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

Так проектируют только мудаки

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

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

anonymous
()

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

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

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

версии alloc_context отличаются количеством аргументов

Если бы alloc_context была членом класса, это повлияло бы на все дочерние классы

Что мешало бы добавить alloc_context2, 3 ... 100500 в базовый класс контекста? Вообще, чем структура+функции отличается от класс+методы? Если вы хотите сказать про дочерние классы, то ненадо, т.к. добавление новых методов в базовый класс на дочерние никак не влияет.

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

Что мешало бы добавить alloc_context2, 3 ... 100500 в базовый класс контекста? Вообще, чем структура+функции отличается от класс+методы? Если вы хотите сказать про дочерние классы, то ненадо, т.к. добавление новых методов в базовый класс на дочерние никак не влияет.

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

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

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

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

Все эти действия не имеют аналогов в процедурном программировании

Вот именно. Поэтому, обсуждаемая здесь «фундаментальная проблема» ООП - это ничто по сравнению с адом процедурного программирования.

no-such-file ★★★★★
()

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

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

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

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

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

Так это проблема любой парадигмы. Более того - в ООП эта проблема стоит наименее остро, т.к. для ее решения ООП и было придумано.

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

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

?

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

Вообще я немного пошутил и утрировал. В сферическом классическом ООП есть контракты (я не шибко подкован)?

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