Sunday, May 20, 2018

Method implementation

The methods are implemented within the Implementation section of the pascal file (see  The structure of a Pascal file).

The implementation
To generate an empty implementation from the method definitions press Ctrl + Shift + C.
The generated method looks like this:

You see that that method name has been prefixed by MyClass. and a begin / end; block is appended.
The MyClass. prefix tells the compiler to which class the method belongs - this is needed because you can define more than one class in the file.

Between the begin and end; statements you can write your implementation, by example when this method has to:
  - return the string representation of the result of 100 divided by the integer parameter
  - but only when the parameter is bigger than zero (otherwise a custom error is thrown)
  - and only when the result is bigger than 10 (otherwise a custom error is thrown)

The implementation will be:

The first line checks if p is bigger than zero, and if it is, the second line is executed where the calculation is done. The third line checks if the result is bigger than 10 and if so, the fourth line is executed where the string representation of the result is returned to the caller.
When the result was smaller than 10 or when the parameter smaller or equal to zero, a custom exception (error) is thrown.

Note that, when you use the keyword Exit, the function exits immediately - any code following it won't be executed anymore. And when an exception is raised, the method is also immediately terminated - without result, and the exception should be catched by an error handler that is defined in the calling chain - if not, the program will terminate with this exception.
Exception handling is a topic for a future post where I will explain it further.

When the check p > 0 is a requirement to run this method, in other words when a call to this method with a value of p <= 0 is considered as a bug then a contract should be used.

A contract specifies the expected state of the parameters (and or other class states) that are required to run this method. The example above with an implemented contract for the parameter p looks as following:
When a debug build is executed, the "require" check is executed to find the bugs during development; an assertion is raised when the method is called with invalid parameters or when it is called in combination with a class state that does not allow this.

If a result smaller or equal to 10 is also considered a bug then we can check this in a contract too:
The "ensure"check is executed before the method returns to the caller and an assertion is raised when the check do no pass. And as you see, the resulting code is very small and readable.

In this example I use the result variable instead of Exit. The result variable is always available in any function and has the same type as the return type of the function.
When the function completes without an exit statement, the value of the result variable is returned to the caller.

Because the result will be smaller than 10 as soon as p > 10, we could also have written:

You see that the boolean check in the require contract exists out of 3 values instead of 2 as normal. This is the way you can write a boolean between test anywhere in Oxygene.

One thing that I did not show is that you can use the old value of the parameters (this is the value that the parameters had on method entry) and also use the current value of these parameter, making the following check  possible:

You can add as may checks as you want to both the require and ensure checks - all checks must be in the form of a boolean expression and an assertion is raised when one or more checks do not pass.

The require and ensure checks are not executed in a release build making sure that the checks do no slow down the released version, but are only used during the development and debug stage.

I personally find the contracts really awesome!
Next post will be about the Field definitions and constants - see you there.

No comments:

Post a Comment