Статическая / строгая типизация и рефакторинг

Мне кажется, что самое ценное в статическом / строго типизированном языке программирования - это то, что он помогает рефакторингу: если / когда вы измените какой-либо API, компилятор скажет вам, что это изменение нарушило.

Я могу представить себе написание кода на языке времени выполнения / слабо типизированном языке ... но я не могу представить рефакторинг без помощи компилятора, и я не могу представить, как писать десятки тысяч строк кода без рефакторинга.

Это правда?


person ChrisW    schedule 19.05.2009    source источник
comment
Вы бы все равно провели рефакторинг, вы просто использовали бы модульные тесты в качестве замены IDE / компилятора. Это действительно не что плохо, если к этому привыкнуть.   -  person Sasha Chedygov    schedule 19.05.2009


Ответы (5)


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

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

Основное ограничение статических типов заключается в том, что они ограничены в ограничениях, которые они могут выражать. Это зависит от языка: большинство языков имеют относительно простые системы типов (c, java), а другие - чрезвычайно мощные системы типов (haskell, cayenne).

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

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

Что касается вашего второго вопроса:

Как мы можем безопасно повторно использовать типизированный язык среды выполнения?

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

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

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

person Jason Watkins    schedule 19.05.2009
comment
Не могли бы вы подробнее рассказать о том, чего мне действительно не хватает в системах статического типа? Насколько я могу судить, я не чувствую недостатка, который можно было бы приписать системе типов, как и с тех пор, как я изучал C ++ несколько лет назад. Я вполне мог что-то упустить и не замечать, и в этом случае было бы очень ценно, если бы вы мне на это указали. - person John Saunders; 19.05.2009
comment
@John: Кто-то из Lambda the Ultimate мог бы ответить вам лучше, но, исходя из моего ограниченного понимания, похоже, что большое слабое место в типизации C ++ - это ограничения на агрегаты. Взлом шаблонов C ++ а-ля alexandrescue может увести вас довольно далеко, но я думаю, что есть еще немало кода, связанного с контейнером / массивом, который прибегает к проверкам во время выполнения. Например, что, если я хочу ограничить форму вектора? Или ограничить длину строки диапазоном? Зависимые типы могут выражать такие вещи. - person Jason Watkins; 19.05.2009
comment
Я не использовал язык с «зависимыми типами» ... мое понятие «типизация во время выполнения» больше похоже на JavaScript! - person ChrisW; 19.05.2009
comment
Спасибо, что познакомили меня с идеей «мутационного тестирования». - person ChrisW; 19.05.2009

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

Тем временем вы можете проверить тип передаваемой переменной и каким-то образом потерпеть неудачу, если это не ожидаемый тип. Вам все равно придется запускать свой код, чтобы искоренить эти случаи, но по крайней мере что-то вам скажет.

Я думаю, что внутренние инструменты Google действительно выполняют компиляцию и, вероятно, проверку типов в своем Javascript. Хотел бы я иметь эти инструменты.

person Matthias Wandel    schedule 19.05.2009
comment
Разве вы не говорите в основном о Google Web Toolkit (code.google.com/webtoolkit)? - person Matthew Flaschen; 19.05.2009
comment
В самом деле, существует ли такая вещь, как программа, которая продолжает работать, если типы данных изменяются нетривиальным образом без фактического изменения кода? Да, на любом языке, поддерживающем структурную типизацию (также известную как утиная типизация), вы часто увидите это. Это не обязательно статично или динамично. Например, универсальные функции C ++ заботятся только о конкретных аспектах используемых ими типов, поэтому возможно множество изменений типов аргументов, которые не требуют изменения функции. - person Jason Watkins; 19.05.2009

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

Я не считаю отсутствие статических типов проблемой для рефакторинга. Я считаю проблемой отсутствие рефакторинга браузера. Проблема динамических языков состоит в том, что вы не знаете, что код действительно будет делать, пока вы его не запустите. У Perl этого больше, чем у большинства. У Perl есть дополнительная проблема, связанная с очень сложным, почти не поддающимся анализу синтаксисом. Результат: нет инструментов для рефакторинга (хотя над этим работают очень быстро). В конечном итоге мне нужно провести рефакторинг вручную. И это то, что приводит к ошибкам.

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

Статические типы, как это предусмотрено в Java, C ++ или C #, на самом деле решают только небольшой класс проблем программирования. Они гарантируют, что в ваши интерфейсы передаются биты данных с правильной меткой. Но то, что вы получаете Collection, не означает, что Collection содержит данные, которые, как вы думаете, содержат. То, что вы получили целое число, не означает, что вы получили правильное целое число. Ваш метод принимает объект User, но вошел ли этот пользователь в систему?

Классический пример: public static double sqrt(double a) - это подпись для функции квадратного корня Java. Квадратный корень не работает с отрицательными числами. Где это написано в подписи? Это не так. Хуже того, где говорится, что эта функция вообще делает? Подпись только говорит, какие типы она принимает и что возвращает. Он ничего не говорит о том, что происходит между ними и где живет интересный код. Некоторые люди пытались получить полный API, используя дизайн по контракту, который в широком смысле можно охарактеризовать как встраивание тестов времени выполнения для входных и выходных данных вашей функции и побочных эффектов (или их отсутствия) ... но это уже другое шоу.

API - это гораздо больше, чем просто сигнатуры функций (в противном случае вам не понадобилась бы вся эта описательная проза в документации Javadocs), а рефакторинг - это гораздо больше, чем просто изменение API.

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

person Schwern    schedule 20.05.2009
comment
API - это гораздо больше, чем просто сигнатуры функций ... Если я изменю значение метода, не меняя его сигнатуры, я могу просто изменить имя метода: тогда компилятор поможет / потребует от меня найти все места в остальном программы, которые используют старое имя и ожидают старого значения. - person ChrisW; 20.05.2009

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

person Andy White    schedule 19.05.2009

Одним из преимуществ использования var в C # 3.0 является то, что вы можете часто изменять тип, не нарушая кода. Тип должен по-прежнему выглядеть одинаково - свойства с одинаковыми именами должны существовать, методы с такой же или похожей сигнатурой должны существовать. Но вы действительно можете перейти на совершенно другой тип, даже не используя что-то вроде ReSharper.

person John Saunders    schedule 19.05.2009