суббота, 17 ноября 2007 г.

Проектирование по контракту (часть 1ая)

Now playing on iTunes: Fleur - Сегодня
В настоящее время я занимаюсь попыткой "внедрить" проектирование по контракту в .NET (на примере языка C#). Внедрить я взял в кавычки неспроста, дело в том, что под внедрить понимается не как непосредственное внедрение проектирования по контракту в синтаксис языка (ну или в саму среду .NET), больше имеется в виду внедрение идеи проектирования по контракту в сам процесс написания приложений на .NET. В связи с этим, хотел бы поделится некоторыми идеями и наблюдениями, но о всем по порядку...
Наверное сначала стоит уточнить, что вообще такое проектирование по контракту (знающие могут пропустить абзац ;). Если совсем кратко, то это описание некоторых утверждений, которые должны выполняться в определенный момент работы программы. Например есть некоторые ограничения на входные параметры метода (предусловия), если они будут выполнены при вызове метода, то гарантируется что и сам метод выполнится успешно, кроме того будут выполнены ограничения наложенные на выходные параметры метода (постусловия), если они конечно есть. Ну например для метода деления одного числа на другого: "второе число не должно быть равно нулю, в таком случае деление произойдет". Или для того же метода деления чисел: "если оба числа положительные, то деление произойдет и результат будет положительным". Ну и т.д. Все это называется контрактом (контракт на метод). Кроме контрактов есть так называемые инварианты класса (вообще говоря, это может быть и интерфейс) - это некие утверждения (как правило затрагивающие поля и свойства), которые должны удовлетворятся на протяжении всей жизни экземпляра этого класса (в теории их проверка обычно происходит перед вызовом метода и после его выполнения, т.е. в пред и пост условиях). Примером инварианта для какой нибудь коллекции может служить такое вот утверждение: "количество элементов в коллекции больше нуля". Ну я, надеюсь, что вкратце ясненько, хотя, быть может и не по научному ;)
Для чего вообще используется идея проектирования по контракту? В первую очередь для повышения надежности разрабатываемого продукта (приложения, кода, "черти что и с боку бантик" - для кого что ;). Естественно, что идея проектирования по контракту не на столько нова, а значит и для C# (а он тоже уже не так нов) уже что то придумано (стоит заметить, что в наше время вообще сложно быть в чем то первым, как правило получается только снова изобрести уже существующий велосипед, но это так - лирическое отступление). Существующими и , в принципе, работоспособными примерами чего то придуманного служат, например, Spec# и eXtensible C#. Расскажу про них совсем немного ;)
Spec# представляет собой некое расширение синтаксиса языка C#. При таком подходе можно написать предусловия, постусловия и инварианты (далее будем называть все это прелестями для краткости ;) прямо в коде - прямо как в теории проектирования по контракту. При этом компилятор дополнительно "что то там" проверяет на этапе компиляции на основе этих самых прелестей, что, естественно позволяет выявить ошибки еще на этапе разработки. Так же компилятор на основе все тех же прелестей добавляет ряд проверок в код, которые будут работать во время исполнения. Ну и, естественно для всего этого есть некая среда разработки в виде расширения Visual Studio, что бы жизнь казалась медом :) Не смотря на все, вроде как, имеющиеся плюсы, есть один минус (который может все перевесить) - Spec# всетаки не C#, т.е. другой язык программирования. Ну так же, по моему, не имеется уже привычного нам всем IntelliSense ;).
Проблему "другого" языка может решить eXtensible C#, который представляет собой немного другой подход к проблеме внедрения проектирования по контракту. Во-первых, это набор атрибутов, с помощью которых (вроде как) можно описать наши прелести. Во-вторых, это некое расширение компилятора, которое на основе соответствующих атрибутов вставляет в код дополнительные проверки. Тут может возникнуть вопрос - в чем же отличие, если eXtensible C# это тоже расширение компилятора? На самом деле в том, что код написанный с применением eXtensible C# можно скомпилировать и обычным компилятором, в таком случае просто атрибуты eXtensible никак не повлияют на скомпилированный код (просто останутся в метаданных).
Тут я сделаю некое лирическое отступление про то, как все это работает. На самом деле с eXtensible C# компиляция проходит в два прохода: сначала проект собирается обычным компилятором, а затем компилятор eXtensible C# загружает созданную сборку, через рефлексию анализирует свои атрибуты, на их основе "подправляет" код (вставляя проверки) и снова все компилирует (ну в случаи неудачи выводит стандартные ошибки компиляции). Соотвественно, за счет этого eXtensible C# легко отключается, ну а за счет атрибутов достигается обратная совместимость со стандартным компилятором (если это можно назвать совместимостью, лучше сказать так - eXtensible C# использует то, что никак не мешает обычному компилятору ;).
Стоит сразу сказать, что в eXtensible C# так же не обошлось без минусов. Во-первых, из-за применения атрибутов нет никаких проверок во время разработки (ошибки вскрываются во время компиляции, ну или после ;). Во-вторых, из-за применения атрибутов, писать утверждения довольно проблематично, точнее сказать - в атрибут они передаются как самая обычная строчка ("count > 0") - наверное, отсюда, кстати и берется первый минус , т.к. во время передачи строчки никак нельзя узнать, правильно ли она написана, только во время компиляции (другими словами забудьте про Intellisense ;). В третьих, никак не используются инварианты классов (кстати, может быть их нет и в Spec#...). В четвертых, eXtensible C# применим (как видно из названия, кстати) только для C# - другими словами, для других языков программирования данные атрибуты являются просто пустышкой.
Есть и немного другие подходы (например кто то советует просто использовать класс System.Diagnostic.Debug с его методом Assert). Подведя итог можно сказать следующее: велосипеды уже есть, но ездить на них не так уж и удобно (еще лучше сказать - ездить конечно можно, но только при явном желании). Вопрос в другом - можно ли изобрести что то лучше? В общем, если кто то найдет ответ - скажите мне ;) А я по старинке буду пока что просто пробовать изобрести очередной велосипед. Кстати, наконец то настало время рассказать про свои замечания... Но уже, наверное, не сегодня ;)
P.S. А пока можете комментировать - ваше мнение мне очень интересно ;)

3 комментария:

  1. Пост немного для новичков получился ;) Могу задать пару наводящих воспрос, над которыми размышлял в свое время:
    1) Чем DBC отличается от TDD (и отличается ли)?
    2) DBC и ООП:
    - Как эти концепции вообще соотносяться?
    - Что нового добавляет DBC в оо-анализ и -проектирование?
    3) Как DBC соотноситься с defensive programming?
    4) DBC и верификация программ - одно и тоже?
    5) DBC и коммандная разработка.
    6) DBC и процесс разработки в целом: то есть, в каком порядке, что делать.
    7) DBC и рефакторинг: стоимость поддержки проверочных условий.
    8) Отрицательные стороны DBC.

    Пока все что пришло в голову.

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

    ОтветитьУдалить
  3. Ждем: DBC. Эпизод второй. Скрытая угроза :)

    ОтветитьУдалить