Проектирование по контрактам и конструкторам - PullRequest
10 голосов
/ 04 мая 2010

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

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

        _array = new T[capacity];
    }

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

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

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

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

Спасибо

Ответы [ 5 ]

5 голосов
/ 05 мая 2010

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

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

1 голос
/ 29 июня 2010

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

Подведем итог:

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

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

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

1 голос
/ 06 мая 2010

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

Итак (например):

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

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

Вам необходимо четко указать все контракты; программа переписывания / статической проверки контрактов не «просматривает» метод, чтобы увидеть какие-либо последствия & mdash; см. мой ответ на связанный вопрос «Нужно ли указывать операторы Contract.Requires (…) избыточно при делегировании методов?»

0 голосов
/ 06 мая 2010

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

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

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

0 голосов
/ 04 мая 2010

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

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

НТН! Thomas

...