Perfect Developer tutorial This page last modified 2007-8-15 (JAC)


please use the Back button in your browser to return

Search using Google:

Abstraction means extracting the essential features of something without including unnecessary detail. In the context of object oriented development, we design classes so that clients of a class do not need to know about the detailed implementation of the class.

Perfect supports this kind of abstraction in specifications as well as implementations. Although it is sometimes possible to define a class adequately by its behaviour alone, more often it is necessary to have some model of the data stored by the class in order to understand what the class is for. The class methods are specified in terms of this abstract data model (although naturally, clients of the class do not have direct access to it). This leaves the way open to refining the abstract data model to a more efficient implementation model.

If an object has an alias, two or more paths to it exist. It may not be apparent that multiple names do in fact refer to exactly the same object.

This source of confusion can lead to unexpected results. For example, if two variables X and Y both refer to the same object, then if a member method is called on X to update the object, the change will also be visible to Y. Sometimes this is exactly what we want; but it is also a frequent source of error.

assertions and post-assertions
An assertion is a declaration of some condition that must hold at that point in an implementation.

A post-assertion is a declaration of some condition that must hold when a method or constructor completes. (Some authors would call this a postcondition, but we have a stronger meaning for that term. See below for the differences.)

class invariant
A class invariant is a condition that holds for all instances of a class, all (or nearly all) the time.

A constructor is a function provided to instantiate an object, i.e. to create the object and initialize it.

In Perfect, constructors are declared using the keyword build. The parameter list is enclosed in curly brackets to reflect the syntax of a constructor call. You never omit "{}" even if the constructor parameter list is empty.

Constructors have no side-effects, so changeable parameters are not permitted.

Constructors may have preconditions, but constructor preconditions cannot refer to the current object, because there isn't one at the start of the call. It follows that a constructor which takes no parameters cannot have a meaningful precondition.

dynamic binding
Dynamic binding means that where you are using polymorphism and you call a method, the "version" of the method that actually gets called depends on the actual type(s) of the instance(s) involved.
For example, suppose you declared BankAccount and then SavingsAccount, but in declaring SavingsAccount you redefined the inherited method withdraw. This gives you two versions of withdraw. Then, each time a call is made to myAccount.withdraw(someMoney), the version of withdraw that is called will depend on whether myAccount holds a plain BankAccount, or a SavingsAccount.

You might know this under the name of "information hiding".
The idea is that data belonging to an object is under the control of the methods (also known as functions) of that object, and may not be accessed in any way other than by those methods. The implementation details of the methods are not generally known.

What is generally known is what each method does, and the prototype for each method. So the developer using a method knows what parameters to pass to it, and what sort of result will be returned, and any side-effects that it may have.

The developer does not know, and does not need to know, how the method achieves its purpose. The data belonging to each object is only accessible by using these methods - hence it is "encapsulated".

A function in Perfect is a method returning one (or more) results, and having no side-effects.

ghost methods
who-oo-oo says it's not?

This is not about haunted houses!

A ghost (whatever) in Perfect is one which has no executable code.

Perfect allows ghost methods, ghost operators, ghost selectors, and ghost schemas.

So what good is something with no executable code, then? Well, for instance, when defining functions to represent abstract properties, it sometimes happens that the property is only used in declaring preconditions, postconditions, class invariants, etc.

Inheritance implies child objects that inherit the attributes of a parent object.


Polymorphism is the ability of different objects to respond differently to the same message.

A postassertion is different from a postcondition. A postassertion specifies everything that the client can expect to be true when a method finishes, even in the presence of polymorphism, when different objects may be responding differently to the same message.

A postondition, in Perfect, says precisely which variables have changed, and what conditions are satisfied by the final values of those variables. Perfect postconditions must be complete in every way.

A precondition is a condition which must be met before a method is called.

In Perfect, a schema is a method that changes something, either the current object, or a parameter.

A side-effect is an effect of calling a method other than the obvious one. For example, in most programming languages, calling a function to obtain a result may have the side-effect of changing some data.

Side effects are sometimes used deliberately; but are better avoided if possible, as they can make a program harder to understand. In Perfect, functions and constructors may not have side-effects.

validation and verification
These are not the same thing.

Validation means checking that the specification of the software system under development conforms with the user's requirements.

Verification, on the other hand, means checking that the system performs in accordance with its (valid) specification.

Perfect Developer can validate your specification (provided that you correctly ascertained and expressed the user's requirements) and can verify that the implementation will conform to the specification.

System-level testing is normally still needed to ensure there were no requirements-gathering errors at the beginning of the development process and no hardware or back-end compiler bugs at the other end.

Unit-level testing is normally regarded as unnecessary when implementations are mathematically validated against specifications and requirements.

Escher Technologies home