Здравствуйте, Геннадий Васильев, Вы писали:
AVK>>Так же совершенно очевидным является то что сопоставление этой самой информации руками не есть лучшее решение, поскольку все то же самое можно сделать автоматически.
ГВ>Это бабушка надвое сказала. Полностью автоматически вся метаинформация всё равно не появится.
Что значит полностью автоматически?
ГВ>Тем паче, что C++ позволяет до определённой степени автоматизировать этот процесс.
Здравствуйте, AndrewVK, Вы писали:
ГВ>>Извини, так получилось, хотя набор можно расширять за счёт дополнительных библиотек без перекомпиляции основного модуля. Всё равно там получаются виртуальные вызовы. AVK>Извини, но все равно получается через задний проход.
"А баба Яга — против!" (c) Мультик. Извини, просто ты так эмоционально реагируешь.
AVK>>>А зачем там такой анализ? Там все проще будет — достать из атрибута тип конвертера(Комментарии ниже — ГВ.), создать экземпляр, вызвать метод. И все. Никакого захардкоденного анализа, рассчитанного на определенный набор входных типов не надо. Прелесть сборок в том что они самоописательны. Т.е. для добавления новых типов достаточно просто положить в каталог сборки с дополнительными обработчиками. Базовую сборку менять или пересобирать не надо. Вариант с шаблонами требует как минимум перекомпиляции. ГВ>>А что ты будешь делать, если для некоего нового типа данных ещё нет обработчика среди дополнительных сборок? AVK>Напишу и скомпиляю в отдельную сборку.
И что? Где же помощь reflection? Только в том, что ты выполнишь вместо подстановки средствами механизма виртуальных функций подстановку средствами атрибутов? Т.е., грубо говоря, слоты размещаются не в VMT, а в атрибутах.
В цитате я специально выделил то место, где ты упускаешь из внимания фазу анализа. Framework сначала ищет заданный атрибут, а потом извлекает из него тип конвертера и создаёт экземпляр. То же самое делает программа на C++, поскольку тоже сначала анализирует сигнатуру типа, соспоставленую входным данным, например, в ADO-рекордсете. И в моём и в твоём случае цикл поиска:
по сигнатуре типа найти указатель на нужный конвертер (C++) или сгенерировать по определённому программистом закону имя типа конвертера и подгрузить его (.Net). Второй вариант, действительно, попроще в реализации, чем первый, если для первого нет небольшого наработанного Framework.
Более того, ты утверждаешь, что положишь тип конвертера в атрибут некоего типа, соответствующего типу, размещённому в SOAP-ответе. Значит, предлагаешь две фазы поиска — найти соответствующий соаповскому тип и поискать среди его атрибутов нужный, либо при получении SOAP-типа сгенерить простейший конвертер, который соединит эти фазы условно в одну. Можно даже сам шлюзовый тип сгенерить на лету. Напоминаю, что мы предполагаем, что SOAP-ответ приходит от программы стороннего разработчика, который знать не знает о наших конвертациях.
Обязанность по поддержанию адекватного набора конвертеров по прежнему лежит на программисте. С той лишь разницей, что для .Net он будет делать сборки, а на C++ скорее всего, — .dll или Shared Objects.
ГВ>>OK, я тебя понял. И то, к чему ты упомянул про дополнительные сборки в каталогах программы — тоже. И сразу хочу задать встречный вопрос. А кто напишет для моей программы нужные именно ей конвертеры? Предположим, что речь идёт не о банльной конвертации в строковое представление, а например, в хливких шорьков, метОда использования которых известна только мне. Я позволю себе задать этот вопрос, поскольку спор начинался с общих фраз.
AVK>Это уже совсем другой вопрос.
Да нет, в том-то и дело, что это две стороны одно и того же вопроса.
AVK>Главное что при добавлении новых типов старый код менять не нужно. Это уменьшает количество работы и гарантирует от глюков, связанных с тем что ты поменял тип, но забыл поменять его мап.
При такой ситуации (необрабатываемые данные во внешнем источнике данных) — всегда возможны глюки, а вернее — runtime-ошибки.
И где я говорил, что новые конвертеры, обрабатывающие сигнатуры новых типов данных источника не могут подгружаться? В default-ветке switch-а вполне может стоять что-то вроде:
default: locate_custom_datatype_converter(...);
где и будет выполняться поиск по map, если заранее предусмотренного конвертера нет. А map будет заполняться библиотеками драйверов.
Кстати, мне немного неясно, что ты имеешь ввиду под словами "поменял тип, но забыл поменять его мап".
AVK>>>А вот в таком смысле и изменился — в возвращаемую табличку добавились типы, неизвестные на момент компиляции. ГВ>>1. Программа выдаст пустые строки или сообщение об ошибке в зависимости от контекста. ГВ>>2. Я разберусь с тем, что происходит и сделаю Upgrade своей проги. AVK>Причем править придется старый код. А это действительно кривой дизайн.
Не факт. Ситуация может потребовать изменения как всей проги, так и только написания для неё дополнительного адаптера/конвертера и т.п. Для твоего случая справедливо то же самое, так что не надо преждевременно обобщать.
AVK>>>Тогда объясни свой тезис о неправильном дизайне при использовании rtti ГВ>>Всё очень просто. Вот код:
[...] ГВ>>То, что написано выше я называю "RTTI-based подходом". Возможно, я не совсем прав в выборе термина. AVK>Ну уж нет, ты не про rtti-based подход говорил, ты говорил просто про использование rtti.
А, ну вот уже лучше, о терминах договариваемся. Progress, однако. А то твоя иллюстрация никак не противоречила моим тезисам. Думаю, что это естественно, поскольку "опыт — не пропьёшь". Понимаешь, ты просто привёл пример того же самого правильного LSP-дизайна, только опирающегося на подстановку, выполняемую с помощью атрибутов, а не виртуальными функциями и абстрактными интерфейсами. Так что, противоречия, на самом деле — нет. Принципы дизайна остались теми же самыми. Ну да, часть задач дотнет берёт на себя, но я это и не собирался опровергать, знаешь, было бы глупо.
ГВ>>Следствия такого подхода (который я называю в том числе и кривым) нужно описывать? Думаю — нет. AVK>Это все понятно. Только вот некривой подход с использованием шаблонов идет лесом, как только оказывается что часть кода недоступна на момент компиляции. Ты привых не использовать компоненты, поэтому тебе кажется что твой подход всегда лучше, а на деле это совсем не так.
Причём тут моё наличие или отсутствие привычки использовать компоненты? Я привык жёстко организовывать программу, чтобы обеспечивать её надёжность (и скорость). Это отнюдь не означает, что в ней мест, где используются компоненты (библиотеки, модули — хоть груздём назови).
ГВ>>В противовес этому сильная типизация исключает фазы анализа x на наличие у него этого метода. Он есть по определению. Остаётся только оверхед на вычисление адреса точки входа по VMT. Но! Само по себе наличие метода гарантируется компилятором. Это, кстати, справедливо и для C++ и для .Net. AVK>Вот только компилятор не может гарантировать того чего может не знать.
Естественно. И, ИМХО, "правильный" дизайн должен вести к снижению количества подобных ситуаций. Просто это повышает надёжность, быстродействие и упрощает дальнейшие модификации. И потом, как это компилятор может не знать? Если он чего-то не знает, значит это — не его компетенция. Пример, которй ты привёл как раз это и подтверждает. SOAP-ответы и есть источники входных данных, которым требуется дополнительный анализ, вроде парсинга и принятия решения об обработке. Клиентская программа может не иметь соответствующих обработчиков, да, но это — случай обработки внешних данных и у программы на C++ будут ровно те же проблемы, что и дотнетовской. Проблема-то не в обработке внешних данных, где от анализа нельзя избавиться по определению, а в применении RTTI-based дизайна внутри программы. Значение термина "RTTI-based" я объяснял предыдущим постом.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Здравствуйте, AndrewVK, Вы писали:
AVK>>>Так как тогда быть с твоими заявлениям про неверный дизайн? ГВ>>Оно никуда не делось, и я от него не отказываюсь.
AVK>Забавно. Правильно сказали — если рефлекшен позволяет создаватьболее красивые и лаконичные решения, так может стоит пересмотреть свои оценки дизайна?
Причём здесь оценки дизайна? Если рефлекшн можно использовать в рамках вполне приличного дизайна (твой пример о конвертерах неизвестных типов), то никаких противоречий нет. Просто некоторые задачи решаются с помощью рефлекшена. А в общем дизайн от этого не менятеся.
AVK>>>Не увиливай. Речь идет о сериализации. ГВ>>Не передёргивай. Речь идёт о подходе, основанном на "самоанализе" программы. Всему своё время, сериализацию ещё вспомним. AVK>Переключение спора на другую тему является демагогией.
Не совсем так, просто на конкретный вопрос о сериализации я дам конкретный ответ несколько позже.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Здравствуйте, AndrewVK, Вы писали:
ГВ>>>>Да фиг с ней, с сериализацией. Вопрос не в сериализации самой по себе. Предположим, что речь идёт не о сериализации, VD>>>Нет, ну почему же? Это хороший пример демонстрирующий приемущества наличия полноценной информации. ГВ>>Бесспорно, но не отрицающий недостатков "типоаналитического" подхода в целом. AVK>Недостатки в целом? Извини, но это демагогия. Уже одно то что рефлекшен позволяет в разы упростить сериализацию дает ему право на существование. Естественно что у него, как и у любой технологии, есть свои недостатки, ровно как есть они у шаблонов или МН, но это отнюдь не означает что его применение свидетельствует о кривом дизайне.
Ещё раз. Я говорил о дизайне и описал, какой именно дизайн я имею ввиду.
VD>>>Еще раз повторяю. Отойди от привычный тебе концепций. В дотнете богатство информации о типах позволяет во многих случаях писать универсальный код. В том числе и для сериализации. ГВ>>Согласен — во многих, что логически (да и жизненно ) верно видоизменить на "в некоторых". AVK>Тут позволь с тобой не согласиться. Все таки имея приличный опыт использования шарпа могу тебя заверить что пользу от применения рефлекшена можно получить очень частро. То что ты не видишь других способов использования рефлекшена, кроме как замены шаблонов еще не означает что их нет. Все алгоритмы, рассчитанные на работу с неизвестными на момент компиляции типами при наличии рефлекшена решаются значительно проще.
Да не неизвестные это типы! Это типы, для которых известна, как минимум, структура.
ГВ>>Для того, чтобы говорить о "многих", нужно определить "все возможные случаи". AVK>Случаи, встречающиеся мне на практике — так устроит?
Они и есть — "некоторые".
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Здравствуйте, AndrewVK, Вы писали:
AVK>>>Так же совершенно очевидным является то что сопоставление этой самой информации руками не есть лучшее решение, поскольку все то же самое можно сделать автоматически. ГВ>>Это бабушка надвое сказала. Полностью автоматически вся метаинформация всё равно не появится. AVK>Что значит полностью автоматически?
Custom-атрибуты автоматически добавляются? А указания об их добавлении откуда появляются?
ГВ>>Тем паче, что C++ позволяет до определённой степени автоматизировать этот процесс. AVK>Степень только этоа не очень.
Рассмотрим попозже.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Здравствуйте, AndrewVK, Вы писали:
AVK>>>Потому что можно обойтись без этого. ГВ>>В дотнете — да. В той системе, для которой были написаны виденные тобой реализации, скорее всего — нет. AVK>Так как быть с неправильностью rtti?
Смотря что этим словом называется и где применено. Я ответил в соседнем постинге.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Здравствуйте, AndrewVK, Вы писали:
AVK>>>О, мы уже до кодогенераторов дошли. Типа рефлекшен это кривой дизайн, а вот кодогенераторы это уже прямой. Самому не смешно? ГВ>>Смешно. Поскольку под термином "прямой" я имел ввиду вовсе не оценку из системы "прямой-кривой", а аналогичность в смысле функциональности. В том смысле, что действия "мышкой тыкать и имена колонок в окошках вводить" и "в тексте набить маппинг" одинаковы по своему результату — настройке колонок грида на структуру запроса. Последнее даже проще. AVK>Нифига подобного. Не говоря уж о жутком времени компиляции плюсов — сделать этьо в рантайме вобще практически невозможно. А вот в случае рефлекшена я могу собрать кодогенератор прямо в рантайме, причем сразу в кодах, минуя стадию компиляции.
Да, не спорю, эмиттинг это позволяет, но проблема в другом — в правилах и алгоритмах сборки кодогенератора. Они-то откуда появятся?
AVK>Опять оказывается что столь нелюбимый тобой рефлекшен позволяет создавать более красивые решения.
В ряде случаев — да. Но в ряде случаев Lisp, например, гораздо красивее C++, и что из этого следует?
AVK>>>>>Потому что прикладному программисту нужно обязательно помнить о необходимости предварительно вносить типы в меппер. ГВ>>>>И для этого нужна высокая квалификация? AVK>>>Нет, но это однозначно кривой дизайн. ГВ>>Да, если такая функциональность (внесения в мапперы или аналогичная) уже реализована в окружении и попросту дублируется.Нет, если такой функциональнсти нет. AVK>Все равно кривой дизайн. И дело не в окружении, дело в подходе. В нете я могу спокойно планировать очень широкое использование рефлекшена, потому что использовать его ничего в плане дизайна не стоит.
Дизайн всегда стоит одинаково.
AVK>На плюсах же аналогичные алгоритмы с некоторого момента весь привнесенный плюс погребут под огромным количеством служебного кода и снижением удобства модификации программы.
Очень и очень спорное обобщение, что на C++ — обязательно погребут.
AVK>А это означает ровно одно — кривой дизайн.
Это-то бесспорно — если программа погребена под огромным количеством служебного кода, но эту проблему как раз и должен решать дизайн.
AVK> И если С++ не позволяет реализовать задачу как то иначе то это проблемы именно С++.
Или дизайнера, что, ИМХО — чаще.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Здравствуйте, Геннадий Васильев, Вы писали:
ГВ>Ещё раз. Я говорил о дизайне и описал, какой именно дизайн я имею ввиду.
Ты говорил что применение "самоанализа" это кривой дизайн. Перечитай еще раз свой пост.
AVK>>Тут позволь с тобой не согласиться. Все таки имея приличный опыт использования шарпа могу тебя заверить что пользу от применения рефлекшена можно получить очень частро. То что ты не видишь других способов использования рефлекшена, кроме как замены шаблонов еще не означает что их нет. Все алгоритмы, рассчитанные на работу с неизвестными на момент компиляции типами при наличии рефлекшена решаются значительно проще.
ГВ>Да не неизвестные это типы! Это типы, для которых известна, как минимум, структура.
Ничего подобного. Структура ровно так же может описываться в метаданных. Взгляни на досуге замечательный дотнетовский компонент — PropertyGrid, который может редактировать ЛЮБОЙ класс дотнета. При этом исходников самого PropertyGrid не нужно и изменение его версии не повлечет за собой необходимость изменения редактируемых им компонент. На момент компиляции PropertyGrid компилятор не имеет ни малейшего представление ни о интерфейсе, ни даже о структуре типов, которые он затем будет использовать.
AVK>>Случаи, встречающиеся мне на практике — так устроит?
ГВ>Они и есть — "некоторые".
Но твое то утверждение было безусловным, не ограничивающим область применения.
Здравствуйте, Геннадий Васильев, Вы писали:
AVK>>Забавно. Правильно сказали — если рефлекшен позволяет создаватьболее красивые и лаконичные решения, так может стоит пересмотреть свои оценки дизайна?
ГВ>Причём здесь оценки дизайна?
При том что твое заявление что дизайн с использованием "самоанализа" безусловно кривой неверно.
ГВ>Если рефлекшн можно использовать в рамках вполне приличного дизайна (твой пример о конвертерах неизвестных типов), то никаких противоречий нет.
То есть ты признаешь что твое первое заявление было неверным?
Здравствуйте, Геннадий Васильев, Вы писали:
ГВ>>>Это бабушка надвое сказала. Полностью автоматически вся метаинформация всё равно не появится. AVK>>Что значит полностью автоматически?
ГВ>Custom-атрибуты автоматически добавляются?
А кастом атрибуты нужны только если не устраивает некий штатный вариант поведения. В большинстве случаев можно обойтись и без них, штатная информация весьма богата. И потом — принципиальное отличие атрибутов от шаблонов в том что атрибуты это сугубо декларативная сущность, они не содержат алгоритмической части, а значит вероятность ошибки крайне низка.
Здравствуйте, Геннадий Васильев, Вы писали:
ГВ>Да, не спорю, эмиттинг это позволяет, но проблема в другом — в правилах и алгоритмах сборки кодогенератора. Они-то откуда появятся?
На основании информации из рефлекшена.
AVK>>Все равно кривой дизайн. И дело не в окружении, дело в подходе. В нете я могу спокойно планировать очень широкое использование рефлекшена, потому что использовать его ничего в плане дизайна не стоит.
ГВ>Дизайн всегда стоит одинаково.
Вот как раз ничего подобного. К сожалению красивая сказка о том что дизайн не зависит от инструмента для его реализации всего лишь сказка, инструмент накладывает серьезные ограничивающие или наоборот стимулирующие факторы на возможность применения тех или иных дизайнерских решений. И не учитывать это в дизайне по меньшей мере глупо.
AVK>>На плюсах же аналогичные алгоритмы с некоторого момента весь привнесенный плюс погребут под огромным количеством служебного кода и снижением удобства модификации программы.
ГВ>Очень и очень спорное обобщение, что на C++ — обязательно погребут.
Это вывод на основании практики использования.
AVK>> И если С++ не позволяет реализовать задачу как то иначе то это проблемы именно С++.
ГВ>Или дизайнера, что, ИМХО — чаще.
Бестолковый аргумент — на неумелость дизайнера можно свалить что угодно.
Здравствуйте, Геннадий Васильев, Вы писали:
ГВ>Я говорил о подходе. То, что в частном случае вытряхивание информации о структуре типа полезно — никак не отрицает недостатков RTTI-bases.
Да нет никаких недостатков "RTTI-bases". Есть разумное и не разумное использование некоторых подходов в конкретных условиях. Информация о типах полезна везде, где есть необходимось добиться универсальности в рантайме. Везде где можно обойтись компайл-таймом она не нужна.
... << RSDN@Home 1.1 beta 1 >>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Позволю себе не ввязываться в демагогию по поводу размеров понятий "некоторых" и "многих"...
ГВ>Да не неизвестные это типы! Это типы, для которых известна, как минимум, структура.
Все вено. Но это не полная правда. Все дело в том, что без информации о типах структура как раз и не будет известна. А с информацией о типах она известна, но на стадии выполнения, а не компиляции. Разговоры о кривизне дизайна в этих вслових просто бессмыслненны. Как можно сравнивать, что-то с пустатой?
... << RSDN@Home 1.1 beta 1 >>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Геннадий Васильев, Вы писали:
VD>>Тогда попробуй предствить, что вся метаинформация делается именно для того, чтобы ее читали.
ГВ>По-моему, я этого и не отрицал. Это прикольно — приписывать мне отрицание моих собственных взглядов.
Ты утверждаешь, что алгоритмы основанные на чтении метаданных являются кривым дизайном. В то время, как метаданные предназначены исключитально для этого. Следовательно она сама по себе являются кривостью. Обычные логические выводы.
... << RSDN@Home 1.1 beta 1 >>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Nose, Вы писали:
N>Здравствуйте, AndrewVK, Вы писали:
N>Для Вас и для VladD2.
Слушай, объясни мне одну вещь. Вот в области С++ на этом сайте есть два авторитета: Андрей Тарасевич и Павел Кузнецов. Я с ними не однократно спорил не тему крутости/убогости С++. Почему ни одни из них не тыкла меня носом в незнание С++, а ты с WolfHound этим занимаешся? Вам обоим не кажется, что прочтение одной книжки еще не дает права считать себя экспертом в некоторой оболасти?
Еще раз повторсь, что С++ я знаю. И уж точно я знаю его настолько, чтобы иметь право выносить свои суждения об этом зяыке.
Так что давай в будущем, если вам захочется доказать свое мнение, вы будете опелировать аргументами и доказательствами, а не учить других жизни.
N> Мне не очень хочется с вами спорить. У меня не много опыта для этого. N>Но. Если вам в руки попадется Александреску, прочитайте. Плохо сосотавлять мнение по чьим-то словам.
Попадется — погляжу. Но искать специально точно не буду. Не верится мне, что можно открыть америку в ванной.
отредактированно модератором. _MM_
... << RSDN@Home 1.1 beta 1 >>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, AndrewVK, Вы писали:
ГВ>>Ещё раз. Я говорил о дизайне и описал, какой именно дизайн я имею ввиду. AVK>Ты говорил что применение "самоанализа" это кривой дизайн. Перечитай еще раз свой пост.
Совершенно верно, я даже описал — какой именно дизайн.
AVK>>>Тут позволь с тобой не согласиться. Все таки имея приличный опыт использования шарпа могу тебя заверить что пользу от применения рефлекшена можно получить очень частро. То что ты не видишь других способов использования рефлекшена, кроме как замены шаблонов еще не означает что их нет. Все алгоритмы, рассчитанные на работу с неизвестными на момент компиляции типами при наличии рефлекшена решаются значительно проще. ГВ>>Да не неизвестные это типы! Это типы, для которых известна, как минимум, структура. AVK>Ничего подобного. Структура ровно так же может описываться в метаданных.
Ага, что и говорит об известности, но достигаемой путём обработки описания этой структуры. Дотнет ведь генерит описание структуры класса и т.п. При этом обязательно делаются предположения о способах использования тех или иных типов данных, либо типы данных содержат дополнительную информацию, касающуюся их использования в определённых ситуациях.
AVK>Взгляни на досуге замечательный дотнетовский компонент — PropertyGrid, который может редактировать ЛЮБОЙ класс дотнета. При этом исходников самого PropertyGrid не нужно и изменение его версии не повлечет за собой необходимость изменения редактируемых им компонент. На момент компиляции PropertyGrid компилятор не имеет ни малейшего представление ни о интерфейсе, ни даже о структуре типов, которые он затем будет использовать.
Зато он настроен на определённый входной язык — сиречь язык описания структуры дотнетовских классов и делает предположения об адекватных способах редактирования. Никакого противоречия тут нет. Более того, должны существовать ситуации, в которых он окажется неадекватным и потребуется его замена. Впрочем, это неважно.
AVK>>>Случаи, встречающиеся мне на практике — так устроит? ГВ>>Они и есть — "некоторые". AVK>Но твое то утверждение было безусловным, не ограничивающим область применения.
Я несколько некорректно его сформулировал, забыл, что термин "RTTI" успел приобрести другое значение. В частности — для тебя.
VD>Дык информация о типах нужна то обычно в рантайме, а вот тут у плюсов идеологический калапс. Его авторы просто не думали о рантайме при проектировании языка.
ГВ>Они сознательно ограничивали его рассмотрение. И ИМХО, совершенно разумно, поскольку необходимость "самоанализа" (он же — rtti) в сущности, является ошибкой дизайна.
Моё утверждение о "самоанализе", естественно, не распространялось на ситуации анализа внешних данных, как не распространяется на них определение RTTI. Имелись ввиду только ситуации, в которых программа начинает анализировать свой сосбственный состав.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!