Каковы некоторые преимущества утиного набора текста по сравнению с обычным?статическая типизация?

StackOverflow https://stackoverflow.com/questions/47972

  •  09-06-2019
  •  | 
  •  

Вопрос

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

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

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

Это было полезно?

Решение

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

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

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

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

Другие советы

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

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

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

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

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

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

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

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

class SimpleResults {
    def results
    def total
    def categories
}

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

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

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

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

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

Вот один из сценариев, в котором «уточный набор текста» экономит работу.

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

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"
}

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

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

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

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

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

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

против

//Dynamic typing
def someMap = [:]   

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

@Крис Банч

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

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

Мое мнение:

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

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

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

Проблемы возникают, когда, как отмечали многие, вы начинаете с этим вести себя странно.Кто-то указал на функцию, которая возвращает один объект, коллекцию или значение NULL.Пусть функция возвращает определенный тип, а не несколько.Используйте несколько функций для одной и коллекции.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top