mirror of
https://github.com/EiffelSoftware/eiffel-org.git
synced 2025-12-07 15:22:31 +01:00
Author:halw
Date:2008-10-22T21:23:04.000000Z git-svn-id: https://svn.eiffel.com/eiffel-org/trunk@94 abb3cda0-5349-4a8f-a601-0c33ac3a8c38
This commit is contained in:
@@ -32,7 +32,7 @@ For the classes using it, <code>console</code>, although a function, looks very
|
||||
|
||||
The "Hello World" system at the beginning of this discussion (section [[4 Hello World|4]] ) used an output instruction of the form <code>io</code>. <code>put_string ( Some string )</code>. This is another example of the general scheme illustrated by <code>console</code>. Feature <code>io</code>, declared in <code>ANY</code> and hence usable by all classes, is a once function that returns an object of type <code>STANDARD_FILES</code> (another Kernel Library class) providing access to basic input and output features, one of which is procedure <code>put_string</code>. Because basic input and output must all work on the same files, <code>io</code> should clearly be a <code>once</code> function, shared by all classes that need these mechanisms.
|
||||
|
||||
==Constant and unique attributes==
|
||||
==Constant attributes==
|
||||
|
||||
The attributes studied earlier were variable: each represents a field present in each instance of the class and changeable by its routines.
|
||||
|
||||
@@ -45,15 +45,6 @@ These will have the same value for every instance and hence do not need to occup
|
||||
|
||||
What comes after the <code>=</code> is a manifest constant: a self-denoting value of the appropriate type. Manifest constants are available for integers, reals (also used for doubles), booleans ( <code>True</code> and <code>False</code>), characters (in single quotes, as <code>'A'</code>, with special characters expressed using a percent sign as in <code>'%N'</code> for new line, <code>'%B'</code> for backspace and <code>'%U'</code> for null).
|
||||
|
||||
For integer constants, it is also possible to avoid specifying the values. A declaration of the form
|
||||
<code>
|
||||
a, b, c, ... n : INTEGER = unique
|
||||
</code>
|
||||
|
||||
introduces <code>a</code>, <code>b</code>, <code>c</code>, ... <code>n</code> as constant integer attributes, whose value are assigned by the Eiffel compiler rather than explicitly by the programmer. The values are different for all <code>unique</code> attributes in a system; they are all positive, and, in a single declaration such as the above, guaranteed to be consecutive (so that you may use an invariant property of the form <code>code > a and code < n</code> to express that <code>code</code> should be one of the values). This mechanism replaces the "enumerated types" found in many languages, without suffering from the same problems. (Enumerated types have an ill-defined place in the type system; and it is not clear what operations are permitted.)
|
||||
|
||||
You may use Unique values in conjunction with the <code>inspect</code> multi-branch instruction studied in the next section. They are only appropriate for codes that can take on a fixed number of well-defined values -- not as a way to program operations with many variants, a need better addressed by the object-oriented technique studied earlier and relying on inheritance, polymorphism, redeclaration and dynamic binding.
|
||||
|
||||
Manifest constants are also available for strings, using double quotes as in
|
||||
<code>
|
||||
User_friendly_error_message: STRING is "Go get a life !"
|
||||
|
||||
@@ -20,19 +20,20 @@ The term "entity" generalizes the more common notion of "variable". An entity de
|
||||
An entity is said to be void if it is not attached to any object. By default, entities are void at initialization. To obtain objects at run-time, a routine <code>r</code> appearing in the client class <code>X</code> may use a '''creation instruction''' of the form
|
||||
|
||||
<code>
|
||||
create acc</code>
|
||||
create acc</code>
|
||||
|
||||
|
||||
which creates a new direct instance of <code> ACCOUNT</code>, attaches <code>acc</code> to that instance, and initializes all its fields to default values. A variant of this notation, studied below, makes it possible to override the default initializations.
|
||||
|
||||
Once the client has attached <code>acc</code> to an object, it may call on this object the features defined in class <code>ACCOUNT</code>. Here is an extract with some feature calls using <code>acc</code> as their target:
|
||||
<code>
|
||||
acc.open ("Jill")
|
||||
acc.deposit (5000)
|
||||
if acc.may_withdraw (3000) then
|
||||
acc.withdraw (3000)
|
||||
print (acc.balance)
|
||||
end</code>
|
||||
acc.open ("Jill")
|
||||
acc.deposit (5000)
|
||||
if acc.may_withdraw (3000) then
|
||||
acc.withdraw (3000)
|
||||
print (acc.balance)
|
||||
end
|
||||
</code>
|
||||
|
||||
These feature calls use dot notation, of the form <code>target_name.feature_name</code>, possibly followed by a list of arguments in parentheses. Features are of two kinds:
|
||||
* '''Routines''', such as <code>open</code>, <code>deposit</code>, <code>may_withdraw</code>, <code>withdraw</code>, represent computations applicable to instances of the class.
|
||||
@@ -44,47 +45,48 @@ Routines are further divided into '''procedures''' (commands, which do not retur
|
||||
|
||||
In class <code>ACCOUNT</code>, is feature <code>balance</code> an attribute, or is it a function with no argument? The above extract of the client class <code>X</code> doesn't say, and this ambiguity is intentional. A client of <code>ACCOUNT</code> must not need to know how class <code>ACCOUNT</code> delivers an account's balance when requested: by looking up a field present in each account object, or by calling a function that computes the balance from other fields. Choosing between these techniques is the business of class <code>ACCOUNT</code>, not anybody else's. Because such implementation choices are often changed over the lifetime of a project, it is essential to protect clients against their effects. This is known as the '''Uniform Access Principle''', stating that the choice between representing a property through memory (an attribute) or through an algorithm (function) shall not affect how clients use it.
|
||||
|
||||
So much for how client classes will typically use <code>ACCOUNT. </code> Below is a first sketch of how class <code>ACCOUNT</code> itself might look. Line segments beginning with <code>--</code> are comments. The class includes two <code>feature</code> clauses, introducing its features. The first begins with just the keyword <code>feature</code>, without further qualification; this means that the features declared in this clause are available (or "exported") to all clients of the class. The second clause is introduced by <code>feature</code> { <code>NONE</code>} to indicate that the feature that follows, called <code>add</code>, is available to no client. What appears between the braces is a list of client classes to which the corresponding features are available; <code>NONE</code> is a special class of the Kernel Library, which has no instances, so that <code>add</code> is in effect a secret feature, available only locally to the other routines of class <code>ACCOUNT</code>. So in a client class such as <code>X</code>, the call <code>acc.add ( -3000 )</code> would be invalid.
|
||||
So much for how client classes will typically use <code>ACCOUNT. </code> Below is a first sketch of how class <code>ACCOUNT</code> itself might look. Line segments beginning with <code>--</code> are comments. The class includes two <code>feature</code> clauses, introducing its features. The first begins with just the keyword <code>feature</code>, without further qualification; this means that the features declared in this clause are available (or "exported") to all clients of the class. The second clause is introduced by <code>feature {NONE}</code> to indicate that the feature that follows, called <code>add</code>, is available to no client. What appears between the braces is a list of client classes to which the corresponding features are available; <code>NONE</code> is a special class of the Kernel Library, which has no instances, so that <code>add</code> is in effect a secret feature, available only locally to the other routines of class <code>ACCOUNT</code>. So in a client class such as <code>X</code>, the call <code>acc.add ( -3000 )</code> would be invalid.
|
||||
<code>
|
||||
class ACCOUNT
|
||||
class
|
||||
ACCOUNT
|
||||
|
||||
feature
|
||||
|
||||
balance: INTEGER
|
||||
owner: PERSON
|
||||
minimum_balance: INTEGER = 1000
|
||||
balance: INTEGER
|
||||
owner: PERSON
|
||||
minimum_balance: INTEGER = 1000
|
||||
|
||||
open (who: PERSON)
|
||||
-- Assign the account to owner who.
|
||||
do
|
||||
owner := who
|
||||
end
|
||||
open (who: PERSON)
|
||||
-- Assign the account to owner who.
|
||||
do
|
||||
owner := who
|
||||
end
|
||||
|
||||
deposit (sum: INTEGER)
|
||||
-- Deposit sum into the account.
|
||||
do
|
||||
add (sum)
|
||||
end
|
||||
deposit (sum: INTEGER)
|
||||
-- Deposit sum into the account.
|
||||
do
|
||||
add (sum)
|
||||
end
|
||||
|
||||
withdraw (sum: INTEGER)
|
||||
-- Withdraw sum from the account.
|
||||
do
|
||||
add (-sum)
|
||||
end
|
||||
withdraw (sum: INTEGER)
|
||||
-- Withdraw sum from the account.
|
||||
do
|
||||
add (-sum)
|
||||
end
|
||||
|
||||
may_withdraw (sum: INTEGER): BOOLEAN
|
||||
-- Is there enough money to withdraw sum?
|
||||
do
|
||||
Result := (balance >= sum + minimum_balance)
|
||||
end
|
||||
may_withdraw (sum: INTEGER): BOOLEAN
|
||||
-- Is there enough money to withdraw sum?
|
||||
do
|
||||
Result := (balance >= sum + minimum_balance)
|
||||
end
|
||||
|
||||
feature {NONE}
|
||||
|
||||
add (sum: INTEGER)
|
||||
-- Add sum to the balance
|
||||
do
|
||||
balance := balance + sum
|
||||
end
|
||||
add (sum: INTEGER)
|
||||
-- Add sum to the balance
|
||||
do
|
||||
balance := balance + sum
|
||||
end
|
||||
|
||||
end -- ACCOUNT
|
||||
</code>
|
||||
@@ -93,7 +95,7 @@ Let us examine the features in sequence. The <code>do</code> <code>...</code> <c
|
||||
|
||||
The language definition guarantees automatic initialization, so that the initial balance of an account object will be zero after a creation instruction. Each type has a default initial value: zero for <code>INTEGER</code> and <code>REAL</code>, false for <code>BOOLEAN</code>, null character for <code>CHARACTER</code>, and a void reference for reference types. The class designer may also provide clients with different initialization options, as will be seen below in a revised version of this example.
|
||||
|
||||
The other public features, <code>withdraw deposit, open,</code> and <code>may_withdraw</code> are straight-forward routines. The special entity <code>Result</code>, used in <code>may_withdraw</code>, denotes the function result; it is initialized on function entry to the default value of the function's result type. You may only use <code>Result</code> in functions.
|
||||
The other public features, <code>withdraw, deposit, open,</code> and <code>may_withdraw</code> are straight-forward routines. The special entity <code>Result</code>, used in <code>may_withdraw</code>, denotes the function result; it is initialized on function entry to the default value of the function's result type. You may only use <code>Result</code> in functions.
|
||||
|
||||
The secret procedure <code>add</code> serves for the implementation of the public procedures <code>deposit</code> and <code>withdraw</code>; the designer of <code>ACCOUNT</code> judged it too general to be exported by itself. The clause "<code>= 1000</code>" introduces <code>minimum_balance</code> as a constant attribute, which will not occupy any space in instances of the class; in contrast, every instance has a field for every non-constant attribute such as <code>balance</code>.
|
||||
|
||||
|
||||
@@ -4,31 +4,31 @@
|
||||
[[Property:uuid|912e4c38-9add-e478-59c3-5c10aa75d784]]
|
||||
Genericity and inheritance, the two fundamental mechanisms for generalizing classes, may be combined in two fruitful ways.
|
||||
|
||||
The first technique yields '''polymorphic data structures'''. Assume that in the generic class <code> LIST [G] </code> the insertion procedure <code> put </code> has a formal argument of type <code> G </code>, representing the element to be inserted. Then with a declaration such as
|
||||
The first technique yields '''polymorphic data structures'''. Assume that in the generic class <code>LIST [G]</code> the insertion procedure <code>put</code> has a formal argument of type <code>G</code>, representing the element to be inserted. Then with a declaration such as
|
||||
<code>
|
||||
pl: LIST [POLYGON]</code>
|
||||
pl: LIST [POLYGON]</code>
|
||||
|
||||
the type rules imply that in a call<code> pl.put ( "p" ) </code>the permitted types for the argument <code> p </code> include not just <code> POLYGON </code>, but also <code> RECTANGLE </code> (an heir of <code> POLYGON </code>) or any other type conforming to <code> POLYGON </code> through inheritance.
|
||||
the type rules imply that in a call <code>pl.put ( "p" )</code>the permitted types for the argument <code>p</code> include not just <code>POLYGON</code>, but also <code>RECTANGLE</code> (an heir of <code>POLYGON</code>) or any other type conforming to <code>POLYGON</code> through inheritance.
|
||||
|
||||
The basic conformance requirement used here is the inheritance-based type compatibility rule: <code> V </code> conforms to <code> T </code> if <code> V </code> is a descendant of <code> T </code>.
|
||||
The basic conformance requirement used here is the inheritance-based type compatibility rule: <code>V</code> conforms to <code>T</code> if <code>V</code> is a descendant of <code>T</code>.
|
||||
|
||||
Structures such as <code> pl </code> may contain objects of different types, hence the name "polymorphic data structure". Such polymorphism is, again, made safe by the type rules: by choosing an actual generic parameter ( <code> POLYGON </code> in the example) based higher or lower in the inheritance graph, you extend or restrict the permissible types of objects in <code> pl </code>. A fully general list would be declared as
|
||||
Structures such as <code>pl</code> may contain objects of different types, hence the name "polymorphic data structure". Such polymorphism is, again, made safe by the type rules: by choosing an actual generic parameter ( <code>POLYGON</code> in the example) based higher or lower in the inheritance graph, you extend or restrict the permissible types of objects in <code>pl</code>. A fully general list would be declared as
|
||||
<code>
|
||||
LIST [ANY]</code>
|
||||
LIST [ANY]</code>
|
||||
|
||||
where <code> ANY </code>, a Kernel Library class, is automatically an ancestor of any class that you may write.
|
||||
where <code>ANY</code>, a Kernel Library class, is automatically an ancestor of any class that you may write.
|
||||
|
||||
The other mechanism for combining genericity and inheritance is '''constrained genericity'''. By indicating a class name after a formal generic parameter, as in
|
||||
<code>
|
||||
VECTOR [T -> NUMERIC]</code>
|
||||
VECTOR [T -> NUMERIC]</code>
|
||||
|
||||
you express that only descendants of that class (here <code> NUMERIC </code>) may be used as the corresponding actual generic parameters. This makes it possible to use the corresponding operations. Here, for example, class <code> VECTOR </code> may define a routine <code> infix </code> "+" for adding vectors, based on the corresponding routine from <code> NUMERIC </code> for adding vector elements. Then by making <code> VECTOR </code> itself inherit from <code> NUMERIC </code>, you ensure that it satisfies its own generic constraint and enable the definition of types such as<code> VECTOR [VECTOR [T]]</code> .
|
||||
you express that only descendants of that class (here <code>NUMERIC</code>) may be used as the corresponding actual generic parameters. This makes it possible to use the corresponding operations. Here, for example, class <code>VECTOR</code> may define a routine <code>infix</code> "+" for adding vectors, based on the corresponding routine from <code>NUMERIC</code> for adding vector elements. Then by making <code>VECTOR</code> itself inherit from <code>NUMERIC</code>, you ensure that it satisfies its own generic constraint and enable the definition of types such as <code>VECTOR [VECTOR [T]]</code> .
|
||||
|
||||
As you have perhaps guessed, unconstrained genericity, as in<code> LIST [G]</code> , may be viewed as an abbreviation for genericity constrained by <code> ANY </code>, as in
|
||||
As you have perhaps guessed, unconstrained genericity, as in <code>LIST [G]</code> , may be viewed as an abbreviation for genericity constrained by <code>ANY</code>, as in
|
||||
<code>
|
||||
LIST [G -> ANY]</code>
|
||||
LIST [G -> ANY]</code>
|
||||
|
||||
Something else you may have guessed: if <code> ANY </code>, introduced in this session, is the top of the inheritance structure -- providing all classes with universal features such as <code> equal </code> to compare arbitrary objects and <code> clone </code> to duplicate objects -- then <code> NONE </code>, seen earlier in the notation<code> feature {NONE} </code>, is its bottom. <code> NONE </code> indeed conceptually inherits from all other classes. <code> NONE </code> is, among other things, the type of <code> ensure </code>, the void reference.
|
||||
Something else you may have guessed: if <code>ANY</code>, introduced in this session, is the top of the inheritance structure -- providing all classes with universal features such as <code>equal</code> to compare arbitrary objects and <code>clone</code> to duplicate objects -- then <code>NONE</code>, seen earlier in the notation <code>feature {NONE}</code>, is its bottom. <code>NONE</code> indeed conceptually inherits from all other classes. <code>NONE</code> is, among other things, the type of <code>ensure</code>, the void reference.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
[[Property:uuid|b3264238-f160-a6fc-0b03-adcd80b1f55a]]
|
||||
The inheritance mechanism includes one more major notion: deferred features and classes.
|
||||
|
||||
Declaring a feature <code> f </code> as deferred in a class <code> C </code> expresses that there is no default implementation of <code> f </code> in <code> C </code>; such implementations will appear in eventual descendants of <code> C </code>. A class that has one or more deferred routines is itself said to be deferred. A non-deferred routine or class -- like all those seen until now -- is said to be '''effective'''.
|
||||
Declaring a feature <code>f</code> as deferred in a class <code>C</code> expresses that there is no default implementation of <code>f</code> in <code>C</code>; such implementations will appear in eventual descendants of <code>C</code>. A class that has one or more deferred routines is itself said to be deferred. A non-deferred routine or class -- like all those seen until now -- is said to be '''effective'''.
|
||||
|
||||
For example, a system used by a Department of Motor Vehicles to register vehicles might include a class of the form
|
||||
<code>
|
||||
@@ -41,19 +41,19 @@ This example assumes that no single registration algorithm applies to all kinds
|
||||
|
||||
Whereas an effective class described an implementation of an abstract data types, a deferred class describes a set of possible implementations. You may not instantiate a deferred class: create v is invalid if v is declared of type VEHICLE. But you may assign to v a reference to an instance of an effective descendant of VEHICLE. For example, assuming CAR and TRUCK provide effective definitions for all deferred routines of VEHICLE, the following will be valid:
|
||||
<code>
|
||||
v: VEHICLE
|
||||
c: CAR
|
||||
t: TRUCK
|
||||
...
|
||||
create c
|
||||
create t
|
||||
...
|
||||
if "Some test" then
|
||||
v := c
|
||||
else
|
||||
v := t
|
||||
end
|
||||
v.register (2008)</code>
|
||||
v: VEHICLE
|
||||
c: CAR
|
||||
t: TRUCK
|
||||
...
|
||||
create c
|
||||
create t
|
||||
...
|
||||
if "Some test" then
|
||||
v := c
|
||||
else
|
||||
v := t
|
||||
end
|
||||
v.register (2008)</code>
|
||||
|
||||
This example fully exploits polymorphism: depending on the outcome of "Some test", <code>v</code> will be treated as a car or a truck, and the appropriate registration algorithm will be applied. Also, "Some test" may depend on some event whose outcome is impossible to predict until run-time, for example the user clicking with the mouse to select one among several vehicle icons displayed on the screen.
|
||||
|
||||
|
||||
@@ -7,90 +7,92 @@ If classes are to deserve their definition as abstract data type implementations
|
||||
==The role of assertions==
|
||||
|
||||
Eiffel encourages software developers to express formal properties of classes by writing '''assertions''', which may in particular appear in the following roles: <br/>
|
||||
* Routine '''preconditions''' express the requirements that clients must satisfy whenever they call a routine. For example the designer of <code> ACCOUNT </code> may wish to permit a withdrawal operation only if it keeps the account's balance at or above the minimum. Preconditions are introduced by the keyword <code> require </code>.
|
||||
* Routine '''postconditions''', introduced by the keyword <code> ensure </code>, express conditions that the routine (the supplier) guarantees on return, if the precondition was satisfied on entry.
|
||||
* A class '''invariant''' must be satisfied by every instance of the class whenever the instance is externally accessible: after creation, and after any call to an exported routine of the class. The invariant appears in a clause introduced by the keyword <code> invariant </code>, and represents a general consistency constraint imposed on all routines of the class.
|
||||
* Routine '''preconditions''' express the requirements that clients must satisfy whenever they call a routine. For example the designer of <code>ACCOUNT</code> may wish to permit a withdrawal operation only if it keeps the account's balance at or above the minimum. Preconditions are introduced by the keyword <code>require</code>.
|
||||
* Routine '''postconditions''', introduced by the keyword <code>ensure</code>, express conditions that the routine (the supplier) guarantees on return, if the precondition was satisfied on entry.
|
||||
* A class '''invariant''' must be satisfied by every instance of the class whenever the instance is externally accessible: after creation, and after any call to an exported routine of the class. The invariant appears in a clause introduced by the keyword <code>invariant</code>, and represents a general consistency constraint imposed on all routines of the class.
|
||||
|
||||
|
||||
With appropriate assertions, the class <code> ACCOUNT </code> becomes:
|
||||
With appropriate assertions, the class <code>ACCOUNT</code> becomes:
|
||||
<code>
|
||||
class ACCOUNT
|
||||
class
|
||||
ACCOUNT
|
||||
|
||||
create
|
||||
make
|
||||
make
|
||||
|
||||
feature
|
||||
... Attributes as before:
|
||||
balance , minimum_balance , owner , open ...
|
||||
... Attributes as before:
|
||||
balance , minimum_balance , owner , open ...
|
||||
|
||||
deposit (sum: INTEGER)
|
||||
-- Deposit sum into the account.
|
||||
require
|
||||
sum >= 0
|
||||
do
|
||||
add (sum)
|
||||
ensure
|
||||
balance = old balance + sum
|
||||
end
|
||||
deposit (sum: INTEGER)
|
||||
-- Deposit sum into the account.
|
||||
require
|
||||
sum >= 0
|
||||
do
|
||||
add (sum)
|
||||
ensure
|
||||
balance = old balance + sum
|
||||
end
|
||||
|
||||
withdraw (sum: INTEGER)
|
||||
-- Withdraw sum from the account.
|
||||
require
|
||||
sum >= 0
|
||||
sum <= balance - minimum_balance
|
||||
do
|
||||
add (-sum)
|
||||
ensure
|
||||
balance = old balance - sum
|
||||
end
|
||||
withdraw (sum: INTEGER)
|
||||
-- Withdraw sum from the account.
|
||||
require
|
||||
sum >= 0
|
||||
sum <= balance - minimum_balance
|
||||
do
|
||||
add (-sum)
|
||||
ensure
|
||||
balance = old balance - sum
|
||||
end
|
||||
|
||||
may_withdraw ... -- As before
|
||||
may_withdraw ... -- As before
|
||||
|
||||
feature {NONE}
|
||||
|
||||
add ...
|
||||
add ...
|
||||
|
||||
make (initial: INTEGER)
|
||||
-- Initialize account with balance initial.
|
||||
require
|
||||
initial >= minimum_balance
|
||||
do
|
||||
balance := initial
|
||||
end
|
||||
make (initial: INTEGER)
|
||||
-- Initialize account with balance initial.
|
||||
require
|
||||
initial >= minimum_balance
|
||||
do
|
||||
balance := initial
|
||||
end
|
||||
|
||||
invariant
|
||||
balance >= minimum_balance
|
||||
balance >= minimum_balance
|
||||
|
||||
end -- ACCOUNT
|
||||
</code>
|
||||
|
||||
The notation <code> old </code> <code> expression </code> is only valid in a routine postcondition. It denotes the value the <code> expression </code> had on routine entry.
|
||||
The notation <code>old</code> <code>expression</code> is only valid in a routine postcondition. It denotes the value the <code>expression</code> had on routine entry.
|
||||
|
||||
==Creation procedures==
|
||||
|
||||
In its last version above, the class now includes a creation procedure, <code> make </code>. With the first version, clients used creation instructions such as <code> create </code> <code> acc1 </code> to create accounts; but then the default initialization, setting balance to zero, violated the invariant. By having one or more creation procedures, listed in the <code> create </code> clause at the beginning of the class text, a class offers a way to override the default initializations. The effect of
|
||||
In its last version above, the class now includes a creation procedure, <code>make</code>. With the first version, clients used creation instructions such as <code>create </code> <code>acc1</code> to create accounts; but then the default initialization, setting balance to zero, violated the invariant. By having one or more creation procedures, listed in the <code>create</code> clause at the beginning of the class text, a class offers a way to override the default initializations. The effect of
|
||||
<code>
|
||||
create acc1.make (5_500)</code>
|
||||
create acc1.make (5_500)</code>
|
||||
|
||||
is to allocate the object (as with the default creation) and to call procedure <code> make </code> on this object, with the argument given. This call is correct since it satisfies the precondition; it will ensure the invariant.
|
||||
is to allocate the object (as with the default creation) and to call procedure <code>make</code> on this object, with the argument given. This call is correct since it satisfies the precondition; it will ensure the invariant.
|
||||
|
||||
{{info|The underscore <code>_</code> in the integer constant ''5_500'' has no semantic effect. The general rule is that you can group digits by sets of three from the right to improve the readability of integer constants. }}
|
||||
|
||||
|
||||
Note that the same keyword, <code> create </code>, serves both to introduce creation instructions and the creation clause listing creation procedures at the beginning of the class.
|
||||
Note that the same keyword, <code>create</code>, serves both to introduce creation instructions and the creation clause listing creation procedures at the beginning of the class.
|
||||
|
||||
A procedure listed in the creation clause, such as <code> make </code>, otherwise enjoys the same properties as other routines, especially for calls. Here the procedure <code> make </code> is secret since it appears in a clause starting with
|
||||
A procedure listed in the creation clause, such as <code>make</code>, otherwise enjoys the same properties as other routines, especially for calls. Here the procedure <code>make</code> is secret since it appears in a clause starting with
|
||||
<code>
|
||||
feature {NONE} </code>
|
||||
feature {NONE}</code>
|
||||
|
||||
so it would be invalid for a client to include a call such as
|
||||
<code>
|
||||
acc.make (8_000)</code>
|
||||
acc.make (8_000)</code>
|
||||
|
||||
To make such a call valid, it would suffice to move the declaration of <code> make </code> to the first <code> feature </code> clause of class <code> ACCOUNT </code>, which carries no export restriction. Such a call does not create any new object, but simply resets the balance of a previously created account.
|
||||
To make such a call valid, it would suffice to move the declaration of <code>make</code> to the first <code>feature</code> clause of class <code>ACCOUNT</code>, which carries no export restriction. Such a call does not create any new object, but simply resets the balance of a previously created account.
|
||||
|
||||
==Design by Contract™==
|
||||
|
||||
Syntactically, assertions are boolean expressions, with a few extensions such as the <code> old </code>notation. Also, you may split an assertion into two or more clauses, as here with the precondition of <code> withdraw </code>; this is as if you had separated the clauses with an <code> and </code>, but makes the assertion clearer, especially if it includes many conditions.
|
||||
Syntactically, assertions are boolean expressions, with a few extensions such as the <code>old </code>notation. Also, you may split an assertion into two or more clauses, as here with the precondition of <code>withdraw</code>; this is as if you had separated the clauses with an <code>and</code>, but makes the assertion clearer, especially if it includes many conditions.
|
||||
|
||||
Assertions play a central part in the Eiffel method for building reliable object-oriented software. They serve to make explicit the assumptions on which programmers rely when they write software elements that they believe are correct. Writing assertions amounts to spelling out the terms of the '''contract''' which governs the relationship between a routine and its callers. The precondition binds the callers; the postcondition binds the routine.
|
||||
|
||||
@@ -100,39 +102,39 @@ The underlying theory of Design by Contract™, the centerpiece of the Eiffel
|
||||
|
||||
Assertions are also an indispensable tool for the documentation of reusable software components: one cannot expect large-scale reuse without a precise documentation of what every component expects (precondition), what it guarantees in return (postcondition) and what general conditions it maintains (invariant).
|
||||
|
||||
Documentation tools in EiffelStudio use assertions to produce information for client programmers, describing classes in terms of observable behavior, not implementation. In particular the '''Contract Form''' of a class, also called its "short form", which serves as its interface documentation, is obtained from the full text by removing all non-exported features and all implementation information such as <code> do </code> clauses of routines, but keeping interface information and in particular assertions. Here is the Contract Form of the above class:
|
||||
Documentation tools in EiffelStudio use assertions to produce information for client programmers, describing classes in terms of observable behavior, not implementation. In particular the '''Contract Form''' of a class, also called its "short form", which serves as its interface documentation, is obtained from the full text by removing all non-exported features and all implementation information such as <code>do</code> clauses of routines, but keeping interface information and in particular assertions. Here is the Contract Form of the above class:
|
||||
<code>
|
||||
class interface ACCOUNT
|
||||
|
||||
create
|
||||
make
|
||||
make
|
||||
|
||||
feature
|
||||
|
||||
balance: INTEGER
|
||||
...
|
||||
balance: INTEGER
|
||||
...
|
||||
|
||||
deposit (sum: INTEGER)
|
||||
-- Deposit sum into the account.
|
||||
require
|
||||
sum >= 0
|
||||
ensure
|
||||
balance = old balance + sum
|
||||
deposit (sum: INTEGER)
|
||||
-- Deposit sum into the account.
|
||||
require
|
||||
sum >= 0
|
||||
ensure
|
||||
balance = old balance + sum
|
||||
|
||||
withdraw (sum: INTEGER)
|
||||
-- Withdraw sum from the account.
|
||||
require
|
||||
sum >= 0
|
||||
sum <= balance - minimum_balance
|
||||
ensure
|
||||
balance = old balance - sum
|
||||
withdraw (sum: INTEGER)
|
||||
-- Withdraw sum from the account.
|
||||
require
|
||||
sum >= 0
|
||||
sum <= balance - minimum_balance
|
||||
ensure
|
||||
balance = old balance - sum
|
||||
|
||||
may_withdraw ...
|
||||
may_withdraw ...
|
||||
|
||||
end -- ACCOUNT
|
||||
</code>
|
||||
|
||||
This is not actual Eiffel, only documentation of Eiffel classes, hence the use of slightly different syntax to avoid any confusion ( <code> interface class </code> rather than <code> class </code>). In accordance with the Uniform Access Principle (page [[4 Classes|7]] ), the output for <code> balance </code> would be the same if this feature were a function rather than an attribute.
|
||||
This is not actual Eiffel, only documentation of Eiffel classes, hence the use of slightly different syntax to avoid any confusion ( <code>interface class</code> rather than <code>class</code>). In accordance with the Uniform Access Principle (page [[4 Classes|7]] ), the output for <code>balance</code> would be the same if this feature were a function rather than an attribute.
|
||||
|
||||
You will find in EiffelStudio automatic tools to produce the Contract Form of a class. You can also get the '''Flat Contract''' form, based on the same ideas but including inherited features along with those introduced in the class itself. EiffelStudio can produce these forms, and other documentation views of a class, in a variety of output formats including HTML, so that collaborative projects can automatically post the latest versions of their class interfaces on the Internet or an Intranet.
|
||||
|
||||
|
||||
@@ -9,32 +9,32 @@ In some circumstances it is useful to define an object that denotes an operation
|
||||
"When the user clicks this OK button, the system must update the file"
|
||||
</code>
|
||||
|
||||
each involves a '''control''' (here the OK button), an '''event''' (mouse click) and an '''operation''' (update the file). This can be programmed by having an "event loop", triggered for each event, which performs massive decision-making ( <code>if "The latest event was `left mouse click on button 23'" then "Appropriate instructions" else if </code>... and so on with many branches); but this leads to bulky software architectures where introducing any new control or event requires updating a central part of the code. It's preferable to let any element of the system that encounters a new control-event-operation association
|
||||
each involves a '''control''' (here the OK button), an '''event''' (mouse click) and an '''operation''' (update the file). This can be programmed by having an "event loop", triggered for each event, which performs massive decision-making (<code>if "The latest event was `left mouse click on button 23'" then "Appropriate instructions" else if </code>... and so on with many branches); but this leads to bulky software architectures where introducing any new control or event requires updating a central part of the code. It's preferable to let any element of the system that encounters a new control-event-operation association
|
||||
<code>
|
||||
[control, event, operation]
|
||||
[control, event, operation]
|
||||
</code>
|
||||
|
||||
store it as a triple of objects into an object structure, such as an array or a list. Triples in that structure may come from different parts of the system; there is no central know-it-all structure. The only central element is a simple mechanism which can explore the object structure to execute each <code> operation </code> associated with a certain <code> control </code> and a certain <code> event </code>. The mechanism is not just simple; it's also independent of your application, since it doesn't need to know about any particular control, event or operation (it will find them in the object structure). So it can be programmed once and for all, as part of a library such as EiffelVision 2 for platform-independent graphics.
|
||||
store it as a triple of objects into an object structure, such as an array or a list. Triples in that structure may come from different parts of the system; there is no central know-it-all structure. The only central element is a simple mechanism which can explore the object structure to execute each <code>operation</code> associated with a certain <code>control</code> and a certain <code>event</code>. The mechanism is not just simple; it's also independent of your application, since it doesn't need to know about any particular control, event or operation (it will find them in the object structure). So it can be programmed once and for all, as part of a library such as EiffelVision 2 for platform-independent graphics.
|
||||
|
||||
To build an object structure, we need objects. A <code>control</code>, an <code>event</code> are indeed objects. But an <code>operation</code> is not: it's program code -- a routine of a certain class.
|
||||
|
||||
Agents address this issue. An agent is an object that represents a routine, which can then be kept in an object structure. The simplest form of agent is written <code> agent r </code>, where <code>r</code> is a routine. This denotes an object. If <code>your_agent</code> is such an agent object, the call
|
||||
Agents address this issue. An agent is an object that represents a routine, which can then be kept in an object structure. The simplest form of agent is written <code>agent r</code>, where <code>r</code> is a routine. This denotes an object. If <code>your_agent</code> is such an agent object, the call
|
||||
<code>
|
||||
your_agent.call ([a, b])
|
||||
your_agent.call ([a, b])
|
||||
</code>
|
||||
|
||||
where <code>a</code> and <code>b</code> are valid arguments for <code>r</code>, will have the same effect as a direct call to <code>r</code> with arguments <code>a</code> and <code>b</code>. Of course, if you know that you want to call <code>r</code> with those arguments, you don't need any agents; just use the direct call<code> r (a, b) </code>. The benefit of using an agent is that you can store it into an object structure to be called '''later''', for example when an event-driven mechanism finds the agent in the object structure, associated with a certain control and a certain event. For this reason agents are also called '''delayed calls'''.
|
||||
where <code>a</code> and <code>b</code> are valid arguments for <code>r</code>, will have the same effect as a direct call to <code>r</code> with arguments <code>a</code> and <code>b</code>. Of course, if you know that you want to call <code>r</code> with those arguments, you don't need any agents; just use the direct call <code>r (a, b)</code>. The benefit of using an agent is that you can store it into an object structure to be called '''later''', for example when an event-driven mechanism finds the agent in the object structure, associated with a certain control and a certain event. For this reason agents are also called '''delayed calls'''.
|
||||
|
||||
{{info|The notation <code>[a, b]</code> denotes a sequence of elements, or '''tuple'''. The reason <code>call</code> needs a tuple as argument, whereas the direct call<code> r (a, b)</code> doesn't, is that <code>call</code> is a general routine (from the EiffelBase class <code>ROUTINE</code>, representing agents) applicable to any agent, whereas the direct call refers explicitly to <code>r</code> and hence requires arguments <code> a </code> and <code> b </code> of specific types. The agent mechanism, however, is statically typed like the rest of the language; when you call <code> call </code>, the type checking mechanism ensures that the tuple you pass as argument contains elements <code> a </code> and <code> b </code> of the appropriate types. }}
|
||||
{{info|The notation <code>[a, b]</code> denotes a sequence of elements, or '''tuple'''. The reason <code>call</code> needs a tuple as argument, whereas the direct call <code>r (a, b)</code> doesn't, is that <code>call</code> is a general routine (from the EiffelBase class <code>ROUTINE</code>, representing agents) applicable to any agent, whereas the direct call refers explicitly to <code>r</code> and hence requires arguments <code>a</code> and <code>b</code> of specific types. The agent mechanism, however, is statically typed like the rest of the language; when you call <code>call</code>, the type checking mechanism ensures that the tuple you pass as argument contains elements <code>a</code> and <code>b</code> of the appropriate types. }}
|
||||
|
||||
|
||||
A typical use of agents with EiffelVision 2 is
|
||||
<code>
|
||||
ok_button.select_actions.extend (agent your_routine)</code>
|
||||
ok_button.select_actions.extend (agent your_routine)</code>
|
||||
|
||||
which says: "add <code>your_routine</code> to the list of operations to be performed whenever a<code> select</code> event (left click) happens on<code> ok_button</code>". <code>ok_button.select_actions </code>is the list of agents associated with the button and the event; in list classes, procedure<code> extend</code> adds an item at the end of a list. Here, the object to be added is the agent.
|
||||
which says: "add <code>your_routine</code> to the list of operations to be performed whenever a <code>select</code> event (left click) happens on <code>ok_button</code>". <code>ok_button.select_actions</code> is the list of agents associated with the button and the event; in list classes, procedure <code>extend</code> adds an item at the end of a list. Here, the object to be added is the agent.
|
||||
|
||||
This enables the EiffelVision event-handling mechanism to find the appropriate agent when it processes an event, and call<code> call </code> on that agent to trigger the appropriate routine. EiffelVision doesn't know that it's <code> your_routine </code>; in fact, it doesn't know anything about your application. It simply finds an agent in the list, and calls<code> call</code> on it. For your part, as the author of a graphical application, you don't need to know how EiffelVision handles events; you simply associate the desired agents with the desired controls and events, and let EiffelVision 2 do the rest.
|
||||
This enables the EiffelVision 2 event-handling mechanism to find the appropriate agent when it processes an event, and call <code>call</code> on that agent to trigger the appropriate routine. EiffelVision 2 doesn't know that it's <code>your_routine</code>; in fact, it doesn't know anything about your application. It simply finds an agent in the list, and calls <code>call</code> on it. For your part, as the author of a graphical application, you don't need to know how EiffelVision 2 handles events; you simply associate the desired agents with the desired controls and events, and let EiffelVision 2 do the rest.
|
||||
|
||||
Agents extend to many areas beyond GUIs. In '''numerical computation''', you may use an agent to pass to an "integrator" object a numerical function to be integrated over a certain interval. In yet another area, you can use agents (as in the iteration library of EiffelBase) to program '''iterators''' : mechanisms that repetitively apply an arbitrary operation -- represented by an agent -- to every element of a list, tree or other object structure. More generally, agent embody properties of the associated routines, opening the way to mechanism for '''reflection''', also called "introspection": the ability, during software execution, to discover properties of the software itself.
|
||||
|
||||
|
||||
@@ -8,38 +8,38 @@ Exceptions -- contract violations -- may arise from several causes. One is an as
|
||||
|
||||
Unless a routine has made specific provision to handle exceptions, it will '''fail''' if an exception arises during its execution. This in turn provides one more source of exceptions: a routine that fails triggers an exception in its caller.
|
||||
|
||||
A routine may, however, handle an exception through a <code> rescue </code> clause. This optional clause attempts to "patch things up" by bringing the current object to a stable state (one satisfying the class invariant). Then it can terminate in either of two ways:<br/>
|
||||
* The <code> rescue </code> clause may execute a <code> retry </code> instruction, which causes the routine to restart its execution from the beginning, attempting again to fulfill its contract, usually through another strategy. This assumes that the instructions of the <code> rescue </code> clause, before the <code> retry </code>, have attempted to correct the cause of the exception.
|
||||
* If the <code> rescue </code> clause does not end with <code> retry </code>, then the routine fails: it returns to its caller, immediately triggering an exception. (The caller's <code> rescue </code> clause will be executed according to the same rules.)
|
||||
A routine may, however, handle an exception through a <code>rescue</code> clause. This optional clause attempts to "patch things up" by bringing the current object to a stable state (one satisfying the class invariant). Then it can terminate in either of two ways:<br/>
|
||||
* The <code>rescue</code> clause may execute a <code>retry</code> instruction, which causes the routine to restart its execution from the beginning, attempting again to fulfill its contract, usually through another strategy. This assumes that the instructions of the <code>rescue</code> clause, before the <code>retry</code>, have attempted to correct the cause of the exception.
|
||||
* If the <code>rescue</code> clause does not end with <code>retry</code>, then the routine fails: it returns to its caller, immediately triggering an exception. (The caller's <code>rescue</code> clause will be executed according to the same rules.)
|
||||
|
||||
|
||||
The principle is that '''a routine must either succeed or fail''': it either fulfills its contract, or not; in the latter case it must notify its caller by triggering an exception.
|
||||
|
||||
Usually, only a few routines of a system will explicitly include a <code> rescue </code>clause. A routine that doesn't have an explicit <code> rescue </code> is considered to have an implicit one, which calls a routine <code> default_rescue </code> that by default does nothing, so that an exception will cause the routine to fail immediately, propagating the exception to the caller.
|
||||
Usually, only a few routines of a system will explicitly include a <code>rescue </code>clause. A routine that doesn't have an explicit <code>rescue</code> is considered to have an implicit one, which calls a routine <code>default_rescue</code> that by default does nothing, so that an exception will cause the routine to fail immediately, propagating the exception to the caller.
|
||||
|
||||
An example using the exception mechanism is a routine <code> attempt_transmission </code> that tries to transmit a message over a phone line. The actual transmission is performed by an external, low-level routine <code> transmit </code>; once started, however, <code> transmit </code> may abruptly fail, triggering an exception, if the line is disconnected. Routine <code> attempt_transmission </code> tries the transmission at most 50 times; before returning to its caller, it sets a boolean attribute <code> successful </code> to <code> True </code> <code> or </code> <code> False </code> depending on the outcome. Here is the text of the routine:
|
||||
An example using the exception mechanism is a routine <code>attempt_transmission</code> that tries to transmit a message over a phone line. The actual transmission is performed by an external, low-level routine <code>transmit</code>; once started, however, <code>transmit</code> may abruptly fail, triggering an exception, if the line is disconnected. Routine <code>attempt_transmission</code> tries the transmission at most 50 times; before returning to its caller, it sets a boolean attribute <code>successful</code> to <code>True</code> or <code>False</code> depending on the outcome. Here is the text of the routine:
|
||||
<code>
|
||||
attempt_transmission (message: STRING)
|
||||
-- Try to transmit message, at most 50 times.
|
||||
-- Set successful accordingly.
|
||||
local
|
||||
failures: INTEGER
|
||||
do
|
||||
if failures < 50 then
|
||||
transmit (message)
|
||||
successful := True
|
||||
else
|
||||
successful := False
|
||||
end
|
||||
rescue
|
||||
failures := failures + 1
|
||||
retry
|
||||
end
|
||||
-- Try to transmit message, at most 50 times.
|
||||
-- Set successful accordingly.
|
||||
local
|
||||
failures: INTEGER
|
||||
do
|
||||
if failures < 50 then
|
||||
transmit (message)
|
||||
successful := True
|
||||
else
|
||||
successful := False
|
||||
end
|
||||
rescue
|
||||
failures := failures + 1
|
||||
retry
|
||||
end
|
||||
</code>
|
||||
|
||||
Initialization rules ensure that <code> failures </code>, a local entity, is set to zero on entry.
|
||||
Initialization rules ensure that <code>failures</code>, a local entity, is set to zero on entry.
|
||||
|
||||
This example illustrates the simplicity of the mechanism: the <code> rescue </code> clause never attempts to achieve the routine's original intent; this is the sole responsibility of the body (the <code> do </code> clause). The only role of the <code> rescue </code> clause is to clean up the objects involved, and then either to fail or to retry.
|
||||
This example illustrates the simplicity of the mechanism: the <code>rescue</code> clause never attempts to achieve the routine's original intent; this is the sole responsibility of the body (the <code>do</code> clause). The only role of the <code>rescue</code> clause is to clean up the objects involved, and then either to fail or to retry.
|
||||
|
||||
This disciplined exception mechanism is essential for software developers, who need protection against unexpected events, but cannot be expected to sacrifice safety and simplicity to pay for this protection.
|
||||
|
||||
|
||||
@@ -5,16 +5,18 @@
|
||||
Building software components (classes) as implementations of abstract data types yields systems with a solid architecture but does not in itself ensure reusability and extendibility. Two key techniques address the problem: generosity (unconstrained or constrained) and inheritance. Let us look first at the unconstrained form.
|
||||
|
||||
To make a class generic is to give it '''formal generic parameters''' representing as unknown types, as in these examples from EiffelBase, an open-source library covering basic data structures and algorithms:
|
||||
<code>ARRAY [G]
|
||||
LIST [G]
|
||||
LINKED_LIST [G]</code>
|
||||
<code>
|
||||
ARRAY [G]
|
||||
LIST [G]
|
||||
LINKED_LIST [G]</code>
|
||||
|
||||
These classes describe data structures -- arrays, lists without commitment to a specific representation, lists in linked representation -- containing objects of a certain type. The formal generic parameter <code> G </code> denotes this type.
|
||||
These classes describe data structures -- arrays, lists without commitment to a specific representation, lists in linked representation -- containing objects of a certain type. The formal generic parameter <code>G</code> denotes this type.
|
||||
|
||||
A class such as these doesn't quite yet describe a type, but a type template, since <code> G </code> itself denotes an unknown type. To derive a directly usable list or array type, you must provide a type corresponding to <code> G </code>, called an '''actual generic parameter'''; this may be either an expanded type, including basic types such as <code> INTEGER </code>, or a reference type. Here are some possible generic derivations:
|
||||
<code>il: LIST [INTEGER]
|
||||
aa: ARRAY [ACCOUNT]
|
||||
aal: LIST [ARRAY [ACCOUNT]]</code>
|
||||
A class such as these doesn't quite yet describe a type, but a type template, since <code> G </code> itself denotes an unknown type. To derive a directly usable list or array type, you must provide a type corresponding to <code>G</code>, called an '''actual generic parameter'''; this may be either an expanded type, including basic types such as <code>INTEGER</code>, or a reference type. Here are some possible generic derivations:
|
||||
<code>
|
||||
il: LIST [INTEGER]
|
||||
aa: ARRAY [ACCOUNT]
|
||||
aal: LIST [ARRAY [ACCOUNT]]</code>
|
||||
|
||||
As the last example indicates, an actual generic parameter may itself be generically derived.
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
[[Property:uuid|acf84989-0e7c-f2f7-427a-19e7fce404ce]]
|
||||
Inheritance, the other fundamental generalization mechanism, makes it possible to define a new class by combination and specialization of existing classes rather than from scratch.
|
||||
|
||||
The following simple example, from the Data Structure Library in EiffelBase, is typical. <code> LIST </code>, as noted, describes lists in any representation. One such representation if the lists have a fixed number of elements uses an array. We may define the corresponding class by combination of <code> LIST </code> and <code> ARRAY </code>, as follows:
|
||||
The following simple example, from the Data Structure Library in EiffelBase, is typical. <code>LIST</code>, as noted, describes lists in any representation. One such representation if the lists have a fixed number of elements uses an array. We may define the corresponding class by combination of <code>LIST</code> and <code>ARRAY</code>, as follows:
|
||||
<code>
|
||||
class ARRAYED_LIST [G]
|
||||
inherit
|
||||
@@ -19,7 +19,7 @@ feature
|
||||
end -- ARRAYED_LIST
|
||||
</code>
|
||||
|
||||
The <code> inherit </code> ... clause lists all the "parents" of the new class, which is said to be their "heir". (The "ancestors" of a class include the class itself, its parents, grandparents etc.; the reverse term is "descendant".) Declaring <code> ARRAYED_LIST </code> as shown ensures that all the features and properties of lists and arrays are applicable to arrayed lists as well. Since the class has more than one parent, this is a case of multiple inheritance.
|
||||
The <code>inherit</code> ... clause lists all the "parents" of the new class, which is said to be their "heir". (The "ancestors" of a class include the class itself, its parents, grandparents etc.; the reverse term is "descendant".) Declaring <code>ARRAYED_LIST</code> as shown ensures that all the features and properties of lists and arrays are applicable to arrayed lists as well. Since the class has more than one parent, this is a case of multiple inheritance.
|
||||
|
||||
Standard graphical conventions -- drawn from the Business Object Notation or BON, a graphical object-oriented notation based on concepts close to those of Eiffel, and directly supported by EiffelStudio -- illustrate such inheritance structures:
|
||||
|
||||
@@ -27,7 +27,7 @@ Standard graphical conventions -- drawn from the Business Object Notation or BON
|
||||
[[Image:invitation-4]]
|
||||
|
||||
|
||||
An heir class such as <code> ARRAYED_LIST </code> needs the ability to define its own export policy. By default, inherited features keep their export status (publicly available, secret, available to selected classes only); but this may be changed in the heir. Here, for example, <code> ARRAYED_LIST </code> will export only the exported features of <code> LIST </code>, making those of <code> ARRAY </code> unavailable directly to <code> ARRAYED_LIST </code> 's clients. The syntax to achieve this is straightforward:
|
||||
An heir class such as <code>ARRAYED_LIST</code> needs the ability to define its own export policy. By default, inherited features keep their export status (publicly available, secret, available to selected classes only); but this may be changed in the heir. Here, for example, <code>ARRAYED_LIST</code> will export only the exported features of <code>LIST</code>, making those of <code>ARRAY</code> unavailable directly to <code>ARRAYED_LIST</code> 's clients. The syntax to achieve this is straightforward:
|
||||
<code>
|
||||
class ARRAYED_LIST [G]
|
||||
inherit
|
||||
@@ -39,7 +39,7 @@ class ARRAYED_LIST [G]
|
||||
... The rest as above ...
|
||||
</code>
|
||||
|
||||
Another example of multiple inheritance comes from a windowing system based on a class <code> WINDOW </code>, close to actual classes in EiffelVision 2. Windows have '''graphical''' features: a height, a width, a position, routines to scale windows, move them, and other graphical operations. The system permits windows to be nested, so that a window also has '''hierarchical''' features: access to sub windows and the parent window, adding a sub window, deleting a sub window, attaching to another parent and so on. Rather than writing complex class that would contain specific implementations for all of these features, it is preferable to inherit all hierarchical features from <code> TREE </code> (a class in EiffelBase describing trees), and all graphical features from a class <code> RECTANGLE </code>.
|
||||
Another example of multiple inheritance comes from a windowing system based on a class <code>WINDOW</code>, close to actual classes in EiffelVision 2. Windows have '''graphical''' features: a height, a width, a position, routines to scale windows, move them, and other graphical operations. The system permits windows to be nested, so that a window also has '''hierarchical''' features: access to sub windows and the parent window, adding a sub window, deleting a sub window, attaching to another parent and so on. Rather than writing complex class that would contain specific implementations for all of these features, it is preferable to inherit all hierarchical features from <code>TREE</code> (a class in EiffelBase describing trees), and all graphical features from a class <code>RECTANGLE</code>.
|
||||
|
||||
Inheritance complements the "client" relation by providing another form of reuse that yields remarkable economies of effort -- for analysis, design, implementation, evolution -- and has a profound effect on the entire software development process.
|
||||
|
||||
@@ -62,9 +62,9 @@ class C
|
||||
feature ...
|
||||
</code>
|
||||
|
||||
Here, if both <code> A </code> and <code> B </code> have features named <code> x </code> and <code> y </code>, class <code> C </code> would be invalid without the renaming.
|
||||
Here, if both <code>A</code> and <code>B</code> have features named <code>x</code> and <code>y</code>, class <code>C</code> would be invalid without the renaming.
|
||||
|
||||
Renaming also serves to provide more appropriate feature names in descendants. For example, class <code> WINDOW </code> may inherit a routine <code> insert_subtree </code> from <code> TREE </code>. For clients of <code> WINDOW </code>, however, such a routine name is no longer appropriate. An application that uses this class needs coherent window terminology, and should have to concern itself with the inheritance structure that led to the class. So you may wish to rename <code> insert_subtree </code> as <code> add_subwindow </code> in the inheritance clause of <code> WINDOW </code>.
|
||||
Renaming also serves to provide more appropriate feature names in descendants. For example, class <code>WINDOW</code> may inherit a routine <code>insert_subtree</code> from <code>TREE</code>. For clients of <code>WINDOW</code>, however, such a routine name is no longer appropriate. An application that uses this class needs coherent window terminology, and should have to concern itself with the inheritance structure that led to the class. So you may wish to rename <code>insert_subtree</code> as <code>add_subwindow</code> in the inheritance clause of <code>WINDOW</code>.
|
||||
|
||||
As a further protection against misusing multiple inheritance, the invariants of all parent classes automatically apply to a newly defined class. So classes may not be combined if their invariants are incompatible.
|
||||
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
[[Property:uuid|1c3221be-0237-1c9a-407d-652a4084de12]]
|
||||
Inheritance is not just a module combination and enrichment mechanism. It also enables the definition of flexible entities that may become attached to objects of various forms at run time, a property known as polymorphism.
|
||||
|
||||
This remarkable facility must be reconciled with static typing. The language convention is simple: an assignment of the form <code> a : = b </code> is permitted not only if <code> a </code> and <code> b </code> are of the same type, but more generally if <code> a </code> and <code> b </code> are of reference types <code> A </code> and <code> B </code>, based on classes <code> A </code> and <code> B </code> such that <code> B </code> is a descendant of <code> A </code>.
|
||||
This remarkable facility must be reconciled with static typing. The language convention is simple: an assignment of the form <code>a : = b</code> is permitted not only if <code>a</code> and <code>b</code> are of the same type, but more generally if <code>a</code> and <code>b</code> are of reference types <code>A</code> and <code>B</code>, based on classes <code>A</code> and <code>B</code> such that <code>B</code> is a descendant of <code>A</code>.
|
||||
|
||||
This corresponds to the intuitive idea that a value of a more specialized type may be assigned to an entity of a less specialized type -- but not the reverse. (As an analogy, consider that if you request vegetables, getting green vegetables is fine, but if you ask for green vegetables, receiving a dish labeled just "vegetables" is not acceptable, as it could include, say, carrots.)
|
||||
|
||||
What makes this possibility particularly powerful is the complementary facility: '''feature redefinition'''. A class may redefine some or all of the features which it inherits from its parents. For an attribute or function, the redefinition may affect the type, replacing the original by a descendant; for a routine it may also affect the implementation, replacing the original routine body by a new one.
|
||||
|
||||
Assume for example a class <code> POLYGON </code>, describing polygons, whose features include an array of points representing the vertices and a function <code> perimeter </code> which computes a polygon's perimeter by summing the successive distances between adjacent vertices. An heir of <code> POLYGON </code> may begin as:
|
||||
Assume for example a class <code>POLYGON</code>, describing polygons, whose features include an array of points representing the vertices and a function <code>perimeter</code> which computes a polygon's perimeter by summing the successive distances between adjacent vertices. An heir of <code>POLYGON</code> may begin as:
|
||||
<code>
|
||||
class RECTANGLE
|
||||
inherit
|
||||
@@ -33,39 +33,39 @@ feature -- Specific features of rectangles, such as:
|
||||
... Other RECTANGLE features ...
|
||||
</code>
|
||||
|
||||
Here it is appropriate to redefine <code> perimeter </code> for rectangles as there is a simpler and more efficient algorithm. Note the explicit <code> redefine </code> sub clause (which would come after the <code> rename </code> if present).
|
||||
Here it is appropriate to redefine <code>perimeter</code> for rectangles as there is a simpler and more efficient algorithm. Note the explicit <code>redefine</code> sub clause (which would come after the <code>rename</code> if present).
|
||||
|
||||
Other descendants of <code> POLYGON </code> may also have their own redefinitions of <code> perimeter </code>. The version to use in any call is determined by the run-time form of the target. Consider the following class fragment:
|
||||
Other descendants of <code>POLYGON</code> may also have their own redefinitions of <code>perimeter</code>. The version to use in any call is determined by the run-time form of the target. Consider the following class fragment:
|
||||
<code>
|
||||
p: POLYGON
|
||||
r: RECTANGLE
|
||||
p: POLYGON
|
||||
r: RECTANGLE
|
||||
...
|
||||
|
||||
create p
|
||||
create r
|
||||
create p
|
||||
create r
|
||||
...
|
||||
if c then
|
||||
p := r
|
||||
end
|
||||
print (p.perimeter)</code>
|
||||
if c then
|
||||
p := r
|
||||
end
|
||||
print (p.perimeter)</code>
|
||||
|
||||
The polymorphic assignment<code> p := r </code>is valid because of the above rule. If condition <code> c </code> is false, <code> p </code> will be attached to an object of type <code> POLYGON </code> for the computation of <code> p </code>. <code> perimeter </code>, which will thus use the polygon algorithm. In the opposite case, however, <code> p </code> will be attached to a rectangle; then the computation will use the version redefined for <code> RECTANGLE </code>. This is known as '''dynamic binding'''.
|
||||
The polymorphic assignment <code>p := r</code> is valid because of the above rule. If condition <code>c</code> is false, <code>p</code> will be attached to an object of type <code>POLYGON</code> for the computation of <code>p</code>. <code>perimeter</code>, which will thus use the polygon algorithm. In the opposite case, however, <code>p</code> will be attached to a rectangle; then the computation will use the version redefined for <code>RECTANGLE</code>. This is known as '''dynamic binding'''.
|
||||
|
||||
Dynamic binding provides a high degree of flexibility. The advantage for clients is the ability to request an operation (such as perimeter computation) without explicitly selecting one of its variants; the choice only occurs at run-time. This is essential in large systems, where many variants may be available; dynamic binding protects each component against changes in other components.
|
||||
|
||||
This technique is particularly attractive when compared to its closest equivalent in traditional approaches, where you would need records with variant components, or union types (C), together with <code> case </code> (switch) instructions to discriminate between variants. This means that every client must know about every possible case, and that any extension may invalidate a large body of existing software.
|
||||
This technique is particularly attractive when compared to its closest equivalent in traditional approaches, where you would need records with variant components, or union types (C), together with <code>case</code> (switch) instructions to discriminate between variants. This means that every client must know about every possible case, and that any extension may invalidate a large body of existing software.
|
||||
|
||||
The combination of inheritance, feature redefinition, polymorphism and dynamic binding supports a development mode in which every module is open and incremental. When you want to reuse an existing class but need to adapt it to a new context, you can define a new descendant of that class (with new features, redefined ones, or both) without any change to the original. This facility is of great importance in software development, an activity that -- by design or circumstance -- is invariably incremental.
|
||||
|
||||
The power of these techniques demands adequate controls. First, feature redefinition, as seen above, is explicit. Second, because the language is typed, a compiler can check statically whether a feature application<code> a.f </code>is correct. In contrast, dynamically typed object-oriented languages defer checks until run-time and hope for the best: if an object "sends a message" to another (that is to say, calls one of its routines) one just expects that the corresponding class, or one of its ancestors, will happen to include an appropriate routine; if not, a run-time error will occur. Such errors will not happen during the execution of a type-checked Eiffel system.
|
||||
The power of these techniques demands adequate controls. First, feature redefinition, as seen above, is explicit. Second, because the language is typed, a compiler can check statically whether a feature application <code>a.f</code> is correct. In contrast, dynamically typed object-oriented languages defer checks until run-time and hope for the best: if an object "sends a message" to another (that is to say, calls one of its routines) one just expects that the corresponding class, or one of its ancestors, will happen to include an appropriate routine; if not, a run-time error will occur. Such errors will not happen during the execution of a type-checked Eiffel system.
|
||||
|
||||
In other words, the language reconciles dynamic binding with static typing. Dynamic binding guarantees that whenever more than one version of a routine is applicable the right version (the one most directly adapted to the target object) will be selected. Static typing means that the compiler makes sure there is at least one such version.
|
||||
|
||||
This policy also yields an important performance benefit: in contrast with the costly run-time searches that may be needed with dynamic typing (since a requested routine may not be defined in the class of the target object but inherited from a possibly remote ancestor), the EiffelStudio implementation always finds the appropriate routine in constant-bounded time.
|
||||
|
||||
Assertions provide a further mechanism for controlling the power of redefinition. In the absence of specific precautions, redefinition may be dangerous: how can a client be sure that evaluation of<code> p.perimeter </code>will not in some cases return, say, the area? Preconditions and postconditions provide the answer by limiting the amount of freedom granted to eventual redefiners. The rule is that any redefined version must satisfy a weaker or equal precondition and ensure a stronger or equal postcondition than in the original. This means that it must stay within the semantic boundaries set by the original assertions.
|
||||
Assertions provide a further mechanism for controlling the power of redefinition. In the absence of specific precautions, redefinition may be dangerous: how can a client be sure that evaluation of <code>p.perimeter</code> will not in some cases return, say, the area? Preconditions and postconditions provide the answer by limiting the amount of freedom granted to eventual redefiners. The rule is that any redefined version must satisfy a weaker or equal precondition and ensure a stronger or equal postcondition than in the original. This means that it must stay within the semantic boundaries set by the original assertions.
|
||||
|
||||
The rules on redefinition and assertions are part of the Design by Contract™ theory, where redefinition and dynamic binding introduce subcontracting. <code> POLYGON </code>, for example, subcontracts the implementation of perimeter to <code> RECTANGLE </code> when applied to any entity that is attached at run-time to a rectangle object. An honest subcontractor is bound to honor the contract accepted by the prime contractor. This means that it may not impose stronger requirements on the clients, but may accept more general requests: weaker precondition; and that it must achieve at least as much as promised by the prime contractor, but may achieve more: stronger postcondition.
|
||||
The rules on redefinition and assertions are part of the Design by Contract™ theory, where redefinition and dynamic binding introduce subcontracting. <code>POLYGON</code>, for example, subcontracts the implementation of perimeter to <code>RECTANGLE</code> when applied to any entity that is attached at run-time to a rectangle object. An honest subcontractor is bound to honor the contract accepted by the prime contractor. This means that it may not impose stronger requirements on the clients, but may accept more general requests: weaker precondition; and that it must achieve at least as much as promised by the prime contractor, but may achieve more: stronger postcondition.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -18,13 +18,12 @@ For the system to be valid, it must include all the classes which the root '''ne
|
||||
|
||||
For a library we don't need to specify a root. If we want to make sure that every class in a library compiles fine we can specify that we want all classes to be the root.
|
||||
|
||||
The Eiffel method suggests grouping related classes (typically 5 to 40 classes) into collections called '''clusters'''. A universe is then a set of clusters. For example the EiffelBase library is divided into clusters corresponding each to a major category of data structure: <code> lists </code>, <code> tables </code>, <code> iteration </code> and so on. You can nest clusters, using for example EiffelBase, with its own subclusters as listed, as a cluster of your system.
|
||||
The Eiffel method suggests grouping related classes (typically 5 to 40 classes) into collections called '''clusters'''. A universe is then a set of clusters. For example the EiffelBase library is divided into clusters corresponding each to a major category of data structure: <code>lists</code>, <code>tables</code>, <code>iteration</code>, and so on. You can nest clusters, using for example EiffelBase, with its own subclusters as listed, as a cluster of your system.
|
||||
|
||||
How will you specify a universe? Any Eiffel implementation can use its own conventions. EiffelStudio applies a simple policy: <br/>
|
||||
* Store each class in a single file, called its class file, with a name of the form <code> name </code> . <code> e </code>. For clarity, <code> name </code> should be the lower-case version of the class name, although this is a style rule, not a requirement.
|
||||
* Store each class in a single file, called its class file, with a name of the form <code>name</code>.<code>e</code> . For clarity, <code>name</code> should be the lower-case version of the class name, although this is a style rule, not a requirement.
|
||||
* Put all the class files of a cluster into a single directory (folder on Windows), called its cluster directory.
|
||||
{{note|It is desirable for clarity, as a style rule, to separate clusters that directly contain classes ("terminal clusters") from those that have subclusters. Cluster directories will then contain class files or cluster subdirectories, but not both. }}
|
||||
|
||||
* To specify a system, it suffices to provide a list of cluster directories, along with the name of the root class and root procedure. The universe consists of the classes contained in all the class files in the listed cluster directories.
|
||||
|
||||
|
||||
|
||||
@@ -4,17 +4,17 @@
|
||||
[[Property:uuid|344a9fdc-3346-5e2d-5fdd-77464e92f72f]]
|
||||
Eiffel is strongly typed for readability and reliability. Every entity is declared of a certain type, which may be either a reference type or an expanded type.
|
||||
|
||||
Any type <code> T </code> is based on a class, which defines the operations that will be applicable to instances of <code> T </code>. The difference between the two categories of type affects the semantics of an entity <code> x </code> declared of type <code> T </code>: for a reference type, the most common case, possible values for <code> x </code> are references to objects; for an expanded type, the values are objects. In both cases, the type rules guarantee that the objects will be instances of <code> T </code>.
|
||||
Any type <code>T</code> is based on a class, which defines the operations that will be applicable to instances of <code>T</code>. The difference between the two categories of type affects the semantics of an entity <code>x</code> declared of type <code>T</code>: for a reference type, the most common case, possible values for <code>x</code> are references to objects; for an expanded type, the values are objects. In both cases, the type rules guarantee that the objects will be instances of <code>T</code>.
|
||||
|
||||
A non-expanded class such as <code> ACCOUNT </code> yields a reference type. As a result, an entity of type <code> ACCOUNT </code>, such as <code> acc </code> in the earlier client example (see the declaration of <code> acc </code> and the accompanying picture as given on page [[Invitation to Eiffel|6]] ), denotes possible run-time references to objects of type <code> ACCOUNT </code>.
|
||||
A non-expanded class such as <code>ACCOUNT</code> yields a reference type. As a result, an entity of type <code>ACCOUNT</code>, such as <code>acc</code> in the earlier client example (see the declaration of <code>acc</code> and the accompanying picture as given in [[4 Classes|section 4]] ), denotes possible run-time references to objects of type <code>ACCOUNT</code>.
|
||||
|
||||
In contrast, the value of an entity <code> acc </code> declared of type <code>expanded ACCOUNT </code> is an object such as the one shown on the figure below, with no reference. The only difference with the earlier figure is that the value of <code> acc </code> is now an <code> ACCOUNT </code> object, not a reference to such an object. No creation instruction is needed in this case. (The figure does not show the <code> PERSON </code> object to which the <code> owner </code> field of the <code> ACCOUNT </code> object -- itself a reference -- is attached.)
|
||||
In contrast, the value of an entity <code>acc</code> declared of type <code>expanded ACCOUNT</code> is an object such as the one shown on the figure below, with no reference. The only difference with the earlier figure is that the value of <code>acc</code> is now an <code>ACCOUNT</code> object, not a reference to such an object. No creation instruction is needed in this case. (The figure does not show the <code>PERSON</code> object to which the <code>owner</code> field of the <code>ACCOUNT</code> object -- itself a reference -- is attached.)
|
||||
|
||||
|
||||
[[Image:invitation-3]]
|
||||
|
||||
|
||||
An important group of expanded types, based on library classes, includes the basic types <code> CHARACTER, DOUBLE, REAL, INTEGER </code> and <code>BOOLEAN</code>. Clearly, the value of an entity declared of type <code> INTEGER </code> should be an integer, not a reference to an object containing an integer value. Operations on these types are defined by prefix and infix operators such as "+" and "<".
|
||||
An important group of expanded types, based on library classes, includes the basic types <code>CHARACTER, DOUBLE, REAL, INTEGER,</code> and <code>BOOLEAN</code>. Clearly, the value of an entity declared of type <code>INTEGER</code> should be an integer, not a reference to an object containing an integer value. Operations on these types are defined by prefix and infix operators such as "+" and "<".
|
||||
|
||||
As a result of these conventions, the type system is uniform and consistent: all types, including the basic types, are defined from classes, either as reference types or as expanded types.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user