Re: Утиные истории vs ООП?
От: Voblin Россия http://maslyaew.narod.ru/
Дата: 25.10.05 13:37
Оценка: 13 (2)
Здравствуйте, eao197, Вы писали:

E>

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

E>
Мне, ортодоксальному поклоннику строгой типизации, эти утиные истории кажутся слишком хлипкой конструкцией. Сначала, может быть, это и работает, но потом могут возникать проблемы. Например:
  • "Утиный" код менее очевиден. Не хотел бы я брать на доработку и сопровождение код, в котором Duck typing применён массово.
  • Чем больше в системе всяких явно не описанных "соглашений", "умолчаний", "подразумеваний" и т.п., тем меньше у неё шансов добраться до финального релиза. Может быть, я что-то неправильно понял, но вся мощь "утиных историй" проявляется тогда, когда язык программирования не заставляет нас предельно чётко и скрупулёзно описывать понятие "Утка". Один программер что-то там себе подразумевает, а его товарищ может подразумевать что-то чут-чуть другое. Один из них, например, может быть свято уверен, что утки в принципе не умеют летать.

    E>

    Они взяли наихудшую парадигму из языка и возвели ее в степень догмы. А именно — идею наследования. Наследование — это самая большая провокация в индустрии.

    E>
    Одобрямс!

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

    Пару лет назад, размышляя над этими проблемами, я дошёл до идеи множественной классификации (см. здесь). Лекарство, конечно, слишком сильнодействующее, но я до сих пор не разуверился в том, что рецепт выписан правильно.
  • Re[2]: Утиные истории vs ООП?
    От: eao197 Беларусь http://eao197.blogspot.com
    Дата: 25.10.05 17:03
    Оценка:
    Здравствуйте, Voblin, Вы писали:

    E>>

    V>Мне, ортодоксальному поклоннику строгой типизации, эти утиные истории кажутся слишком хлипкой конструкцией.

    Наверное, правильнее было бы сказать не строгой, а статической типизации. Т.к. duck typing вполне нормально себя чувствует в динамических языках со строгой (сильной) типизацией.

    V> Сначала, может быть, это и работает, но потом могут возникать проблемы. Например:

    V>
  • "Утиный" код менее очевиден. Не хотел бы я брать на доработку и сопровождение код, в котором Duck typing применён массово.
    V>
  • Чем больше в системе всяких явно не описанных "соглашений", "умолчаний", "подразумеваний" и т.п., тем меньше у неё шансов добраться до финального релиза. Может быть, я что-то неправильно понял, но вся мощь "утиных историй" проявляется тогда, когда язык программирования не заставляет нас предельно чётко и скрупулёзно описывать понятие "Утка". Один программер что-то там себе подразумевает, а его товарищ может подразумевать что-то чут-чуть другое. Один из них, например, может быть свято уверен, что утки в принципе не умеют летать.

    Здесь меня уже давно пытаются в этом убедить. А я, собственно, против этого и не возражаю.
    Ну а чего можно возразить? Ну есть unit-тестирование, чтобы постараться выявить моменты нецелевого использования уток на самых ранних стадиях. Ну еще есть такой феномен, как крайне редкое использование уток не по назначению. Наверное потому, что чаще всего утки используются именно там, где известно, что утки -- это именно утки. Есть еще шаблоны C++, которые можно считать чистым duck typing-ом, да еще удачно совмещенным со статической типизацией.

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

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

    V>На мой взгляд, "утиные истории" — это весьма неуклюжая попытка обойти "деревянную" сущность иерархий наследования. И даже когда язык позволяет делать множественное наследование, проблема всё равно окончательно не снимается, а всего лишь загоняется вглубь.


    V>Пару лет назад, размышляя над этими проблемами, я дошёл до идеи множественной классификации (см. здесь). Лекарство, конечно, слишком сильнодействующее, но я до сих пор не разуверился в том, что рецепт выписан правильно.


    Если довести твою идею до мелких интерфесов, содержащих всего один-два метода, и отказаться от именования интерфейсов, то как раз duck typing и получится. По крайней мере мне так показалось после беглого прочтения твоей статьи. Мне показалось, что изобретатели duck typing-а сделали просто еще один шаг вперед. На который ты не решился.
    ... << RSDN@Home 1.1.4 stable rev. 510>>


  • SObjectizer: <микро>Агентно-ориентированное программирование на C++.
    Re[3]: Утиные истории vs ООП?
    От: Voblin Россия http://maslyaew.narod.ru/
    Дата: 26.10.05 07:38
    Оценка:
    Здравствуйте, eao197, Вы писали:

    E>Наверное, правильнее было бы сказать не строгой, а статической типизации. Т.к. duck typing вполне нормально себя чувствует в динамических языках со строгой (сильной) типизацией.


    Ага, особенно когда вот так:
    Duck* MakeDuck(void *anybody) {
      return (Duck*)anybody;
    }



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


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

    E>Если довести твою идею до мелких интерфесов, содержащих всего один-два метода, и отказаться от именования интерфейсов, то как раз duck typing и получится. По крайней мере мне так показалось после беглого прочтения твоей статьи. Мне показалось, что изобретатели duck typing-а сделали просто еще один шаг вперед. На который ты не решился.


    Вот здесь не согласен. По методике "duck typing" мы создаём классы, которые ведут себя как утка, а по моей измышлятине мы получаем возможность создавать переменные, которые являются уткой помимо того, что являются ещё, например, дичью.
    Re[4]: Утиные истории vs ООП?
    От: eao197 Беларусь http://eao197.blogspot.com
    Дата: 26.10.05 10:41
    Оценка:
    Здравствуйте, Voblin, Вы писали:

    E>>Наверное, правильнее было бы сказать не строгой, а статической типизации. Т.к. duck typing вполне нормально себя чувствует в динамических языках со строгой (сильной) типизацией.


    V>Ага, особенно когда вот так:

    V>
    V>Duck* MakeDuck(void *anybody) {
    V>  return (Duck*)anybody;
    V>}
    V>

    V>

    Это пример грязного хака в языке со статической, но слабой типизацией. К duck typing-у отношения не имеет.
    Попробуй сделать что-нибудь подобное в языке с динамической сильной типизацией. Вот, скажем:
    class Duck; def qrack(); end; end
    class Dog; def gaff(); end; end
    
    def make_duck(anybody); return anybody; end
    
    duck = make_duck( Dog.new )
    duck.qrack()

    и получишь то, что следовало ожидать -- исключение:
    duck_typing_strong_typing.rb:7: undefined method `qrack' for #<Dog:0x2778060> (NoMethodError)


    Вместо Undefined Behaviour в слаботипизированном C++.

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


    V>Насколько я понимаю, основная цель применения утиных историй — понижение сложности.


    Не думаю. Основная цель -- уменьшение количества абстракций. Если пользоваться твоими примерами, то для того, чтобы сказать "Здравствуйте", вовсе не нужен интерфейс "Вежливый человек". Достаточно метода "поздоровайся".

    V> И заниматься этим актуально именно там, где приложение очень большое. Для реализации "маленьких независимых процессиков" применение крутых наворотов может оказаться стрельбой из пушки по воробьям.


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

    E>>Если довести твою идею до мелких интерфесов, содержащих всего один-два метода, и отказаться от именования интерфейсов, то как раз duck typing и получится. По крайней мере мне так показалось после беглого прочтения твоей статьи. Мне показалось, что изобретатели duck typing-а сделали просто еще один шаг вперед. На который ты не решился.


    V>Вот здесь не согласен. По методике "duck typing" мы создаём классы, которые ведут себя как утка, а по моей измышлятине мы получаем возможность создавать переменные, которые являются уткой помимо того, что являются ещё, например, дичью.


    Так вся разница в том, что по твоему подходу чтобы быть уткой нужно реализовывать интерфейс "утка". А что есть интерфейс -- набор методов + имя. Если посчитать, что имя интерфейса -- это не нужный рудимент, то остается только набор методов. Если у сущности такой же набор методов, как у утки -- значит это утка и есть. Зачем еще дополнительные ярлыки равешивать?
    ... << RSDN@Home 1.1.4 stable rev. 510>>


    SObjectizer: <микро>Агентно-ориентированное программирование на C++.
    Re[5]: Утиные истории vs ООП?
    От: Voblin Россия http://maslyaew.narod.ru/
    Дата: 26.10.05 16:37
    Оценка:
    Здравствуйте, eao197, Вы писали:

    E>Это пример грязного хака в языке со статической, но слабой типизацией. К duck typing-у отношения не имеет.


    Пардон. Я просто поглумился маленько.

    E>Основная цель -- уменьшение количества абстракций. Если пользоваться твоими примерами, то для того, чтобы сказать "Здравствуйте", вовсе не нужен интерфейс "Вежливый человек". Достаточно метода "поздоровайся".


    Просто иметь метод — недостаточно. Нужно, чтобы он ещё откуда-то вызывался. В простейшем случае — дать понять "клиенту", что у "сервера" есть такой метод. Подходы могут быть следующими:
    1. Прямо сказать клиенту, что ты утка.
    2. Иметь возможность ответить на вопрос: "А не утка ли ты?"
    3. Иметь возможность ответить на вопрос: "А умеешь ли ты крякать?"
    4. Иметь метод SayAnything, который у нас вызовет метод qrack(), а у собаки — gaff().
    5. Ухитриться вписываться в контекст таким и только таким образом, что и так всем понятно, что ты утка. Например, вписываться только в коллекцию Утятник или в реквизит УткаПоПекински объекта Ужин.
    6....и т.п.

    V>>Вот здесь не согласен. По методике "duck typing" мы создаём классы, которые ведут себя как утка, а по моей измышлятине мы получаем возможность создавать переменные, которые являются уткой помимо того, что являются ещё, например, дичью.


    E>Так вся разница в том, что по твоему подходу чтобы быть уткой нужно реализовывать интерфейс "утка". А что есть интерфейс -- набор методов + имя. Если посчитать, что имя интерфейса -- это не нужный рудимент, то остается только набор методов. Если у сущности такой же набор методов, как у утки -- значит это утка и есть. Зачем еще дополнительные ярлыки равешивать?


    В том то всё и дело, что по моему методу описать и реализовать интерфейс "Утка" нужно всего один раз, а не копиблочить по всем классам, где он нужен.

    Справедливый вопрос: а как добавлять специфические фичи к поведению утки, если реализующий его исходный код предлагается (или даже рекомендуется) не трогать? Да очень просто — реализуем эти фичи для сочетаний классов. Например, для сочетания (Утка, Дичь) реакцию на посылку сообщения "УтиУтиУти" можно сделать именно такой, какая характерна для дикой утки.
    Re[6]: Утиные истории vs ООП?
    От: eao197 Беларусь http://eao197.blogspot.com
    Дата: 26.10.05 20:13
    Оценка:
    Здравствуйте, Voblin, Вы писали:

    V>Просто иметь метод — недостаточно. Нужно, чтобы он ещё откуда-то вызывался. В простейшем случае — дать понять "клиенту", что у "сервера" есть такой метод.


    А зачем? Пусть вызывает то, что считает нужным.

    Если мы имеем дело с динамической типизацией, то получим исключение в run-time.
    Если со статической (как в C++ шаблонах), то нас остановит компилятор.

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

    E>>Так вся разница в том, что по твоему подходу чтобы быть уткой нужно реализовывать интерфейс "утка". А что есть интерфейс -- набор методов + имя. Если посчитать, что имя интерфейса -- это не нужный рудимент, то остается только набор методов. Если у сущности такой же набор методов, как у утки -- значит это утка и есть. Зачем еще дополнительные ярлыки равешивать?


    V>В том то всё и дело, что по моему методу описать и реализовать интерфейс "Утка" нужно всего один раз, а не копиблочить по всем классам, где он нужен.




    А чем это duck typing-у противоречит?

    V>Справедливый вопрос: а как добавлять специфические фичи к поведению утки, если реализующий его исходный код предлагается (или даже рекомендуется) не трогать? Да очень просто — реализуем эти фичи для сочетаний классов. Например, для сочетания (Утка, Дичь) реакцию на посылку сообщения "УтиУтиУти" можно сделать именно такой, какая характерна для дикой утки.


    Приведи пример, пожалуйста, a?
    ... << RSDN@Home 1.1.4 stable rev. 510>>


    SObjectizer: <микро>Агентно-ориентированное программирование на C++.
    Re[7]: Утиные истории vs ООП?
    От: Voblin Россия http://maslyaew.narod.ru/
    Дата: 28.10.05 06:30
    Оценка: 3 (2)
    Здравствуйте, eao197, Вы писали:

    V>>Просто иметь метод — недостаточно. Нужно, чтобы он ещё откуда-то вызывался. В простейшем случае — дать понять "клиенту", что у "сервера" есть такой метод.

    E>А зачем? Пусть вызывает то, что считает нужным.
    E>Если мы имеем дело с динамической типизацией, то получим исключение в run-time.
    E>Если со статической (как в C++ шаблонах), то нас остановит компилятор.

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

    E>Не нужно ничего выдумывать. Если клиент предполагает, что он общается с уткой, то нужно ему сервера-утки и подсовывать. Только и всего.


    Хорошо, когда это так, но иногда клиент может отрабатывать разных животных, но уток он хочет отрабатывать как-нибудь по-особенному. Например, вызывать у них не методы ЦыпЦып() или КисКис(), а именно УтиУтиУти().

    E>

    E>А чем это duck typing-у противоречит?
    Процитирую корневой постинг:

    Ведь в duck typing нет отношения "is-a", а есть что-то типа "like-a" ("can", "respond_to"). Т.е., наследования нет, а полиморфизм, благодоря динамической типизации, есть.


    У меня же есть отношение "is-a", нет отношения "like-a", полиморфизм есть, наследование можно не применять (оно просто рассматривается как необязательный частный случай множественной классификации), динамическая строгая типизация (с поддержкой мутации!) есть.

    V>>Справедливый вопрос: а как добавлять специфические фичи к поведению утки, если реализующий его исходный код предлагается (или даже рекомендуется) не трогать? Да очень просто — реализуем эти фичи для сочетаний классов. Например, для сочетания (Утка, Дичь) реакцию на посылку сообщения "УтиУтиУти" можно сделать именно такой, какая характерна для дикой утки.


    E>Приведи пример, пожалуйста, a?

    // Как всегда, на вымышленном языке программирования
    class Duck { // Утка
     func УтиУтиУти(Somebody: AHuman);
    }
    
    class Alive { // Нечто живое, что может двигаться само
     func Move(Direction: ADirection);
    }
    
    class Wild {} // нечто дикое
    
    class Domestic {} // нечто домашнее
    
    func (Duck, Alive)::УтиУтиУти(Somebody: AHuman) { // Сваливаем подальше
       MakeSound("Кря-кря");
    };
    
    func (Duck, Wild, Alive)::УтиУтиУти(Somebody: AHuman) { // Сваливаем подальше
      Move(CalcOppositeDirection(GetDirectionTo(AHuman)));
    };
    
    func (Duck, Domestic, Alive)::УтиУтиУти(Somebody: AHuman) { // Бежим к человеку. Может, пищу дадут...
      Move(GetDirectionTo(AHuman));
    };
    
    // где-то в программе...
    var
     Duck1: Duck,  // Просто утка
     Duck2: (Duck, Alive), // Живая утка
     Duck3: (Duck, Wild),  // Дикая утка
     Duck4: (Duck, Wild, Alive), // Дикая живая утка
     Duck5: (Duck, Alive, Domestic); // Домашняя живая утка
    
    Duck1.УтиУтиУти(Me); // Ничего не будет. "Кря-кря" не скажет и никуда не двинется.
    Duck2.УтиУтиУти(Me); // Скажет "Кря-кря", но никуда не двинется.
    Duck3.УтиУтиУти(Me); // Ничего не будет.
    Duck4.УтиУтиУти(Me); // Скажет "Кря-кря" и убежит.
    Duck5.УтиУтиУти(Me); // Скажет "Кря-кря" и прибежит.
    
    classify Duck2 as Wild; // Смутировали живую утку Duck2. Сделали её дикой.
    Duck2.УтиУтиУти(Me); // Теперь не только крякнет, но ещё и убежит.

    При этом можно предположить, что классы Duck и Alive — библиотечные, а Wild и Domestic — это наши дополнительные фичи.
    Streams
    От: SilverCloud Россия http://rodonist.wordpress.com
    Дата: 02.11.05 19:33
    Оценка:
    Здравствуй, VladD2, ты писал (о потоках в .NET):

    VD>Думаю, что они это сделали по глупости. Точно так же как использовали базовый класс вместо интерфейса.


    Точнее, просто слизали из Delphi, не подумав.. Когда Хейлсберг над Delphi мозговал, интерфейсов там ещё и в помине не было.
    ... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
    Re: Streams
    От: VladD2 Российская Империя www.nemerle.org
    Дата: 03.11.05 00:50
    Оценка:
    Здравствуйте, SilverCloud, Вы писали:

    SC>Точнее, просто слизали из Delphi, не подумав.. Когда Хейлсберг над Delphi мозговал, интерфейсов там ещё и в помине не было.


    Причем тут дельфи. BCL большей части содрана с Явы. Там те же проблемы. Но когда эти проблемы создавались, то Дельфи даже в проекте не было.

    Тут видимо играют роль тонкие дизайнерские изыски. Вот в чем они заключаются я хоть убей понять не могу.
    ... << RSDN@Home 1.2.0 alpha rev. 618>>
    Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
    Подождите ...
    Wait...
    Пока на собственное сообщение не было ответов, его можно удалить.