Каковы некоторые преимущества утиной типизации по сравнению со статической? - PullRequest
26 голосов
/ 07 сентября 2008

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

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

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

Ответы [ 12 ]

16 голосов
/ 19 января 2010

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

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

У людей в этом проекте, похоже, были проблемы с "прижатием" извлеченных уроков, но когда у вас есть методы, возвращающие либо один элемент типа T, массив T, ErrorResult или ноль ... это становится довольно очевидным .

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

10 голосов
/ 07 сентября 2008

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

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

9 голосов
/ 30 октября 2008

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

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

class SimpleResults {
    def results
    def total
    def categories
}

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

.

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

7 голосов
/ 07 сентября 2008

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

6 голосов
/ 09 ноября 2008

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

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

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

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

4 голосов
/ 09 ноября 2008

ИМХО, преимущество утиной типизации возрастает, когда вы придерживаетесь некоторых соглашений, таких как согласование имен переменных и методов. Если взять пример из Ken G , я думаю, что это будет лучше всего читать:

class SimpleResults {
    def mapOfListResults
    def total
    def categories
}

Допустим, вы определили контракт для некоторой операции с именем «CalcuRating (A, B)», где 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, и A, и B должны реализовать какой-то интерфейс, который выглядит примерно так:

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

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

public long calculateRating(MyService A, MyServiceB);

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

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

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

3 голосов
/ 16 февраля 2010

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

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

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 из-за отсутствия статической проверки типов. При статической проверке типов нам нужно было бы убедиться, что SearchEngine был интерфейсом или, по крайней мере, абстрактным классом, и создать его полную имитационную реализацию. Это трудоемкий процесс, или вы можете использовать сложную специализированную среду для насмешек. Но типирование утки универсально и помогло нам.

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

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

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

2 голосов
/ 17 ноября 2013

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

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

против

//Dynamic typing
def someMap = [:]   
1 голос
/ 09 июня 2010

Мое мнение:

Динамически типизированные или утиные языки - это игрушки. Вы не можете получить Intellisense, и вы теряете время компиляции (или редактирование - при использовании РЕАЛЬНОЙ IDE, такой как VS, а не мусора, который другие люди считают IDE) проверки кода.

Держитесь подальше от каждого языка, который не является статически типизированным, все остальное - просто мазохизм.

1 голос
/ 07 сентября 2008

@ Крис Банч

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

Извините, но мне пришлось это сделать ...

...