Write Cleaner, Higher Quality Code with Class Contracts in Delphi Prism

By: Nick Hodges

Abstract: This article covers the language features of Class Contracts in Delphi Prism. With these features, you can write “cleaner”, more concise code by stating what are the assumptions and conditions for classes and methods in your Prism Code

    Introduction

Delphi Prism is Embarcadero’s new .NET development tool which uses the Delphi language to give developers access to the complete .NET 3.5 framework. With Delphi Prism, Delphi developers can use their favorite language to develop Winforms, ASP.NET, Silverlight, and Windows Presentation Foundation (WPF) applications. Delphi Prism also introduces a number of powerful language features that make coding in Delphi easier and more powerful than ever. One of those features is Class Contracts.

    Class Contracts

Class Contracts is a programming technique that allows developers to define specific conditions and states that must be true before code is run. It also allows developers to define states that must always be true within a given class. Class Contracts allow for a cleaner separation of concerns by providing a “contract” section of code that defines states that must always be true before a specific code block can be executed. Often, developers must write a lot of “defensive” code to ensure that code can run properly, and Class Contracts makes this easier and cleaner within code.

For instance, it is frequently wise to check if an object reference is valid before using that reference. This can often involved injecting “protection code” into a code block which can distract from the actual code that is being run. Class Contracts allow the developer to define the required state in a separate code block, and then write code in the main code block with the assurance that the proper state has been met.

In addition, once the main code block has been run, Class Contracts can ensure that the resulting state is correct. For instance, if a function returns a valid instance of an object, the developer can define a “contract” in code that states that the result of that function must be a valid instance and not be equal to nil.

It should be noted that the concept of Class Contracts (sometimes called “Programming by Contract” or “Design by Contract”) is a concept first put forth by Bertrand Meyer in the Eiffel language. It should also be noted that the term “Design by Contract” is a registered trademark of Interactive Software Engineering, Inc.

Delphi Prism provides language level support for Class Contracts by providing require and ensure clauses within methods, and by allowing for the definition of invariants for class fields.

    A Simple Class Contracts Example

Consider the following code:

procedure SomeClass.AddToStringBuilder(aStringBuilder: StringBuilder; aString: Text);
begin
if (aStringBuilder <> nil) and (aString <> ‘’) then
begin
  aStringBuilder.Append(aString);
end;
end;

In this admittedly simple example, the code conducts two checks before anything happens: it ensures that the StringBuilder parameter is not nil and that the string parameter actually has some content in it. Most developers write a lot of code just like this. However, such code mixes concerns between defensive, error checking code and the actual code that gets executed.

Delphi Prism provides language features that make writing code like this much “cleaner”. The require and ensure keywords allow developers to place protection code in separate code blocks from the “working” code. Require defines a set of pre-conditions that much be met before code is executed, and ensure defines post-conditions that must be true after the code is run. So, given the above, the following might be the process one would go through to write the above procedure using Class Contracts.

    Defining the Contract

First, the developer would determine what the needed states would be before and after the actual code is written. Thus, a Prism developer might do the following:

procedure SomeClass.AddToStringBuilder(aStringBuilder: StringBuilder; aString: Text);
require
aStringBuilder <> nil;
aString <> ‘’;
begin
// Main code block will go here

ensure
  aStringBuidler <> nil; // Be sure the StringBuilder hasn’t been set to nil
  aStringBuilder.Length > 0; // Be sure the StringBuilder contains text
end;

Much as test-driven development requires a developer to write tests before implementation, Class Contracts allow for the contract to be written before the implementation. In this case, the require clause includes two Boolean statements that must be true before the main code block in the procedure will execute – requiring that aStringBuilder is a valid reference, and that aString isn’t an empty string. That is, the require clause will guarantee that the application behaves properly – for example, by providing proper input via parameters -- before executing the main code block.

And then, once the main code block has executed, the ensure section will, well, ensure that aStringBuilder still is valid and that it actually contains some text. That is, the ensure clause will provide a guarantee that the main code block will do and/or won’t do certain things required by the application as a whole.

That becomes the contract definition for this procedure. If any part of the contract is broken, an assertion will be raised. During testing and development, tests should be run that require that such assertions are never raised, and if they are, the code should be rewritten so that the contract is always fulfilled. Note that Class Contracts are really a code testing and development feature, and that code with Class Contracts should not be released for general use with Assertions turned on.

    Writing the Code

Once the contract is defined, then the main code block can be written:

procedure SomeClass.AddToStringBuilder(aStringBuilder: StringBuilder; aString: string);
require
aStringBuilder <> nil;
aString <> ‘’;
begin
aStringBuilder.Append(aString);
ensure
  aStringBuidler <> nil; // Be sure the StringBuilder hasn’t been set to nil
  aStringBuilder.Length > 0; // Be sure the StringBuilder contains text
end;

The developer can write the code block knowing that the contract has been fulfilled by the require clause, and that an assertion will be raised if his code does something to violate the contract. As the code is developed and tested, if errors are caused by certain situations that are not defined by the contract, additional items can be easily added to the contact to ensure that correct code is written and that incorrect data and states never occur.

The syntax for the require and ensure clauses has an additional feature which allows for custom error descriptions to be passed to the assertion. Thus, the above code might be written as follows:

procedure SomeClass.AddToStringBuilder(aStringBuilder: StringBuilder; aString: string);
require
aStringBuilder <> nil: ‘The aStringBuilder parameter cannot be nil’;
aString <> ‘’: ‘The aString parameter must contain at lease on character’;
begin
aStringBuilder.Append(aString);
ensure
  aStringBuilder <> nil: ‘aStringBuilder should not be set to nil’;
  aStringBuilder.Length > 0: ‘aStringBuilder should always have some text.’;
end;

This way, when the assertion is raised the custom message contained in the string after the contract item will be included as explanation.

    Running the Code

So now that the method has been written with a contract embedded within it, a developer can be confident that the code will run only when the necessary conditions have been met. The question then becomes -- what would happen if the conditions are not met?

Consider the following code that calls the routine written above:

method MainForm.button1_Click(sender: System.Object; e: System.EventArgs);
var 
  SB: StringBuilder;
begin
  AddToStringBuilder(SB, 'This will violate the contract');
end;

This code will pass a nil pointer into the AddToStringBuilder procedure, a clear violation of the method’s contract. The result is an Assertion exception:

Hide image
Click to see full-sized image

Note that the error message from the require clause is included in the Assert message.A similar message would result if the code were to pass in an empty string in the aString parameter.

This feature is mainly a development and testing feature. Assertions will normally be turned off when an application is deployed. But during the testing and development phase, the contracts will provide immediate feedback to any problems that are caused by incorrect code. Such changes can be made via the Build Configuration options in the IDE.

    Class Invariants

Another aspect of Class Contracts is the notion of Class Invariants. Classes will usually hold data, and sometimes that data needs to fall into specific limitations or be restricted in some way. Class invariants enable the developer to declare states or restrictions -- i.e. “contracts” – that cannot be violated any time during the lifetime of a class instance.

Consider the following simple class declaration:

type
  TCustomerWithInvariants = class
  private
    FCustomerAge: Integer;
  private invariants
    FCustomerAge < 120;
  public
    constructor Create;
    property CustomerName: string;
    property CustomerAge: Integer read FCustomerAge write FCustomerAge;
  end;

This simple class keeps track of a customer’s name and age. It also has a private invariants section that states that a customer’s age can never equal or exceed 120. Invariants, like entries in the require and ensure sections, are simple boolean expressions that define states and settings that are required and/or forbidden for a class.

Once an invariant has been declared, the class will monitor all setting to the field or property in question and raise an assertion if the invariant is violated. This ensures that your classes always remain in a proper state.

Thus, if you were to execute the following code:

method MainForm.button3_Click(sender: System.Object; e: System.EventArgs);
var  
  Customer: TCustomerWithInvariants;
begin
  Customer := TCustomerWithInvariants.Create;
  Customer.CustomerAge := 554;
  textBox1.Clear;
  textBox1.Text := 'Customer Age: ' + Customer.CustomerAge.ToString;
end;

an Assertion exception will be raised when the CustomerAge property is set to the Mathuselean age of 554. Class Invariants work in the same was as require and ensure clauses, by raising assertions when the “promises” are broken. They allow you to define and then test your classes to ensure that they never hold data that doesn’t meet the needs or requirements of a class.

    Conclusion

Class Contracts are an effective way to write clean code with fewer errors. By designing your methods and classes with a contract first, the code you write can be cleaner and less cluttered, safe in the knowledge that it will be executing in an environment guaranteed by the contracts and invariants you define for it. This language feature is built directly into Delphi Prism, and gives it an advantage over the other popular .NET development languages.

Server Response from: ETNASC02