Подтвердить что ты не робот

Каковы преимущества утиной печати или статической типизации?

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

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

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

4b9b3361

Ответ 1

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

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

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

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

Ответ 2

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

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

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

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

Ответ 3

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

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

Ответ 4

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

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

class SimpleResults {
    def results
    def total
    def categories
}

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

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

Ответ 5

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

Ответ 6

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

class SimpleResults {
    def mapOfListResults
    def total
    def categories
}

Скажем, вы определяете контракт на какую-либо операцию под названием "calculateRating (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, например). Противоположным примером этого является Поднимите фреймворк, где они говорят, что подсчет SLOC рамки похож на Rails, но тестовый код имеет меньше строк, потому что им не нужно выполнять проверки типов в рамках тестов.

Ответ 7

Здесь один сценарий, в котором печатание утка сохраняет работу.

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

class BookFinder {
    def searchEngine

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

Теперь для unit test:

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

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

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

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

Ответ 8

Мое мнение:

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

Следите за всеми языками, которые не статически типизированы, а все остальное - просто мазохизм.

Ответ 9

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

Ответ 10

@Chris Bunch

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

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

Ответ 11

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

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

против

//Dynamic typing
def someMap = [:]