2008-09-07 2 views
23

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

Groovy дает мне возможность duck-type, но я не могу увидеть значение. Как утиная печать более продуктивна, чем статическая типизация? Что я могу сделать в своей практике кода, чтобы помочь мне понять преимущества этого?

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

+1

Duck типирование и статической типизации являются ортогональными понятиями. – 2010-12-14 15:53:13

ответ

4

Далее, что лучше: EMACS или vi? Это одна из бегущих религиозных войн.

Подумайте об этом так: любая программа, которая является правильной, будет правильной, если язык статически типизирован. Какая статическая типизация дает возможность компилятору иметь достаточную информацию для обнаружения несоответствий типов во время компиляции вместо времени выполнения. Это может быть раздражением, если вы делаете поэтапные виды программирования, хотя (я утверждаю), если вы четко думаете о своей программе, это не имеет большого значения; с другой стороны, если вы создаете действительно большую программу, такую ​​как операционная система или телефонный коммутатор, с десятками или сотнями или тысячами людей, работающих на ней, или с действительно высокими требованиями к надежности, а затем с его компилятором обнаруживают для вас большой класс проблем, не требуя тестового примера для реализации только правильного пути кода.

Это не так, как если бы динамическая типизация была новой и другой: C, например, эффективно динамически типизирован, так как я всегда могу отличить foo* до bar*. Это просто означает, что тогда моя ответственность как программиста C никогда не использовать код, который подходит для bar*, когда адрес действительно указывает на foo*. Но в результате проблем с большими программами C выросли инструменты, такие как lint (1), укрепили систему своего типа с typedef и в итоге разработали строго типизированный вариант на C++. (И, конечно же, C++, в свою очередь, разработал способы сильной типизации, со всеми разновидностями отливок и дженериков/шаблонов и с RTTI.

Еще одна вещь, хотя --- не путайте «гибкое программирование» с «динамическими языками». Agile programming - о том, как люди работают вместе в проекте: может ли проект адаптироваться к изменяющимся требованиям для удовлетворения потребностей клиентов, сохраняя при этом гуманную среду для программистов? Это может быть сделано с динамически типизированными языками, и часто это потому, что они могут быть более продуктивными (например, Ruby, Smalltalk), но это можно сделать, было сделано успешно, на C и даже на ассемблере. Фактически, Rally Development даже использует гибкие методы (в частности, SCRUM) для маркетинга и документации.

1

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

+0

Существует много анекдотических данных, свидетельствующих о том, что утиная печать значительно более продуктивна, чем статическая типизация. – postfuturist 2009-02-10 16:55:07

+1

@postfuturist: Только для номинальных систем статического типа. – 2010-12-14 16:25:45

6

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

1

Bunch @ Крис

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

Извините, но я должен был сделать это ...

+0

Хе-хе, конечно, все в порядке! Вот почему ни один из них не является действительно более продуктивным и просто другим! – 2008-09-07 01:09:55

9

Там нет ничего плохого в статической типизации, если вы используете Haskell, который имеет невероятную статическую систему типов. Однако, если вы используете такие языки, как Java и C++, которые имеют ужасно разрушительные типы систем, утиная печать, безусловно, является улучшением.

Представьте, что вы пытаетесь использовать что-то так просто, как «map» в Java (и нет, я не имею в виду the data structure). Даже дженерики довольно плохо поддерживаются.

+1

Haskell имеет отличную систему типов - было бы здорово, если бы в более разных языках использовался аналогичный механизм. – mipadi 2008-11-09 16:15:56

+0

Нет ничего плохого в отношении типа карты Java. Проблема заключается в том, что у Java нет свободного синтаксиса для работы с Картами. Это то, что Groovy адресовал, но это не связано с типичной утиной Groovy. – slim 2010-02-16 12:50:49

+0

@slim: Я не говорю о типе «map», я говорю о функции более высокого порядка «map». Я добавлю ссылку на свой ответ. – 2010-02-16 23:49:50

7

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

Мой последний любимый аргумент против утиной типизации происходит от проекта Grails DTO:

class SimpleResults { 
    def results 
    def total 
    def categories 
} 

где results оказывается чем-то вроде Map<String, List<ComplexType>>, которые могут быть обнаружены только после след вызовов методы в разных классах, пока не найдете, где он был создан. Для неизлечимо любопытно, total это сумма размеров List<ComplexType> с и categories является размер Map

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

3

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

class SimpleResults { 
    def mapOfListResults 
    def total 
    def categories 
} 

Допустим, вы определяете контракт на какой-то операции под названием "calculateRating (A, B) где А и В придерживаются другого договора. В псевдокоде, он будет читать:

Long calculateRating(A someObj, B, otherObj) { 

    //some fake algorithm here: 
    if(someObj.doStuff('foo') > otherObj.doStuff('bar')) return someObj.calcRating()); 
    else return otherObj.calcRating(); 

} 

Если вы хотите реализовать это в Java, как А и В должны реализовать некий интерфейс, который читает что-то вроде этого:

public interface MyService { 
    public int doStuff(String input); 
} 

Кроме того, если вы хочу обобщать вам контракт для расчета рейтингов (скажем, у вас есть еще один алгоритм для рейтинговых расчетов), вы также должны создать интерфейс:

public long calculateRating(MyService A, MyServiceB); 

с утиной типизации, вы можете d itch your интерфейсы и просто полагайтесь на то, что во время выполнения оба A и B правильно ответят на ваши вызовы doStuff(). Нет необходимости в конкретном определении контракта. Это может работать для вас, но оно также может работать против вас.

Недостатком является то, что вы должны быть особенно осторожны, чтобы гарантировать, что ваш код не сломается, когда другие его изменят (т. Е. Другой человек должен знать о неявном контракте на имя и аргументы метода) ,

Обратите внимание, что это особенно усугубляется в Java, где синтаксис не такой уж краток, как он мог бы быть (по сравнению с Scala, например). Противоположным примером является Lift framework, где они говорят, что количество SLOC фреймворка аналогично Rails, но тестовый код имеет меньше строк, потому что им не нужно реализовывать проверки типов в рамках тестов.

13

Многие комментарии к печати уток на самом деле не подтверждают претензии. Не «беспокоиться» о типе не является устойчивым для обслуживания или продления срока действия приложения. У меня действительно была хорошая возможность увидеть Грааля в действии над моим последним контрактом, и его довольно забавно наблюдать на самом деле. Все счастливы в том, что получают возможность «создавать-приложение» и уходят - к сожалению, все это догоняет вас на заднем плане.

Groovy кажется мне таким же способом. Конечно, вы можете написать очень сжатый код, и, безусловно, есть хороший сахар в том, как мы работаем со свойствами, коллекциями и т. Д. Но стоимость незнания того, что передается взад и вперед, только ухудшается и ухудшается. В какой-то момент вы почесываете голову, задаваясь вопросом, почему проект стал 80% -ным тестированием и 20% работает. Урок здесь заключается в том, что «меньший» не делает для «более читаемого» кода. Извините, люди, простая логика - чем больше вы должны интуитивно знать, тем сложнее процесс понимания этого кода. Именно поэтому графические интерфейсы на протяжении многих лет перестали быть слишком знаковыми - наверняка выглядит довольно хорошо, но WTH происходит не всегда очевидно.

Люди, которые, по-видимому, столкнулись с «уроками», но когда у вас есть методы, возвращающие один элемент типа T, массив T, ErrorResult или null ... это скорее очевидный.

Одна вещь, работающая с Groovy, сделала для меня, однако - удивительные оплачиваемые часы!

2

Вот один из сценариев, в которых печать утинов сохраняет работу.

Вот очень тривиальное класс

class BookFinder { 
    def searchEngine 

    def findBookByTitle(String title) { 
     return searchEngine.find([ "Title" : title ]) 
    } 
} 

Теперь для модульного тестирования:

void bookFinderTest() { 
    // with Expando we can 'fake' any object at runtime. 
    // alternatively you could write a MockSearchEngine class. 
    def mockSearchEngine = new Expando() 
    mockSearchEngine.find = { 
     return new Book("Heart of Darkness","Joseph Conrad") 
    } 

    def bf = new BookFinder() 
    bf.searchEngine = mockSearchEngine 
    def book = bf.findBookByTitle("Heart of Darkness") 
    assert(book.author == "Joseph Conrad" 
} 

Мы были в состоянии заменить собой EXPANDO для Поисковик, из-за отсутствия статической проверки типов. При проверке статического типа нам пришлось бы обеспечить, чтобы SearchEngine был интерфейсом или, по крайней мере, абстрактным классом, и создавал полную макетную реализацию. Это трудоемко, или вы можете использовать сложную одноцелевую фальсификацию. Но утиная печать - универсальная, и нам это помогло.

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

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

Это преимущество утиной печати. Это не означает, что динамическая типизация является правильной парадигмой для использования во всех ситуациях. В моих проектах Groovy мне нравится переключиться на Java в обстоятельствах, когда я чувствую, что предупреждения компилятора о типах помогут мне.

2

Мое мнение:

Динамически отпечатаны или утка напечатал языки являются игрушками. Вы не можете получить Intellisense, и вы теряете время компиляции (или редактируете время - при использовании REAL IDE, например VS, а не того мусора, который другие люди считают IDE) проверки кода.

Оставайтесь в стороне от любого языка, который не статически типизирован, все остальное - просто мазохизм.

2

С, TDD + 100% покрытие кода + инструменты IDE для постоянного запуска моих тестов, мне больше не нужна статическая типизация. Без сильных типов мое модульное тестирование стало настолько простым (просто используйте Карты для создания макетных объектов). Специально, когда вы используете Generics, вы можете увидеть разницу:

//Static typing 
Map<String,List<Class1<Class2>>> someMap = [:] as HashMap<String,List<Class1<Class2>>> 

против

//Dynamic typing 
def someMap = [:] 
Смежные вопросы