S>> Забавная штука, но практической ценности не имеющая. Это означает отсутствие какой бы то ни было классификации, поскольку любой объект потенциально обладает поведением любого другого объекта, стало быть по его типу нельзя делать никаких выводов о его поведении. Примером такой системы может считаться JavaScript. Не имею ничего против подобных моделей, но лично предпочитаю иметь в запасе несколько более декларативно выразительные средства разработки. DG>Удобнее смешивать статическую и динамическую типизацию, и на практике, динамическая типизация используется очень часто. DG>Взять тот же графический редактор. Без динамической типизации редактор просто не написать.
А вот здесь согласен Действительно динамическая типизация удобнее. Не надо мозг в трубочку заворачивать при проектировании объектной модели.
DG>>>Кто сказал, что ООП должна быть статической? S>>Да никто не сказал. Пожалуйста — вот тебе JavaScript.
А кто сказал что ООП должно быть императивным? Да здравствуют динамически типизированные функциональные ООП языки — долой прокрустово ложе принципа Лисков и деструктивных операций! Сбросим, так сказать, цепи!
DG>>>Статическая типизация нам только дает только одно преимущество, по сравнению с динамической. Основным преимуществом статической типизации является то, что мы заранее (на этапе компиляции) можем проверить правильность выражений. DG>>>Всё. Больше преимуществ нет. S>>Ну как тебе сказать. Вообще говоря, помимо этого можно эффективно выполнять некоторые оптимизации. DG>Оптимизации можно делать и при динамической типизации, например, используя тот же lazy-подход.
Что-то мне этот разговор сильно напоминает Оптимизации на порядок проще делать при наличии статической типизации. Здесь спорить не о чем. It's fuckt.
Re[20]: Задачка про Circle/Ellipse: "официальный" ответ.
DG>>Почему всё? Я же четко определил, что для фигуры определены 3 операции. S>Мало ли что ты определил. А для строк определены еще пять. Поскольку у тебя неизвестно, означает ли строка "треугольник" фигуру, придется всем реализовывать по восемь. Или ты заплатки приделаешь? Типа вот тут была строгая типизация, а теперь не совсем?
Но в VisualBasic-е именно так и сделано.
Хотим работаем через статическую типизацию, хотим — работаем через динамическую.
DG>>А дальше идет нечеткая логика: DG>>для большинства (но не для всех) фигур — эти операции выполняются корректно. S>Угу. А почему бы вероятность корректного выполнения не ввести? Чтобы уж до конца по нечеткой логике пройти? Иначе ценность этого утверждения равна нулю. Я бы, как работодатель, на утверждение "моя программа в некоторых (но не во всех) случаях делает что-то осмысленное", сказал бы "ну, в некоторых (не во всех) случаях я заплачу" .
Может быть где-нибудь в вакууме программа и работает всегда и на всех данных.
Но на практике, среднестатичная программа работает без ошибок на среднестатичном железе в среднестатичной ОС при использовании среднестатичного ПО при обработке среднестатичных данных и т.д. А шаг влево и вправо каряется gpf-ом.
Т.е. фактически, когда разработчик говорит "моя программа работает всегда" — это он просто забыл снял розовые очки.
DG>>например, по тому типу, которому присваивается результат. DG>>или, например, по настройкам в реестре или еще где-то. S>Я рад. Отладка такой программы будет представлять собой сущее удовольствие. Особенно меня интересует процесс введения новых контекстов и обеспечение их миграции в распределенной среде (ну или хотя бы при инсталляции )
S>Ну вот примерно это я себе и представлял, когда ты говорил о контексте. А теперь скажи мне, чем это вдруг стало лучше старого доброго S>void Func() S>{ S> Out(int(2.0)/int(5.0); S>} S>? (Я, конечно, имею в виду совершенно обратный случай)
Тем, что контекст может быть определен сразу на всю программу, может быть определен косвенно, может быть определен конечный пользователем и т.д.
DG>>>>>>Что полезного (кроме громоздкости) нам дает такая модель? S>>>>>А то, что другого варианта корректной эмуляции поведения этих двух классов чисел в рамках ООП я не вижу.
DG>>Что значит корректной? S>А той, что привычные нам математические операции имеют привычную семантику. И умножение вектора на скаляр дает всегда вектор, а не только утром в понедельник. И некоторым комплексным числам соответствуют целые, а некоторым — нет. Поэтому преобразование из целых в комплексные мы можем себе позволить сделать неявно, а в обратную сторону — явно. А еще лучше — потребовать ловить исключение InvalidTypeCast на случай отсутствия соответствия каким-то явным образом, хоть
DG>>и что значит рамки ООП? S>А это как раз и значит, что поведение объектов детерминировано. DG>>Если мы "знаем" про тензоры и кватернионы, то это и второе, и третье. и четвертое. S>Зашибись. То есть от того, что я взял и в своем углу проекта кватернион заимплементил, ты должен там у себя весь код прошерстить на предмет потери семантики? Очень здорово.
нет, потому что контексты разные. В своем контексте я кватерионы не объявлял.
DG>>на практике используют lazy-подход. Такой подход часто применяется при работе с матрицами. DG>>При таком подходе операции реально выполняются только когда действительно нужен результат. S>А, ну это — по-нашему. То есть мы откладываем валидацию до тех приятных времен, когда она уже ничем не сможет нам помочь Я представляю счастье того, кто обнаружит неприводимость строки "дапошливывсе" к дате експирации кредитной карточки в webcommerce проекте. Его ждет незабываемый поиск возможного источника этого чуда, потому что отрепортят ему только локейшн чтения, а никак не записи.
А вот здесь уже статика и нужна, т.е. компилятор (не программист) видит, что объект будет использоваться в качестве даты, поэтому при присваивании значения необходимо проверить, есть ли преобразование к дате или нет.
Именно по такой схеме построены, например, шаблоны в C++.
Re[20]: Задачка про Circle/Ellipse: "официальный" ответ.
S>>>Ну как тебе сказать. Вообще говоря, помимо этого можно эффективно выполнять некоторые оптимизации. DG>>Оптимизации можно делать и при динамической типизации, например, используя тот же lazy-подход. G>Что-то мне этот разговор сильно напоминает Оптимизации на порядок проще делать при наличии статической типизации. Здесь спорить не о чем. It's fuckt.
Но код получается проще и чище, когда определением статической типизации занимается компилятор, а не программист.
Вопрос на засыпку:
Сколько интерфейсов необходимо определить, чтобы уметь работать с коллекциями?
Коллекции бывают: readonly/не readonly, с порядком, без порядка, с добавлением/без добавления, с удалением/без удаления, с последовательным доступом/индексным доступом/без доступа и т.д.
Сколько интерфейсов будет поддерживать, например, массив?
Как будет описываться, например, компонент, который сортирует коллекции? Какие интерфейсы он должне требовать в декларации?
Re[21]: Задачка про Circle/Ellipse: "официальный" ответ.
Здравствуйте, DarkGray, Вы писали:
S>>>>Ну как тебе сказать. Вообще говоря, помимо этого можно эффективно выполнять некоторые оптимизации. DG>>>Оптимизации можно делать и при динамической типизации, например, используя тот же lazy-подход. G>>Что-то мне этот разговор сильно напоминает Оптимизации на порядок проще делать при наличии статической типизации. Здесь спорить не о чем. It's fuckt.
DG>Но код получается проще и чище, когда определением статической типизации занимается компилятор, а не программист.
Да, конечно, все так. Это теоретически возможно. Скоро такие технологии дойдут до зрелых production-систем, лет эдак через 5-10. Я с нетерпением жду времени, когда динамически типизированные языки позволят писать очень быстрые программы. Но динамическая типизация хоть и облегчает дизайн, но не отменяет принцип Лисков.Наследование должно применяться по назначению, иначе поддержка системы (или итеративная разработка) превратится в ад кромешный. Наступишь на эти грабли сам на достаточно сложной системе — это станет очевидно.
Его отменяют функциональные языки. Интересно, когда научатся делать эффективный copy elimination для функциональных языков, ведь с этим все гораздо сложнее. И интереснее
DG>Вопрос на засыпку: DG>Сколько интерфейсов необходимо определить, чтобы уметь работать с коллекциями?
DG>Коллекции бывают: readonly/не readonly, с порядком, без порядка, с добавлением/без добавления, с удалением/без удаления, с последовательным доступом/индексным доступом/без доступа и т.д.
DG>Сколько интерфейсов будет поддерживать, например, массив?
DG>Как будет описываться, например, компонент, который сортирует коллекции? Какие интерфейсы он должне требовать в декларации?
Да, здесь при ООП дизайне в самом деле можно свернуть себе шею. Чтобы этого не произошло, в языке С++ нам помогут templates. И при этом типизация остается статической. Я думаю, ты это знаешь и без моих комментариев.
Re[19]: Задачка про Circle/Ellipse: "официальный" ответ.
Здравствуйте, DarkGray, Вы писали:
DG>>>Какое из ООП-правил нарушается для данного кода? G>>1) Этот код просто-напросто не работает. У тебя нигде не сохраняется промежуточный результат. У тебя только что созданный объект удаляется после каждой операции. Нехорошо. Нерабочий код за пример не канает.
DG>Результат сохраняется в самой фигуре.
Он у тебя не сохранялся. У тебя фигура удалялась сразу после изменения, Синклер тебе тоже на это указал. Ну да ладно, ты приготовил новый код.
G>>2) В реальном мире ты не всегда можешь менять реализацию компонента. Я же сказал — он уже написан и отлажен. G>>И написан он вот как:
skipped DG>например, вот так:
skipped
То, что ты написал, это абсолютно тоже самое, что и
class Figure : public IEllipse, ICircle
{
}
только выглядит страшнее. Но на самом деле ты так делать не можешь, потому как Ellipse тоже является частью нашего компонента, он уже написан. Трогать его нельзя, можно только наследоваться. Попробуй, и посмотри что получится. Вот _в_этом_ смысл принципа Лисков.
Re[22]: Задачка про Circle/Ellipse: "официальный" ответ.
DG>>Вопрос на засыпку: DG>>Сколько интерфейсов необходимо определить, чтобы уметь работать с коллекциями?
DG>>Коллекции бывают: readonly/не readonly, с порядком, без порядка, с добавлением/без добавления, с удалением/без удаления, с последовательным доступом/индексным доступом/без доступа и т.д.
DG>>Сколько интерфейсов будет поддерживать, например, массив?
DG>>Как будет описываться, например, компонент, который сортирует коллекции? Какие интерфейсы он должне требовать в декларации?
G>Да, здесь при ООП дизайне в самом деле можно свернуть себе шею. Чтобы этого не произошло, в языке С++ нам помогут templates. И при этом типизация остается статической. Я думаю, ты это знаешь и без моих комментариев.
Но остается другая проблема. C++-шаблоны совсем не поддерживают компонентность. Нельзя написать на C++ компонент, который наружу выставляет шаблонный метод или шаблонный объект.
Re[20]: Задачка про Circle/Ellipse: "официальный" ответ.
DG>>>>Какое из ООП-правил нарушается для данного кода? G>>>1) Этот код просто-напросто не работает. У тебя нигде не сохраняется промежуточный результат. У тебя только что созданный объект удаляется после каждой операции. Нехорошо. Нерабочий код за пример не канает.
DG>>Результат сохраняется в самой фигуре. G>Он у тебя не сохранялся. У тебя фигура удалялась сразу после изменения, Синклер тебе тоже на это указал. Ну да ладно, ты приготовил новый код.
Можно заметить, что новый код только доопределял идею первоначального кода
G>>>2) В реальном мире ты не всегда можешь менять реализацию компонента. Я же сказал — он уже написан и отлажен. G>>>И написан он вот как: G>skipped DG>>например, вот так: G>skipped G>То, что ты написал, это абсолютно тоже самое, что и
G>class Figure : public IEllipse, ICircle G>{ G>}
Не то же самое.
В моем случае, отдельные грани могут добавляться прямо в runtime, могут разрабатываться по плагинной технологии (поведение класса расширается конечным пользователем) и т.д.
В случае прямого наследования от IEllipse и ICircle мы всех этих прелестей лишены.
G>только выглядит страшнее. Но на самом деле ты так делать не можешь, потому как Ellipse тоже является частью нашего компонента, он уже написан. Трогать его нельзя, можно только наследоваться. Попробуй, и посмотри что получится. Вот _в_этом_ смысл принципа Лисков.
В нормальной архитектуре, компоненты работают не с классами, а с интерфейсами.
Поэтому все вышеприведенное остается в силе.
Re[21]: Задачка про Circle/Ellipse: "официальный" ответ.
G>>То, что ты написал, это абсолютно тоже самое, что и
G>>class Figure : public IEllipse, ICircle G>>{ G>>}
DG>Не то же самое. DG>В моем случае, отдельные грани могут добавляться прямо в runtime, могут разрабатываться по плагинной технологии (поведение класса расширается конечным пользователем) и т.д. DG>В случае прямого наследования от IEllipse и ICircle мы всех этих прелестей лишены.
А-а, так ты пытаешься эмулировать динамическую типизацию? А я-то смотрю — и не догоняю, к чему это все. Так бы и сказал сразу. Тады понятно.
Но в любом случае, ты отдаешь себе отчет в том, что просто предложил обходной путь (workaround), но не опроверг принцип Лисков?
G>>только выглядит страшнее. Но на самом деле ты так делать не можешь, потому как Ellipse тоже является частью нашего компонента, он уже написан. Трогать его нельзя, можно только наследоваться. Попробуй, и посмотри что получится. Вот _в_этом_ смысл принципа Лисков.
DG>В нормальной архитектуре, компоненты работают не с классами, а с интерфейсами. DG>Поэтому все вышеприведенное остается в силе.
Ну, это ты зря совсем. Компонента — это то, что я захочу, если я дизайнер. Возьму — и назову функционально законченную группу плюсовых классов "компонентой", и не буду заморачиваться, так как использовать это буду тоже из С++. Но если ты настаиваешь, пусть это будет "подсистема", так нейтральнее.
А теперь по делу. Наследование есть наследование, а реализация интерфейса — это другое, хоть и похоже. Ты, фактически предлагаешь отказаться от наследования. А мы обсуждаем именно его, отношение наследования! Ну не знаю как "мы", я по крайней мере.
В С++, например, от такого подхода нет никакого толку, благодаря наличию множественного наследования и темплейтов. Если использовать сильные стороны С++, то интерфейсы там встретишь нечасто, и уж точно они не являются основной парадигмой (если конечно не нашиковать приложение на мелкие COM объекты, чего делать низзя ни в коем случае без строгих на то показаний — уж больно велик семантический разрыв между COM и С++).
Re[22]: Задачка про Circle/Ellipse: "официальный" ответ.
G>А теперь по делу. Наследование есть наследование, а реализация интерфейса — это другое, хоть и похоже. Ты, фактически предлагаешь отказаться от наследования. А мы обсуждаем именно его, отношение наследования! Ну не знаю как "мы", я по крайней мере.
Наследование... У наследования есть два разных смысла. Первый смысл: это наследование реализации — новый класс наследует поведение, аттрибуты, реализованные интерфейсы и т.д. от предка.
Второй смысл — это наследование интерфейса — этот термин не совсем правильный (т.к. а что именно мы наследуем при наследовании от интерфейса?). Поэтому правильно говорить, что класс реализует интерфейсы, а не наследуется от них.
Все что я говорил выше про наследовании — это было именно наследование реализации.
У Лисковой, наоборот, говорится про интерфейсы, т.е. если класс наследуется от интерфейса эллипс, то реализация этого наследования должна быть корректной.
Рассмотрим класс "эллипс". Этот класс может обозначать две разные сущности:
1. Элиппс, не являющейся кругом. Или другими словами — это эллипс без вырожденных случаев.
2. Эллипс с учетом всех вырожденных случаев. Фактически такой эллипс должен содержать поведение и для таких вырожденных случаев, как круг, точка и может быть такие вырожденные случаи, как "плоскость"(эллипс бесконечного радиуса), "отрезок" (эллипс нулевой длины и какой-то ширины), "прямая" (эллипс нулевой длины и бесконечного радиуса),
"полоса" (эллипс какой-то ширины и бесконечной длины).
Теперь поговорим об интерфейсах для данного случая.
Интерфейсы получаются следующие:
Не вырожденный эллипс ( 0 < ширина < бесконечность, 0 < высота < бесконечность, ширина != высота)
Круг
"Точка"
"Плоскость"
"Полоса"
"Отрезок"
Обобщенный эллипс
Рассмотрим интерфейс "круг". Здесь тоже возможны два разных интерфейса, которые скрываются под одним словом "круг".
С одной стороны — это интерфейс, который поддерживает все операции доступные над кругом, в том числе и растяжение по одной из оси, с другой стороны — возможен интерфейс, который допускает только те операции, результат которых остается в поле "круг" (т.е. в результате после операции круг остается кругом).
рассмотрим интерфейс "обобщенный эллипс".
Например, метод "Дай оси симметрии". такой метод в отличии от такого же метода для невырожденного эллипса, должен возвращать не только пару осей, как для невырожденного эллипса, но и должен уметь возвращать множество из бесконечного числа осей симметрии, т.к. у частного случая "круг" именно такой набор осей симметрии.
Или возьмем метод "Дай больший радиус", если для невырожденного эллипса такой метод имел смысл, то для обобщенного эллипса — этот метод уже может заканчиваться ошибкой, т.к. большего радиуса у вырожденных случаев (круг/точка) просто нет.
Получается, что когда мы говорим про интерфейс "обобщенный эллипс" мы также подразумеваем два разных интерфейса: один интерфейс — это операции, которые доступны над любым вариантом эллипса, второй интерфейс — это набор операций, которые доступны хотя бы над одним вариантом эллипса.
Компоненты не имеют право, напрямую, ссылаться на типы-реализации (иначе получаем нарушение инкапсуляции и нарушение полиморфизма). Компоненты имеют право только пользоваться вышеприведенными интерфейсами.
Теперь вернемся к наследованию, как я уже вышеупомянул, термин "наследование" в первую очередь означает наследование реализации.
Рассмотрим класс-реализацию "обобщенный эллипс".
Этот класс должен реализовывать все вышеуказанные интерфейсы, а их у нас получилось, как минимум 12 штук.
Т.е. получается, что класс-реализация "обобщенный эллипс" содержит реализации интерфейсов "круг", "точка", "невырожденный эллипс" и т.д.
Рассмотрим класс-реализацию "круг".
Этот класс содержит 3 реализации интерфейсов: класс-реализация "точка", класс-реализация "круг с операциями из поля круг", класс-реализация "круг с операциями, выводящие из поля круг".
Теперь рассмотрим наследование реализаций.
Наследовать мы можем, как класс-реализацию "круг" от класс-реализации "обобщенный эллипс", так и "обобщенный эллипс" от "круга".
-- наследование класса-реализации "обобщенный эллипс" от класса-реализации "круг" --
Здесь все просто. Обобщенный эллипс добавляет реализацию тех методов, которые не были реализованы в круге.
-- Наследование класса-реализации "круг" от класса-реализации "обобщенный эллипс". --
Вот мы и добрались до самого интересного.
В данном случае такое наследование тоже может быть. Класс-реализация "круг" после наследования от класса-реализации "обощенный эллипс" отключает ненужные ему реализации, оставляя только необходимые.
В данном случае, нет нарушений правил Лисковой, потому что внешние компоненты не имеют права ссылаться на класс-реализации, они имеют право только ссылаться на интерфейсы.
А для всех внешних компонентов, данный класс-реализация выглядит как правильная класс-реализация "круг"-а.
И для внешних компонентов такое наследование совершенно прозрачно.
Поговорим чуть-чуть о динамической типизации.
Как мы уже выше убедились, у нас есть интерфейсы, которые оставляют класс в том же поле, что и до операции, но есть также и операции, которые переводят класс из одного поля в другое.
Если программе достаточно интерфейсов первого рода, то можно обойтись статической типизацией, а также тем, что объект не меняет набор поддерживаемых интерфейсов по ходу работы.
Если же нам нужны и интерфейсы второго рода и не достаточно интерфейсов первого рода, то необходимы также и динамической типизации, а также изменение набора поддерживаемых интерфейсов у объекта по ходу работы.
Все текущие индустриальные языки (C++, Java, C#) довольствуются интерфейсами только первого рода, и не поддерживают, напрямую, интерфейсы второго рода.
G>В С++, например, от такого подхода нет никакого толку, благодаря наличию множественного наследования и темплейтов. Если использовать сильные стороны С++, то интерфейсы там встретишь нечасто, и уж точно они не являются основной парадигмой (если конечно не нашиковать приложение на мелкие COM объекты, чего делать низзя ни в коем случае без строгих на то показаний — уж больно велик семантический разрыв между COM и С++).
Я правильно, понял, что ты предлагаешь писать компоненты, которые ссылаются не на интерфейсы, а ссылаются сразу на классы-реализации?
А как быть тогда с инкапсуляцией и полиморфизмом?
Здравствуйте, Gaperton, Вы писали:
G>Началось все достаточно давно с появления концепции "Абстрактный Тип Данных" (то, что сейчас в ООП называется "инкапсуляцией"). Определение таково: тип данных, определяемый набором операций над ним. Концепция совершенно математическая, и вообще говоря самостоятельная. Известны языки, поддерживающие только инкапсуляцию, без наследования и полиморфизма. Уже в конце 70-х курс обучения программированию в Массачусетском технологическом институте был полностью основан на концепции ADT. Желающие могут ознакомиться с этим курсом и сейчас в замечательной книге "Использование абстракций и спецификаций при разработке программ" (Лисков Б., Гатэг Дж. 1989). Кстати, если кто не читал, рекомендую , получите массу удовольствия.
Все верно.
Единственное дополнение, которое я хотел сделать, состоит в следующем.
Читать книгу Лисков и Гатега долго, но есть книга попроще, да и к математике поближе.
Я имею в виду элементарный учебник Ахо, Ульмана и Хопкрофта "Структуры данных и алгоритмы", недавно (пере?)изданный на русском.
В первой же главе там излагается понятие АТД самым простым способом.
АТД именно и трактуется как математическая модель (наверное, точнее было бы сказать: "математическая структура").
Наверное, проще рекомендовать для прочтения эту книгу.
Но существует одно качество, которое нельзя купить, — это надежность. Цена надежности — погоня за крайней простотой. Это цена, которую очень богатому труднее всего заплатить.
Хоар
Re[13]: Задачка про Circle/Ellipse: "официальный" ответ.
Здравствуйте, DarkGray, Вы писали:
G>>Наследование это не отношение "подмножество". Говоря об типах в ООП мы обязаны принять во внимание, что [абстрактный] тип данных (класс) определяется только операциями над ним. При правильном применении наследования в любых языках должен соблюдаться "принцип подстановки Лисков", который состоит в том, что "подкласс должен быть пригоден к использованию во всех контекстах, что и базовый класс", что означает, что операции в подклассе должны работать и сохранять семантику. G>>Другими словами, "сужающее" наследование — совершенно некорректная штука.
Не неправильно. DG>В каких отношениях тогда находится тип целое числа и тип натуральное число? DG>Между ними можно провести отношение наследования или это совсем разные типы, между которыми ничего общего?
DG>Т.е. получается, что мы должны отдельно завести тип целое число, определить для него операции, потом отдельно завести тип натуральное число опять определив все операции? DG>Потом также отдельно завести типы [0..10), [-4..1000], [0..255], [0..65535] и т.д.?
DG>ps DG>Лисков, наверное, жил в идеальном мире, где есть только черное и белое, а мы живем в реальном мире, в котором поддержка только части исходного интерфейса — это норма.
Лисков — это женщина. Ее зовут Бабара.
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Здравствуйте, beretta, Вы писали:
B>Здравствуйте, Кирилл Осенков, Вы писали:
B>Все хотел спросить. А можно ли список параметров метода считать одним объектом?
G>>Да, здесь при ООП дизайне в самом деле можно свернуть себе шею. Чтобы этого не произошло, в языке С++ нам помогут templates. И при этом типизация остается статической. Я думаю, ты это знаешь и без моих комментариев.
DG>Но остается другая проблема. C++-шаблоны совсем не поддерживают компонентность. Нельзя написать на C++ компонент, который наружу выставляет шаблонный метод или шаблонный объект.
Ну и что с того? Какая разница, как будет называться изолированный функционал, готовый к повторному использованию? Особенно, если отдельно я его не собираюсь продавать? Мне как инженеру все равно, будет это достойно называться "компонентом" или нет, лишь бы было удобно и решало задачу.
Например, наша реализация контрола Grid на темплейтах объективно на порядок удобнее в использовании в С++ чем всевозможные AciveX контролы аналогичного назначения (кода получается в разы меньше, при большей гибкости, простоте, и наглядности). Да, ее возможно нельзя назвать "компонентом" в твоих терминах, но это всех волнует в последнюю очередь.
Re[24]: Задачка про Circle/Ellipse: "официальный" ответ.
Здравствуйте, Gaperton, Вы писали:
G>>>Да, здесь при ООП дизайне в самом деле можно свернуть себе шею. Чтобы этого не произошло, в языке С++ нам помогут templates. И при этом типизация остается статической. Я думаю, ты это знаешь и без моих комментариев.
DG>>Но остается другая проблема. C++-шаблоны совсем не поддерживают компонентность. Нельзя написать на C++ компонент, который наружу выставляет шаблонный метод или шаблонный объект.
G>Ну и что с того? Какая разница, как будет называться изолированный функционал, готовый к повторному использованию? Особенно, если отдельно я его не собираюсь продавать? Мне как инженеру все равно, будет это достойно называться "компонентом" или нет, лишь бы было удобно и решало задачу.
Большие проекты разрабатывал?
Большие в том смысле, когда над одним проектом работает три-пять разных компаний (команд), при этом сотрудники каждой отдельной компании могут быть связаны подпиской о неразглашении, поэтому не имеют права отдавать другим свой код в виде исходников. Причем у каждой компании есть свои предпочтения по используемым библиотекам, языкам, компиляторам.
Но даже если можно передавать исходники — все равно это не спасает от проблем с разными библиотеками, версий библиотек, разными компиляторами и т.д.
И как вот такую сборную солянку собирать вместе без компонентов?
G>Например, наша реализация контрола Grid на темплейтах объективно на порядок удобнее в использовании в С++ чем всевозможные AciveX контролы аналогичного назначения (кода получается в разы меньше, при большей гибкости, простоте, и наглядности). Да, ее возможно нельзя назвать "компонентом" в твоих терминах, но это всех волнует в последнюю очередь.
А теперь представь, что таких контролов/классов тысячи, разрабатывают их разные команды, может быть даже на разных компиляторах, а тебе все эти контролы/классы надо собрать в виде цельного проекта.
Твои действия?
ps
Самое страшное — не дай бог у каких-нибудь исходников имена макросов совпадут между собой или совпадут с именами чего-нибудь полезного. Дальше только приходится долго прыгать с бубном, а не заниматься полезными делами.
Re[25]: Задачка про Circle/Ellipse: "официальный" ответ.
Здравствуйте, DarkGray, Вы писали:
DG>Здравствуйте, Gaperton, Вы писали:
G>>>>Да, здесь при ООП дизайне в самом деле можно свернуть себе шею. Чтобы этого не произошло, в языке С++ нам помогут templates. И при этом типизация остается статической. Я думаю, ты это знаешь и без моих комментариев.
DG>>>Но остается другая проблема. C++-шаблоны совсем не поддерживают компонентность. Нельзя написать на C++ компонент, который наружу выставляет шаблонный метод или шаблонный объект.
G>>Ну и что с того? Какая разница, как будет называться изолированный функционал, готовый к повторному использованию? Особенно, если отдельно я его не собираюсь продавать? Мне как инженеру все равно, будет это достойно называться "компонентом" или нет, лишь бы было удобно и решало задачу.
DG>Большие проекты разрабатывал?
Ну, давай посчитаем мой последний проект
Клиент:
ядро — 45 мегов исходников на С++.
Сервер данных:
ядро — 15 мегов исходников на С++.
Код, общий для клиента и сервера, не считая библиотек сторонних производителей — еще мегов 10.
Это все не считая внешних компонент, юнит-тестов, сервисных и конфигурационных приблуд, и прочего софта, имеющего прямое отношение к клиенту и серверу (с ними там гигабайты получаются. Правда.).
Возраст самых старых кусков кода до 10 лет.
Один продукт, 50 разработчиков, распределенная команда Россия — Штаты. Проект уже давно успешно продается, и активно развивается.
Моя группа отвечала за движок базы данных и встроенный язык, и наша работа часто серьезно пересекалась с деятельностью других групп.
А ты большие проекты разрабатывал?
DG>Большие в том смысле, когда над одним проектом работает три-пять разных компаний (команд), при этом сотрудники каждой отдельной компании могут быть связаны подпиской о неразглашении, поэтому не имеют права отдавать другим свой код в виде исходников. Причем у каждой компании есть свои предпочтения по используемым библиотекам, языкам, компиляторам. DG>Но даже если можно передавать исходники — все равно это не спасает от проблем с разными библиотеками, версий библиотек, разными компиляторами и т.д. DG>И как вот такую сборную солянку собирать вместе без компонентов?
Не надо меня подозревать в упертости, там где код надо завернуть в компоненты (как в твоем примере, например, надо обеспечить независимую разработку и обязательно скрыть детали реализации), я это сделаю. Но мелко нашинковывать свой плюсовый код на COM компоненты просто потому, что это модно и современно, не буду ни за какие коврижки — слишком большую цену за это придется заплатить. Я хочу это подчеркнуть потому, что твой предыдущий пример был о контейнерной библиотеке (STL прямо скажем не много теряет от "несовместимости с современными компонентными технологиями"), а мой — об элементе GUI для MS C++.
G>>Например, наша реализация контрола Grid на темплейтах объективно на порядок удобнее в использовании в С++ чем всевозможные AciveX контролы аналогичного назначения (кода получается в разы меньше, при большей гибкости, простоте, и наглядности). Да, ее возможно нельзя назвать "компонентом" в твоих терминах, но это всех волнует в последнюю очередь.
DG>А теперь представь, что таких контролов/классов тысячи, разрабатывают их разные команды, может быть даже на разных компиляторах, а тебе все эти контролы/классы надо собрать в виде цельного проекта. DG>Твои действия?
Реорганизовать разработку, конечно Как можно работать в таком бардаке? Если разные команды разработали тысячи классов, и проект совсем никак не хочет собираться, кому-то надо дать по башке, и неважно, что этот кто-то будет говорить про компоненты. Я бы начал с того, что уволил менеджера. А потом начал разбираться с team lead-ами.
Re[13]: Задачка про Circle/Ellipse: "официальный" ответ.
DG>ps DG>Лисков, наверное, жил в идеальном мире, где есть только черное и белое, а мы живем в реальном мире, в котором поддержка только части исходного интерфейса — это норма.
Лисков = она (баба)
Удачи тебе, браток!
Re[14]: Задачка про Circle/Ellipse: "официальный" ответ.
Здравствуйте, Glоbus, Вы писали:
G>Лисков = она (баба)
Во-первых, не "баба", а женщина (дама, леди, как угодно, но не "баба").
Во-вторых, на этот факт уже два раза указывали в этом топике.
Re[26]: Задачка про Circle/Ellipse: "официальный" ответ.
DG>>Большие проекты разрабатывал? G>Ну, давай посчитаем мой последний проект G>Клиент: G>ядро — 45 мегов исходников на С++.
G>Сервер данных: G>ядро — 15 мегов исходников на С++.
G>Код, общий для клиента и сервера, не считая библиотек сторонних производителей — еще мегов 10.
G>Это все не считая внешних компонент, юнит-тестов, сервисных и конфигурационных приблуд, и прочего софта, имеющего прямое отношение к клиенту и серверу (с ними там гигабайты получаются. Правда.).
G>Возраст самых старых кусков кода до 10 лет.
Итого мегов 70, если я не ошибаюсь исходников недавно пролетавшей винды мегов 200. Вот бы мне такой фантазии, чтоб нагенерить хоть 10 мегов. Или у вас по принципу "Что вижу, то и пою"
Re[27]: Задачка про Circle/Ellipse: "официальный" ответ.
Здравствуйте, beretta, Вы писали:
B>Здравствуйте, Gaperton, Вы писали:
DG>>>Большие проекты разрабатывал? G>>Ну, давай посчитаем мой последний проект G>>Клиент: G>>ядро — 45 мегов исходников на С++.
G>>Сервер данных: G>>ядро — 15 мегов исходников на С++.
G>>Код, общий для клиента и сервера, не считая библиотек сторонних производителей — еще мегов 10.
G>>Это все не считая внешних компонент, юнит-тестов, сервисных и конфигурационных приблуд, и прочего софта, имеющего прямое отношение к клиенту и серверу (с ними там гигабайты получаются. Правда.).
G>>Возраст самых старых кусков кода до 10 лет.
B>Итого мегов 70, если я не ошибаюсь исходников недавно пролетавшей винды мегов 200. Вот бы мне такой фантазии, чтоб нагенерить хоть 10 мегов.
Фантазии. Не верят, гляди ка
B>Или у вас по принципу "Что вижу, то и пою"
Умные все стали, блин. Нет, у нас по принципу 50 человек на 10 лет. Знаешь, начиная с некоторого рамера системы, ее возраста, и размера команды, вы все будете мечтать совсем о другом — как бы не нагенерить лишних мегов. И что самое смешное, у вас это будет не получаться.
А вообще, вот тебе простой рассчет, чтобы снять вопросы про фантазию. Считай: один программер в серднем по индустрии дает 20K строк в год. 50 человек — 1М строк в год. За 10 лет — 10М строк. Пусть строка в среднем 10 байт. Получаем 100Мб исходников.
А насчет винды ты не прав. По интернету проходила статья про то, как билдится Win 2000. Она у них полдня компилируется на сетке из десятка машин. У нас, слава богу, пока справляется одна, только придется погулять несколько часов.
Если интересно посмотреть на это самому, могу организовать экскурсию к нам на работу, и показать. Я лично неплохо ориентируюсь в 18 мегах года, из которых написал сам не более 500 кил.