Дизайн контрактов и конструкторов

Я реализую свой собственный ArrayList в школьных целях, но к специи вещи немного я пытаюсь использовать Контракты Кода C# 4.0. Все были в порядке, пока я не должен был добавлять Контракты к конструкторам. Если я добавляю Контракт. Удостоверяется () в пустом конструкторе параметра?

    public ArrayList(int capacity) {
        Contract.Requires(capacity > 0);
        Contract.Ensures(Size == capacity);

        _array = new T[capacity];
    }

    public ArrayList() : this(32) {
        Contract.Ensures(Size == 32);
    }

Я сказал бы да, каждый метод должен иметь четко определенный контракт. С другой стороны, почему помещенный это, если это просто делегирует работу "основному" конструктору? Logicwise, я не должен был бы.

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

Кроме того, есть ли какие-либо книги вокруг того движения немного глубже на принципах и использовании Дизайна Контрактов? Одна вещь имеет знание синтаксиса того, как использовать Контракты на языке (C#, в этом случае), другой знает, как и когда использовать его. Я прочитал несколько учебных руководств и C# Jon Skeet подробно статья об этом, но я хотел бы пойти немного глубже, если это возможно.

Спасибо

10
задан Pascal Cuoq 5 May 2010 в 00:01
поделиться

5 ответов

Я совершенно не согласен с ответом Томаса. Раз уж вы делаете выбор в реализации ArrayList(), у вас должен быть контракт для него, документирующий этот выбор.

Здесь вы делаете выбор в пользу вызова главного конструктора с аргументом 32. Есть много других вещей, которые вы могли бы решить сделать (не только в отношении выбора размера по умолчанию). Предоставление контракта ArrayList(), который почти идентичен контракту ArrayList(int), свидетельствует о том, что вы решили не делать большинство глупых вещей, которые вы могли бы сделать вместо прямого вызова.

Ответ "он вызывает главный конструктор, так что пусть контракт главного конструктора делает свою работу" полностью игнорирует тот факт, что контракт существует для того, чтобы избавить вас от необходимости смотреть на реализацию. Для стратегии проверки, основанной на проверке утверждений во время выполнения, недостатком написания контрактов даже для таких коротких конструкторов/методов, которые почти напрямую вызывают другой конструктор/метод, является то, что в итоге вы проверяете все дважды. Да, это кажется избыточным, но проверка утверждений во время выполнения - это только одна из стратегий проверки, и принципы DbC не зависят от нее. Принцип таков: если метод может быть вызван, ему нужен контракт для документирования того, что он делает.

5
ответ дан 4 December 2019 в 02:49
поделиться

Хм, я не совсем понимаю, почему вы поместили 'Ensures' также в c'tor по умолчанию. Потому что он вызывает основной c'tor, который уже реализует полный контракт, c'tor по умолчанию делает это также - по определению. Так что это логическая избыточность, и поэтому большое "Не надо". Возможно, это может иметь прагматические последствия, как вы говорите - я не так хорошо знаю Code Contracts...

Что касается литературы - лучшие источники:

HTH! Томас

0
ответ дан 4 December 2019 в 02:49
поделиться

Клиентский код (использующий Code Contracts), который использует ArrayList, не будет знать, что пустой конструктор Ensures that Size == 32, если вы явно не укажете это с помощью Ensure.

Так (например):

var x = new ArrayList();
Contract.Assert(x.Size == 32)

выдаст предупреждение "утверждение не доказано".

Вы должны явно указать все контракты; программа переписывания/статической проверки контрактов кода не будет "просматривать" метод, чтобы увидеть любые последствия - см. мой ответ на смежный вопрос "Должны ли мы излишне указывать утверждения Contract.Requires(...) в делегирующих методах?"

1
ответ дан 4 December 2019 в 02:49
поделиться

Дизайн по контракту проистекает из математических корней функционального программирования: Предварительные условия и Постусловия .

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

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

0
ответ дан 4 December 2019 в 02:49
поделиться

Я рекомендую прочитать Построение объектно-ориентированного программного обеспечения, 2-е издание или, может быть, Touch of Class , оба от Бертрана Мейера. В качестве альтернативы вы можете прочитать статью Применение «Дизайн по контракту» от 1992 года того же автора.

Подводя итог:

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

Итак, в вашем случае сосредоточьтесь на инварианте. Создайте правильный объект (тот, который удовлетворяет инварианту класса), независимо от того, какой конструктор вызывается.

В этом ответе я обсуждал похожие темы, включая пример.

1
ответ дан 4 December 2019 в 02:49
поделиться
Другие вопросы по тегам:

Похожие вопросы: