Design by contracts and constructors
Asked Answered
D

5

10

I am implementing my own ArrayList for school purposes, but to spice up things a bit I'm trying to use C# 4.0 Code Contracts. All was fine until I needed to add Contracts to the constructors. Should I add Contract.Ensures() in the empty parameter constructor?

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

        _array = new T[capacity];
    }

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

I'd say yes, each method should have a well defined contract. On the other hand, why put it if it's just delegating work to the "main" constructor? Logicwise, I wouldn't need to.

The only point I see where it'd be useful to explicitly define the contract in both constructors is if in the future we have Intelisense support for contracts. Would that happen, it'd be useful to be explicit about which contracts each method has, as that'd appear in Intelisense.

Also, are there any books around that go a bit deeper on the principles and usage of Design by Contracts? One thing is having knowledge of the syntax of how to use Contracts in a language (C#, in this case), other is knowing how and when to use it. I read several tutorials and Jon Skeet's C# in Depth article about it, but I'd like to go a bit deeper if possible.

Thanks

Dy answered 4/5, 2010 at 16:51 Comment(3)
related: #2539997Prud
You could get rid of the "Contract.Requires(capacity > 0);" if you make the c'tor take a uint opposed to an int. I try to use Contracts as a last resort, where the language limits your ability to let the next developer know what you were thinking when you created the code in the first place. If you decide to keep the contract, I would write "Contract.Requires(capacity >= 0);" for one should always be able to construct an empty data structure, and then have the option to add objects later.Donnadonnamarie
"...in the future we have Intelisense support for contracts." The future is now! visualstudiogallery.msdn.microsoft.com/…Donnadonnamarie
B
5

I completely disagree with Thomas's answer. As long as you are making choices in the implementation of ArrayList(), you should have a contract for it that document these choices.

Here, you are making the choice of calling the main constructor with argument 32. There are many other things that you could have decided to do (not just concerning the choice of the default size). Giving a contract to ArrayList() that is almost identical to that of ArrayList(int) documents that you decided not to do most of the silly things you could have done instead of calling it directly.

The answer "it calls the main constructor, so let the main constructor's contract do the job" completely ignores the fact that the contract is there to save you from having to look at the implementation. For a verification strategy based on run-time assertion checking, the disadvantage of writing contracts even for such short constructors/methods that almost directly call another constructor/method is that you end up checking things twice. Yes, it seems redundant, but run-time assertion checking is only one verification strategy, and DbC's principles are independent from it. The principle is: if it can be called, it needs a contract to document what it does.

Betray answered 4/5, 2010 at 23:59 Comment(1)
Yes; and if you use the static checker it will tell you this :)Prud
P
1

Client code (using Code Contracts) that uses ArrayList won't know that the empty constructor Ensures that Size == 32 unless you explicitly state so using an Ensure.

So (for example):

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

will give you the warning "assert not proven".

You need to explicitly state all contracts; the code contracts rewriter/static checker won't "look through" a method to see any implications — see my answer to the related question "Do we have to specify Contract.Requires(…) statements redundantly in delegating methods?"

Prud answered 5/5, 2010 at 22:55 Comment(0)
A
1

I recommend reading Object Oriented Software Construction, 2nd Edition, or maybe Touch of Class, both from Bertrand Meyer. Alternatively, you could read the 1992 article Applying "Design by Contract" from the same author.

To summarize:

  • The class invariant must hold after the constructor (any of them) finishes, and before and after any public method of the class is executed.
  • Method preconditions and postconditions are additional conditions which must hold on entering and exiting any public method, along with the invariant.

So in your case, focus in the invariant. Produce a correct object (one which satisfies the class invariant), no matter which constructor is invoked.

In this related answer I discussed similar topics, including an example.

Aussie answered 29/6, 2010 at 8:18 Comment(0)
E
0

Umh, I don't fully understand why you put the 'Ensures' also in the default c'tor. Because it calls the main c'tor, which already does implement the full contract, the default c'tor does that as well - by definition. So this is a logical redundancy, and therefore a big 'Don't'. Maybe it could have pragmatic implications, like you say - don't know Code Contracts that good...

Regarding literature - the best sources are:

HTH! Thomas

Ecclesiastes answered 4/5, 2010 at 17:19 Comment(5)
Assuming one day Intelisense fully supports Code Contracts(or assuming that as of today, we will look to XML contracts documentation), explicitly stating the contract of the no-args constructor would have the benefit of allowing the client to know the contract. If you leave it empty you can't. And that's the reason I created this post.Dy
I understand the rationale, and maybe this will become true one day... But doing sth. because there possibly could be benefits in the future turned out to be one of the worst (and most expensive) things ever in software development. Always do the simplest thing that does the job!Ecclesiastes
Yes, you've got a point. What if Intelisense supported this feature as of today? Would it then make sense to put that Contract in the no args constructor?Dy
Sure, it would make sense in a way then. But the reason for such code to exist would be a non-functional requirement (which is perfectly ok), and this IMHO should be commented in source code, to not confuse other developers.Ecclesiastes
I think that here, it's worth noting that you can actually include the contracts into SandCastle documentation with the included XSL transforms -- then they are visible to other developers.Prud
F
0

Design by contract comes from the mathematical roots of functional programming: Preconditions and Postconditions.

You don't really need a book on it, it's at most a chapter of a Computer Science degree (most will teach the concept). The basic premise is you write the preconditions that the function expects and the output it will produce given the correct parameters. The function will not be expected to work with incorrect initial parameters. The same can be said for an algorithm: it's infallible, that is it's guaranteed to provide the expected result.

That's how I've been taught it in the degree I'm currently studying, there may be better definitions around though. The Wikipedia article on Design by contract is written with an OO slant, but pre/postconditions are language independent.

Flatto answered 5/5, 2010 at 23:17 Comment(3)
Yes, my point is that on paper OO design hasn't that much to learn too. You learn about classes, inheritance and encapsulation and it seems like it ain't much more to learn. But in practise, there are tons of books on how to make good OO programs. Sometimes knowing the syntax and "theory" is not enough, just that.Dy
Those are probably the Wrox and apress books which are about practicality :) A lot more depth is taught in a (decent) degreeFlatto
I don't have a degree, so what do I do?Dy

© 2022 - 2024 — McMap. All rights reserved.