mirror of
https://github.com/EiffelSoftware/eiffel-org.git
synced 2025-12-07 15:22:31 +01:00
create 19.12 branch
git-svn-id: https://svn.eiffel.com/eiffel-org/trunk@2229 abb3cda0-5349-4a8f-a601-0c33ac3a8c38
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
[[Property:modification_date|Mon, 10 Sep 2018 09:09:25 GMT]]
|
||||
[[Property:publication_date|Mon, 10 Sep 2018 09:09:25 GMT]]
|
||||
[[Property:uuid|B74D374E-895C-4F22-B95F-656BD78ECD03]]
|
||||
[[Property:weight|1000]]
|
||||
[[Property:title|Getting a STRING from a NUMERIC object]]
|
||||
[[Property:link_title|NUMERIC to STRING]]
|
||||
Every class has the `out` method that can be used to get a text version of the object. For a lot of classes, this method returns internal information that is not really useful for the end user. But for every `NUMERIC` class, the `out` method returns a text representation of the number that the `NUMERIC` object represents.
|
||||
|
||||
<code>
|
||||
print_integer (a_integer: INTEGER)
|
||||
-- Print the value of `a_integer`.
|
||||
do
|
||||
print (a_integer.out + "%N")
|
||||
end
|
||||
</code>
|
||||
|
||||
Note that for more advanced conversion, you can also use a conversion class like `FORMAT_DOUBLE`.
|
||||
@@ -0,0 +1,28 @@
|
||||
[[Property:modification_date|Mon, 10 Sep 2018 09:06:41 GMT]]
|
||||
[[Property:publication_date|Fri, 07 Sep 2018 12:13:00 GMT]]
|
||||
[[Property:uuid|78393BBA-9B1E-4523-9881-3D83CEB6A952]]
|
||||
[[Property:weight|3000]]
|
||||
[[Property:title|Removing object while iterating on a LIST]]
|
||||
If you already have the object that you want to remove from the `LIST` you can easily use `prune` and `prune_all`. But if you want to remove objects while iterating on that `LIST`, depending on criteria on the objects contained in the `LIST`, here is what you can do.
|
||||
|
||||
First of all, if you think about removing an object while iterating, I do not recommend using an `across` loop. If you iterate on the list using a `from until loop end`, just remember to use the `LIST.forth` only when you do not use `LIST.remove`.
|
||||
|
||||
For example, let's say we have class `MY_CLASS` with an attribute `has_stopped` and that I want to remove every object of a `LIST` that has this attribute set to `True`. Here is what the code will look like.
|
||||
|
||||
<code>
|
||||
removing_stopped (a_list: LIST [MY_CLASS])
|
||||
-- Removing every closed element of `a_list`
|
||||
do
|
||||
from
|
||||
a_list.start
|
||||
until
|
||||
a_list.exhausted
|
||||
loop
|
||||
if a_list.item.has_stopped then
|
||||
a_list.remove
|
||||
else
|
||||
a_list.forth
|
||||
end
|
||||
end
|
||||
end
|
||||
</code>
|
||||
@@ -0,0 +1,64 @@
|
||||
[[Property:modification_date|Wed, 17 Apr 2019 14:09:18 GMT]]
|
||||
[[Property:publication_date|Thu, 06 Sep 2018 15:17:57 GMT]]
|
||||
[[Property:uuid|96077603-DD2D-4D8C-A486-AF4BD066613A]]
|
||||
[[Property:weight|2000]]
|
||||
[[Property:title|Iterating on a LIST]]
|
||||
There are three Eiffel mechanisms to iterate on every element of a `LIST`.
|
||||
|
||||
=== the `across` loop ===
|
||||
The `across` can be used on every `ITERABLE` object (including `LIST` objects).
|
||||
|
||||
<code>
|
||||
print_elements (a_list: LIST[INTEGER])
|
||||
-- Print every elements on `a_list`
|
||||
do
|
||||
across a_list as ic loop
|
||||
print (ic.item.out + "%N")
|
||||
end
|
||||
end
|
||||
</code>
|
||||
|
||||
Note that the temporary variable (`ic` in the example) represents an iterator of the `ITERABLE` object, and not directly an element like in many other languages (like the `for` structure in Python for example).
|
||||
|
||||
=== the `from until` loop syntax===
|
||||
This syntax offer more possibilities than the `across` loop, but is riskier.
|
||||
|
||||
<code>
|
||||
print_elements (a_list:LIST[INTEGER])
|
||||
-- Print every elements on `a_list`
|
||||
do
|
||||
from
|
||||
a_list.start
|
||||
until
|
||||
a_list.exhausted
|
||||
loop
|
||||
print (a_list.item.out + "%N")
|
||||
a_list.forth
|
||||
end
|
||||
end
|
||||
</code>
|
||||
|
||||
=== Using Eiffel agents and `{LIST}.do_all, ....` ===
|
||||
It is possible to use agents in conjunction with the `LIST` features `do_all`, `do_if`, `there_exists`, and `for_all` which are inherited from the class `LINEAR`.
|
||||
<code>
|
||||
list_traversal_agents
|
||||
-- Example of traversing a list with do_all
|
||||
local
|
||||
a_list: LINKED_LIST [STRING]
|
||||
do
|
||||
-- Insert some elements in a_list
|
||||
create a_list.make
|
||||
a_list.extend ("The Moon Is Full")
|
||||
a_list.extend ("Master charge")
|
||||
a_list.extend ("Black cat bone")
|
||||
a_list.do_all (agent {STRING}.append (" - Albert Collins"))
|
||||
a_list.do_all (agent print_element)
|
||||
end
|
||||
|
||||
print_element (a_element: STRING)
|
||||
-- Print `a_element` to standard output
|
||||
do
|
||||
io.put_string (a_element)
|
||||
io.put_new_line
|
||||
end
|
||||
</code>
|
||||
@@ -0,0 +1,54 @@
|
||||
[[Property:modification_date|Wed, 17 Apr 2019 14:06:41 GMT]]
|
||||
[[Property:publication_date|Wed, 17 Apr 2019 14:02:25 GMT]]
|
||||
[[Property:uuid|5CA34C5D-30F1-4D6F-9FE4-B555E541EA8C]]
|
||||
[[Property:weight|4000]]
|
||||
[[Property:title|Managing CTRL+C in console application]]
|
||||
Normally, if the user uses the CTRL+C keys, the Eiffel application detect it as an error and throw an exception of type <code>OPERATING_SYSTEM_SIGNAL_FAILURE</code>.
|
||||
|
||||
To manage the CTRL+C keys, you can use a <code>rescue</code> clause to detect the exception and a <code>retry</code> mechanism to cancel the exception handling done by the Eiffel runtime.
|
||||
|
||||
To detect the exception, you can <code>inherit</code> from the <code>EXCEPTIONS</code> class and use an attachment test on <code>Exception_manager.last_exception</code>.
|
||||
|
||||
Note that this code does not work on Windows. If used on Windows, the application will quit, but the <code>rescue</code> call is not launched.
|
||||
|
||||
<code>
|
||||
note
|
||||
description: "Show how to quit an application using CTRL+C (without trace)."
|
||||
author: "Louis Marchand"
|
||||
date: "Wed, 25 Apr 2018 23:12:33 +0000"
|
||||
revision: "0.1"
|
||||
|
||||
class
|
||||
APPLICATION
|
||||
|
||||
inherit
|
||||
EXCEPTIONS
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make
|
||||
-- Launch `Current'.
|
||||
local
|
||||
l_ctrl_c:BOOLEAN
|
||||
do
|
||||
if not l_ctrl_c then
|
||||
from until False loop
|
||||
io.standard_default.put_string ("Press CTRL+C%N")
|
||||
io.input.read_line
|
||||
end
|
||||
else
|
||||
io.standard_default.put_string ("%NClosing...%N")
|
||||
end
|
||||
rescue
|
||||
if attached {OPERATING_SYSTEM_SIGNAL_FAILURE}
|
||||
Exception_manager.last_exception then
|
||||
l_ctrl_c := True
|
||||
retry
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
</code>
|
||||
10
documentation/19.12/eiffel/Tutorials/Mini-HowTo/index.wiki
Normal file
10
documentation/19.12/eiffel/Tutorials/Mini-HowTo/index.wiki
Normal file
@@ -0,0 +1,10 @@
|
||||
[[Property:modification_date|Mon, 10 Sep 2018 09:04:15 GMT]]
|
||||
[[Property:publication_date|Mon, 10 Sep 2018 09:04:15 GMT]]
|
||||
[[Property:link_title|Mini How-tos]]
|
||||
[[Property:uuid|B2E4622A-2495-47DD-9C02-B9940A026EC1]]
|
||||
[[Property:weight|0]]
|
||||
[[Property:title|Mini How-tos]]
|
||||
In this section, you will find little how-tos that you can use to learn some very specific mechanics in Eiffel. Those how-tos are small by design and can be used to show very fundamental, or more advanced, mechanisms for beginners.
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,250 @@
|
||||
[[Property:title|ET: Agents]]
|
||||
[[Property:weight|-3]]
|
||||
[[Property:uuid|ba49a80d-5ddf-8b30-4943-528974fd0ddd]]
|
||||
Our last mechanism, agents, adds one final level of expressive power to the framework describe so far. Agents apply object-oriented concepts to the modeling of operations.
|
||||
|
||||
==Objects for operations==
|
||||
|
||||
Operations are not objects; in fact, object technology starts from the decision to separate these two aspects, and to choose object types, rather than the operations, as the basis for modular organization of a system, attaching each operation to the resulting modules -- the classes.
|
||||
|
||||
In a number of applications, however, we may need objects that represent operations, so that we can include them in object structures that some other piece of the software will later traverse to uncover the operations and, usually, execute them. Such "operation wrapper" objects, called agents, are useful in a number of application areas such as: <br/>
|
||||
* GUI (Graphical User Interface) programming, where we may associate an agent with a certain event of the interface, such as a mouse click at a certain place on the screen, to prescribe that if the event occurs -- a user clicks there -- it must cause execution of the agent's associated operation.
|
||||
* Iteration on data structures, where we may define a general-purpose routine that can apply an arbitrary operation to all the elements of a structure such as a list; to specify a particular operation to iterate, we will pass to the iteration mechanism an agent representing that operation.
|
||||
* Numerical computation, where we may define a routine that computes the integral of any applicable function on any applicable interval; to represent that function and pass its representation to the integration routine, we will use an agent.
|
||||
|
||||
|
||||
Operations in Eiffel are expressed as routines, and indeed every agent will have an associated routine. Remember, however, that the fundamental distinction between objects and operations remains: an agent is an object, and it is not a routine; it represents a routine. As further evidence that this is a proper data abstraction, note that the procedure <code>call</code>, available on all agents to call the associated routine, is only one of the features of agents. Other features may denote properties such as the class to which the routine belongs, its precondition and postcondition, the result of the last call for a function, the number of arguments.
|
||||
|
||||
==Building an agent==
|
||||
|
||||
In the simplest form, also one of the most common, you obtain an agent just by writing
|
||||
<code>
|
||||
agent r
|
||||
</code>
|
||||
|
||||
where <code>r</code> is the name of a routine of the enclosing class. This is an expression, which you may assign to a writable entity, or pass as argument to a routine. Here for example is how you will specify event handling in the style of the EiffelVision 2 GUI library:
|
||||
<code>
|
||||
your_icon.click_actions.extend (agent your_routine)
|
||||
</code>
|
||||
|
||||
This adds to the end of <code>your_icon.click_actions</code> -- the list of agents associated with the "click" event for <code>your_icon</code>, denoting an icon in the application's user interface -- an agent representing <code>your_routine</code>. Then when a user clicks on the associated icon at execution, the EiffelVision 2 mechanisms will call the procedure <code>call</code> on every agent of the list, which for this agent will execute <code>your_routine</code>. This is a simple way to associate elements of your application, more precisely its "business model" (the processing that you have defined, directly connected to the application's business domain), with elements of its GUI.
|
||||
|
||||
Similarly although in a completely different area, you may request the integration of a function <code>your_function</code> over the interval <code>0..1</code> through a call such as
|
||||
<code>
|
||||
your_integrator.integral (agent your_function, 0, 1)
|
||||
</code>
|
||||
|
||||
In the third example area cited above, you may call an iterator of EiffelBase through
|
||||
<code>
|
||||
your_list.do_all (agent your_proc)
|
||||
</code>
|
||||
|
||||
with <code>your_list</code> of a type such as <code>LIST [YOUR_TYPE]</code>. This will apply <code>your_proc</code> to every element of the list in turn.
|
||||
|
||||
The agent mechanism is type-checked like the rest of Eiffel; so the last example is valid if and only if <code>your_proc</code> is a procedure with one argument of type <code>YOUR_TYPE</code>.
|
||||
|
||||
==Operations on agents==
|
||||
|
||||
An agent <code>agent</code> <code>r</code> built from a procedure <code>r</code> is of type <code>PROCEDURE [T, ARGS]</code> where <code>T</code> represents the class to which <code>r</code> belongs and <code>ARGS</code> the type of its arguments. If <code>r</code> is a function of result type <code>RES</code>, the type is <code>FUNCTION [T, ARGS, RES]</code>. Classes <code>PROCEDURE</code> and <code>FUNCTION</code> are from the Kernel Library of EiffelBase, both inheriting from <code>ROUTINE [T, ARGS]</code>.
|
||||
|
||||
Among the features of <code>ROUTINE</code> and its descendants the most important are <code>call</code>, already noted, which calls the associated routine, and <code>item</code>, appearing only in <code>FUNCTION</code> and yielding the result of the associated function, which it obtains by calling <code>call</code>.
|
||||
|
||||
As an example of using these mechanisms, here is how the function <code>integral</code> could look like in our <code>INTEGRATOR</code> example class. The details of the integration algorithm (straight forward, and making no claims to numerical sophistication) do not matter, but you see the place were we evaluate the mathematical function associated with <code>f</code>, by calling <code>item</code> on <code>f</code>:
|
||||
<code>
|
||||
integral (f: FUNCTION [TUPLE [REAL], REAL]; low, high: REAL): REAL
|
||||
-- Integral of `f' over the interval [`low', `high']
|
||||
require
|
||||
meaningful_interval: low <= high
|
||||
local
|
||||
x: REAL
|
||||
do
|
||||
from
|
||||
x := low
|
||||
invariant
|
||||
x >= low
|
||||
x <= high + step
|
||||
-- Result approximates the integral over
|
||||
-- the interval [low, low.max (x - step)]
|
||||
until
|
||||
x > high
|
||||
loop
|
||||
Result := Result + step * f.item ([x]) -- Here item is applied to f
|
||||
x := x + step
|
||||
end
|
||||
end
|
||||
</code>
|
||||
|
||||
Function <code>integral</code> takes three arguments: the agent <code>f</code> representing the function to be integrated, and the two interval bounds. When we need to evaluate that function for the value <code>x</code>, in the line
|
||||
<code>
|
||||
Result := Result + step * f.item ([x])
|
||||
</code>
|
||||
|
||||
we don't directly pass <code>x</code> to <code>item</code>; instead, we pass a one-element tuple <code>[x]</code>, using the syntax for manifest tuples introduced in [[ET: Other Mechanisms#Tuple_types|"Tuple types"]] . You will always use tuples for the argument to <code>call</code> and <code>item</code>, because these features must be applicable to any routine, and so cannot rely on a fixed number of arguments. Instead they take a single tuple intended to contain all the arguments. This property is reflected in the type of the second actual generic parameter to <code>f</code>, corresponding to <code>ARGS</code> (the formal generic parameter of <code>FUNCTION</code>): here it's <code>TUPLE [REAL]</code> to require an argument such as <code>[x]</code>, where <code>x</code> is of type <code>REAL</code>.
|
||||
|
||||
Similarly, consider the agent that the call seen above:
|
||||
<code>
|
||||
your_icon.click_actions.extend (agent your_routine)
|
||||
</code>
|
||||
|
||||
added to an EiffelVision list. When the EiffelVision mechanism detects a mouse click event, it will apply to each element <code>item</code> of the list of agents, <code>your_icon.click_actions</code>, an instruction such as
|
||||
<code>
|
||||
item.call ([x, y])
|
||||
</code>
|
||||
|
||||
where <code>x</code> and <code>y</code> are the coordinates of the mouse clicking position. If <code>item</code> denotes the list element <code>agent</code> your_routine, inserted by the above call to <code>extend</code>, the effect will be the same as that of calling
|
||||
<code>
|
||||
your_routine (x, y)
|
||||
</code>
|
||||
|
||||
assuming that <code>your_routine</code> indeed takes arguments of the appropriate type, here <code>INTEGER</code> representing a coordinate in pixels. (Otherwise type checking would have rejected the call to <code>extend</code>.)
|
||||
|
||||
==Open and closed arguments==
|
||||
|
||||
In the examples so far, execution of the agent's associated routine, through <code>item</code> or <code>call</code>, passed exactly the arguments that a direct call to the routine would expect. You can have more flexibility. In particular, you may build an agent from a routine with more arguments than expected in the final call, and you may set the values of some arguments at the time you define the agent.
|
||||
|
||||
Assume for example that a cartographical application lets a user record the location of a city by clicking on the corresponding position on the map. The application may do this through a procedure
|
||||
<code>
|
||||
record_city (cn: STRING; pop: INTEGER; x, y: INTEGER)
|
||||
-- Record that the city of name `cn' is at coordinates
|
||||
-- `x' and `y' with population `pop'.
|
||||
</code>
|
||||
|
||||
Then you can associate it with the GUI through a call such as
|
||||
<code>
|
||||
map.click_actions.extend (agent record_city (name, population, ?, ?))
|
||||
</code>
|
||||
|
||||
assuming that the information on the <code>name</code> and the <code>population</code> has already been determined. What the agent denotes is the same as <code>agent</code> <code>your_routine</code> as given before, where <code>your_routine</code> would be a fictitious two-argument routine obtained from <code>record_city</code> -- a four-argument routine -- by setting the first two arguments once and for all to the values given, <code>name</code> and <code>population</code>.
|
||||
|
||||
In the agent <code>agent record_city (name, population, ?, ?)</code>, we say that these first two arguments, with their set values, are '''closed'''; the last two are '''open'''. The question mark syntax introduced by this example may only appear in agent expressions; it denotes open arguments. This means, by the way, that you may view the basic form used in the preceding examples, <code>agent your_routine</code>, as an abbreviation -- assuming your_routine has two arguments -- for <code>agent your_routine (?, ?)</code>. It is indeed permitted, to define an agent with all arguments open, to omit the argument list altogether; no ambiguity may result.
|
||||
|
||||
For type checking, <code>agent record_city (name, population, ?, ?)</code> and <code>agent your_routine (?, ?)</code> are acceptable in exactly the same situations, since both represent routines with two arguments. The type of both is
|
||||
<code>
|
||||
PROCEDURE [TUPLE [INTEGER, INTEGER]]
|
||||
</code>
|
||||
|
||||
where the tuple type specifies the open operands.
|
||||
|
||||
A completely closed agent, such as <code>agent your_routine (25, 32)</code> or <code>agent record_city (name, population, 25, 32)</code>, has the type <code>TUPLE</code>, with no parameters; you will call it with <code>call ([ ])</code>, using an empty tuple as argument.
|
||||
|
||||
The freedom to start from a routine with an arbitrary number of arguments, and choose which ones you want to close and which ones to leave open, provides a good part of the attraction of the agent mechanism. It means in particular that in GUI applications you can limit to the strict minimum the "glue" code (sometimes called the controller in the so-called MVC, Model-View Controller, scheme of GUI design) between the user interface and "business model" parts of a system. A routine such as <code>record_city</code> is a typical example of an element of the business model, uninfluenced -- as it should be -- by considerations of user interface design. Yet by passing it in the form of an agent with partially open and partially closed arguments, you may be able to use it directly in the GUI, as shown above, without any "controller" code.
|
||||
|
||||
As another example of the mechanism's versatility, we saw above an integral function that could integrate a function of one variable over an interval, as in
|
||||
<code>
|
||||
your_integrator.integral (agent your_function (0, 1))
|
||||
</code>
|
||||
|
||||
Now assume that <code>function3</code> takes three arguments. To integrate <code>function3</code> with two arguments fixed, you don't need a new <code>integral</code> function; just use the same <code>integral</code> as before, judiciously selecting what to close and what to leave open:
|
||||
<code>
|
||||
your_integrator.integral (agent function3 (3.5, ?, 6.0), 0, 1)
|
||||
</code>
|
||||
|
||||
==Open targets==
|
||||
|
||||
All the agent examples seen so far were based on routines of the enclosing class. This is not required. Feature calls, as you remember, were either unqualified, as in <code>f (x, y)</code>, or qualified, as in <code>a.g (x, y)</code>. Agents, too, have a qualified variant as in
|
||||
<code>
|
||||
agent a.g
|
||||
</code>
|
||||
|
||||
which is closed on its target <code>a</code> and open on the arguments. Variants such as <code>agent a.g (x, y)</code>, all closed, and <code>agent a.g (?, y)</code>, open on one argument, are all valid.
|
||||
|
||||
You may also want to make the target open. The question mark syntax could not work here, since it wouldn't tell us the class to which feature <code>g</code> belongs, known in the preceding examples from the type of <code>a</code>. As in creation expressions, we must list the type explicitly; the convention is the same: write the types in braces, as in
|
||||
<code>
|
||||
agent {SOME_TYPE}.g
|
||||
agent {SOME_TYPE}.g (?, ?)
|
||||
agent {SOME_TYPE}.g (?, y)
|
||||
</code>
|
||||
|
||||
The first two of these examples are open on the target and both operands; they mean the same. The third is closed on one argument, open on the other and on the target.
|
||||
|
||||
These possibilities give even more flexibility to the mechanism because they mean that an operation that needs agents with certain arguments open doesn't care whether they come from an argument or an operand of the original routine. This is particularly useful for iterators and means that if you have two lists
|
||||
<code>
|
||||
your_account_list: LIST [ACCOUNT]
|
||||
your_integer_list: LIST [INTEGER]
|
||||
</code>
|
||||
|
||||
you may write both
|
||||
<code>
|
||||
your_account_list.do_all (agent {ACCOUNT}.deposit_one_grand)
|
||||
your_integer_list.do_all (agent add_to_total)
|
||||
</code>
|
||||
|
||||
even though the two procedures used in the agents have quite different forms. We are assuming here that the first one, a feature of class <code>ACCOUNT</code>, is something like
|
||||
<code>
|
||||
deposit_one_grand
|
||||
-- Deposit one thousand into `Current'.
|
||||
do
|
||||
deposit (1000)
|
||||
end
|
||||
</code>
|
||||
|
||||
The procedure <code>deposit_one_grand</code> takes no arguments. In the <code>do_all</code> example above, its target is open. The target will be, in turn, each instance of <code>ACCOUNT</code> in <code>your_account_list</code>.
|
||||
|
||||
In contrast, the other routine, assumed to be a feature of the calling class, does take an argument <code>x</code>:
|
||||
<code>
|
||||
add_to_total (x: INTEGER)
|
||||
-- Add `x' to the value of `total'.
|
||||
do
|
||||
total := total + x
|
||||
end
|
||||
</code>
|
||||
|
||||
Here, <code>total</code> is assumed to be an integer attribute of the enclosing class. In the <code>do_all</code> example, each instance of <code>your_integer_list</code> will fill the argument <code>x</code> left open in <code>add_to_total</code>.
|
||||
|
||||
Without the versatility of playing with open and closed arguments for both the original arguments and target, you would have to write separate iteration mechanisms for these two cases. Here you can use a single iteration routine of <code>LIST</code> and similar classes of EiffelBase, <code>do_all</code>, for both purposes: <br/>
|
||||
* Depositing money on every account in a list of accounts.
|
||||
* Adding all the integers in a list of integers.
|
||||
|
||||
|
||||
==Inline agents==
|
||||
|
||||
In the agent discussion above, it has been assumed that there already exists some routine that we wish to represent with an agent. However, sometimes the only usage of such a routine could be as an agent ... that is, the routine does not make sense as a feature of the class in question. In these cases, we can use '''inline agents'''. With an inline agent we write the routine within the agent declaration.
|
||||
|
||||
If we consider the use of agents instead of class features in the two <code>do_all</code> examples in the previous section, the agents would be coded as follows:
|
||||
|
||||
<code>
|
||||
your_account_list.do_all
|
||||
(agent (a: ACCOUNT)
|
||||
do
|
||||
a.deposit (1000)
|
||||
end)
|
||||
</code>
|
||||
|
||||
and
|
||||
|
||||
<code>
|
||||
your_integer_list.do_all
|
||||
(agent (i: INTEGER)
|
||||
do
|
||||
total := total + i
|
||||
end)
|
||||
</code>
|
||||
|
||||
The syntax of the inline agent corresponds to the syntax of a routine. Immediately following the <code>agent</code> keyword are the formal arguments and in the case of functions the type for <code>Result</code>. Inline agents can have <code>local</code> entities, preconditions, and postconditions, just like any routine.
|
||||
|
||||
Inline agents do not have access to the local entities of the routine in which they are coded. So, if it is necessary to use the routine's local variables, they must be passed as arguments to the inline agent.
|
||||
|
||||
Here's an example of an inline agent which is a function. It is used in the context of a check to see if every element of <code>your_integer_list</code> is positive:
|
||||
|
||||
<code>
|
||||
your_integer_list.for_all
|
||||
(agent (i: INTEGER): BOOLEAN
|
||||
do
|
||||
Result := (i > 0)
|
||||
ensure
|
||||
definition: Result = (i > 0)
|
||||
end)
|
||||
</code>
|
||||
|
||||
Inline agents are interesting also as an implementation of the notion of [http://en.wikipedia.org/wiki/Closure_(computer_science) closures] in computer science.
|
||||
|
||||
|
||||
Agents provide a welcome complement to the other mechanisms of Eiffel. They do not conflict with them but, when appropriate -- as in the examples sketched in this section -- provide clear and expressive programming schemes, superior to the alternatives.
|
||||
|
||||
Compatibility note: earlier versions of the agent classes (ROUTINE, PROCEDURE, FUNCTION, PREDICATE) had an extra initial generic parameter, for which ANY was generally used. The compiler has been engineered to accept the old style in most cases.
|
||||
|
||||
|
||||
{{SeeAlso|[[Event Programming with Agents]] }}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,337 @@
|
||||
[[Property:title|ET: Design by Contract (tm), Assertions and Exceptions]]
|
||||
[[Property:weight|-8]]
|
||||
[[Property:uuid|2ef367c9-34d9-d45e-a722-163b39581405]]
|
||||
Eiffel directly implements the ideas of Design by Contract™ , which enhance software reliability and provide a sound basis for software specification, documentation and testing, as well as exception handling and the proper use of inheritance.
|
||||
|
||||
==Design by Contract™ basics==
|
||||
|
||||
A system -- a software system in particular, but the ideas are more general -- is made of a number of cooperating components. Design by Contract™ states that their cooperation should be based on precise specifications -- contracts -- describing each party's expectations and guarantees.
|
||||
|
||||
An Eiffel contract is similar to a real-life contract between two people or two companies, which it is convenient to express in the form of tables listing the expectations and guarantees. Here for example is how we could sketch the contract between a homeowner and the telephone company:
|
||||
|
||||
|
||||
{| border="1"
|
||||
|-
|
||||
| style="width=10%" |provide telephone service
|
||||
| style="width=35%" |'''OBLIGATIONS'''
|
||||
| style="width=35%" |'''BENEFITS'''
|
||||
|-
|
||||
| '''Client'''
|
||||
| (Satisfy precondition:) <br/>
|
||||
Pay bill.
|
||||
| (From postcondition:) <br/>
|
||||
Receive telephone service from Supplier.
|
||||
|-
|
||||
| '''Supplier'''
|
||||
| (Satisfy precondition:) <br/>
|
||||
Provide telephone service.
|
||||
| (From postcondition:) <br/>
|
||||
No need to provide anything if bill not paid.
|
||||
|}
|
||||
|
||||
|
||||
Note how the obligation for each of the parties maps onto a benefit for the other. This will be a general pattern.
|
||||
|
||||
The client's obligation, which protects the supplier, is called a '''precondition'''. It states what the client must satisfy before requesting a certain service. The client's benefit, which describes what the supplier must do (assuming the precondition was satisfied), is called a '''postcondition'''.
|
||||
|
||||
In addition to preconditions and postconditions, contract clauses include '''class invariants''', which apply to a class as a whole. More precisely a class invariant must be ensured by every creation procedure (or by the default initialization if there is no creation procedure), and maintained by every exported routine of the class.
|
||||
|
||||
==Expressing assertions==
|
||||
|
||||
Eiffel provides syntax for expressing preconditions (<code>require</code>), postconditions (<code>ensure</code>) and class invariants (<code>invariant</code>), as well as other assertion constructs studied later (see "[[ET: Instructions|Instructions]]" ): loop invariants and variants, check instructions.
|
||||
|
||||
Here is a partial update of class <code>ACCOUNT</code> with more assertions:
|
||||
<code>
|
||||
note
|
||||
description: "Simple bank accounts"
|
||||
|
||||
class
|
||||
ACCOUNT
|
||||
|
||||
feature -- Access
|
||||
|
||||
balance: INTEGER
|
||||
-- Current balance
|
||||
|
||||
deposit_count: INTEGER
|
||||
-- Number of deposits made since opening
|
||||
do
|
||||
... As before ...
|
||||
end
|
||||
|
||||
feature -- Element change
|
||||
|
||||
deposit (sum: INTEGER)
|
||||
-- Add `sum' to account.
|
||||
require
|
||||
non_negative: sum >= 0
|
||||
do
|
||||
... As before ...
|
||||
ensure
|
||||
one_more_deposit: deposit_count = old deposit_count + 1
|
||||
updated: balance = old balance + sum
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
all_deposits: DEPOSIT_LIST
|
||||
-- List of deposits since account's opening.
|
||||
|
||||
invariant
|
||||
consistent_balance: (all_deposits /= Void) implies
|
||||
(balance = all_deposits . total)
|
||||
zero_if_no_deposits: (all_deposits = Void) implies
|
||||
(balance = 0)
|
||||
|
||||
end -- class ACCOUNT
|
||||
</code>
|
||||
|
||||
Each assertion is made of one or more subclauses, each of them a boolean expression (with the additional possibility of the <code>old</code> construct). The effect of including more than one sub clause, as in the postcondition of <code>deposit</code> and in the invariant, is the same as connecting them through an <code>and</code>. Each clause may be preceded by a label, such as <code>consistent_balance</code> in the invariant, and a colon; the label is optional and does not affect the assertion's semantics, except for error reporting as explained in the next section, but including it systematically is part of the recommended style. The value of the boolean expression <code>a implies b</code> is true except if <code>a</code> is true and <code>b</code> false.
|
||||
|
||||
Because assertions benefit from the full power of boolean expressions, they may include function calls. This makes it possible to express sophisticated consistency conditions, such as " the graph contains no cycle", which would not be otherwise expressible through simple expressions, or even through first-order predicate calculus, but which are easy to implement as Eiffel functions returning boolean results.
|
||||
|
||||
===Preconditions===
|
||||
|
||||
The precondition of a routine expresses conditions that the routine is imposing on its clients. Here a call to <code>deposit</code> is correct if and only if the value of the argument is non-negative. The routine does not guarantee anything for a call that does not satisfy the precondition. It is in fact part of the Eiffel method that a routine body should '''never''' test for the precondition, since it is the client's responsibility to ensure it. (An apparent paradox of Design by Contract™, which is reflected in the bottom-right entries of the preceding and following contract tables, and should not be a paradox any more at the end of this discussion, is that one can get more reliable software by having fewer explicit checks in the software text.)
|
||||
|
||||
===Postconditions===
|
||||
|
||||
The postcondition of a routine expresses what the routine guaranteed to its clients for calls satisfying the precondition. The notation <code>old expression</code>, valid in postconditions ( <code>ensure</code> clauses) only, denotes the value that <code>expression</code> had on entry to the routine.
|
||||
|
||||
The precondition and postcondition state the terms of the contract between the routine and its clients, similar to the earlier example of a human contract:
|
||||
|
||||
|
||||
{| border="1"
|
||||
|-
|
||||
| style="width=10%" |<code>deposit</code>
|
||||
| style="width=35%"| '''OBLIGATIONS'''
|
||||
| style="width=35%" | '''BENEFITS'''
|
||||
|-
|
||||
| '''Client'''
|
||||
| (Satisfy precondition:) <br/>
|
||||
Use a non-negative argument.
|
||||
| (From postcondition:) <br/>
|
||||
Get deposits list and balance updated.
|
||||
|-
|
||||
| '''Supplier'''
|
||||
| (Satisfy precondition:) <br/>
|
||||
Update deposits list and balance.
|
||||
| (From postcondition:) <br/>
|
||||
No need to handle negative arguments.
|
||||
|}
|
||||
|
||||
===Class invariants===
|
||||
|
||||
The class invariant, as noted, applies to all features. It must be satisfied on exit by any creation procedure, and is implicitly added to both the precondition and postcondition of every exported routine. In this respect it is both good news and bad news for the routine implementer: good news because it guarantees that the object will initially be in a stable state, averting the need in the example to check that the total of <code>all_deposits</code> is compatible with the <code>balance</code>; bad news because, in addition to its official contract as expressed by its specific postcondition, every routine must take care of restoring the invariant on exit.
|
||||
|
||||
A requirement on meaningful contracts is that they should be in good faith: satisfiable by an honest partner. This implies a consistency rule: if a routine is exported to a client (either generally or selectively), any feature appearing in its precondition must also be available to that client. Otherwise -- for example if the precondition included <code>require n > 0</code>, where <code>n</code> is a secret attribute -- the supplier would be making demands that a good-faith client cannot possibly check for.
|
||||
|
||||
Note in this respect that guaranteeing a precondition does not necessarily mean, for the client, testing for it. Assuming <code>n</code> is exported, a call may test for the precondition
|
||||
<code>
|
||||
if x.n > 0 then
|
||||
x.r
|
||||
end
|
||||
</code>
|
||||
|
||||
possibly with an <code>else</code> part. But if the context of the call, in the client's code, implies that <code>n</code> is positive -- perhaps because some preceding call set it to the sum of two squares -- then there is no need for an <code>if</code> or similar construct.
|
||||
|
||||
{{note|In such a case, a <code>check</code> instruction as introduced later ( "[[ET: Instructions|Instructions]]" ) is recommended if the reason for omitting the test is non-trivial. }}
|
||||
|
||||
==Using contracts for built-in reliability==
|
||||
|
||||
What are contracts good for? Their first use is purely methodological. By applying a discipline of expressing, as precisely as possible, the logical assumptions behind software elements, you can write software whose reliability is built-in: software that is developed hand-in-hand with the rationale for its correctness.
|
||||
|
||||
This simple observation -- usually not clear to people until they have practiced Design by Contract™ thoroughly on a large-scale project -- brings as much change to software practices and quality as the rest of object technology.
|
||||
|
||||
==Run-time assertion monitoring==
|
||||
|
||||
Contracts in Eiffel are not just wishful thinking. They can be monitored at run time under the control of compilation options.
|
||||
|
||||
It should be clear from the preceding discussion that contracts are not a mechanism to test for special conditions, for example erroneous user input. For that purpose, the usual control structures ( <code>if deposit_sum > 0 then</code> ...) are available, complemented in applicable cases by the exception handling mechanism reviewed next. An assertion is instead a '''correctness condition''' governing the relationship between two software modules (not a software module and a human, or a software module and an external device). If <code>sum</code> is negative on entry to <code>deposit</code>, violating the precondition, the culprit is some other software element, whose author was not careful enough to observe the terms of the deal. Bluntly:
|
||||
|
||||
{{rule|name=Assertion Violation|text=A run-time assertion violation is the manifestation of a bug. }}
|
||||
|
||||
To be more precise: <br/>
|
||||
* A precondition violation signals a bug in the client, which did not observe its part of the deal.
|
||||
* A postcondition (or invariant) violation signals a bug in the supplier -- the routine -- which did not do its job.
|
||||
|
||||
That violations indicate bugs explains why it is legitimate to enable or disable assertion monitoring through mere compilation options: for a correct system -- one without bugs -- assertions will always hold, so the compilation option makes no difference to the semantics of the system.
|
||||
|
||||
But of course for an incorrect system the best way to find out where the bug is -- or just that there is a bug -- is often to monitor the assertions during development and testing. Hence the presence of the compilation options, which EiffelStudio lets you set separately for each class, with defaults at the system and cluster levels: <br/>
|
||||
* <code>no</code> : assertions have no run-time effect.
|
||||
* <code>require</code> : monitor preconditions only, on routine entry.
|
||||
* <code>ensure</code> : preconditions on entry, postconditions on exit.
|
||||
* <code>invariant</code> : same as <code>ensure</code>, plus class invariant on both entry and exit for qualified calls.
|
||||
* <code>all</code> : same as <code>invariant</code>, plus <code>check</code> instructions, loop invariants and loop variants.
|
||||
|
||||
|
||||
An assertion violation, if detected at run time under one of these options other than the first, will cause an exception ( [[ET: Design by Contract (tm), Assertions and Exceptions#exception_handling|"Exception handling"]] ). Unless the software has an explicit "retry" plan as explained in the discussion of exceptions, the violation will produce an exception trace and cause termination (or, in EiffelStudio, a return to the environment's browsing and debugging facilities at the point of failure). If present, the label of the violated sub clause will be displayed, to help identify the problem.
|
||||
|
||||
The default is <code>require</code>. This is particularly interesting in connection with the Eiffel method's insistence on reuse: with libraries such as EiffelBase, richly equipped with preconditions expressing terms of use, an error in the '''client software''' will often lead, for example through an incorrect argument, to violating one of these preconditions. A somewhat paradoxical consequence is that even an application developer who does not apply the method too well (out of carelessness, haste, indifference or ignorance) will still benefit from the presence of contracts in someone else's library code.
|
||||
|
||||
During development and testing, assertion monitoring should be turned on at the highest possible level. Combined with static typing and the immediate feedback of compilation techniques such as the Melting Ice Technology, this permits the development process mentioned in the section [[ET: The Software Process in Eiffel#Quality_and_functionality|"Quality and functionality"]], where errors are exterminated at birth. No one who has not practiced the method in a real project can imagine how many mistakes are found in this way; surprisingly often, a violation will turn out to affect an assertion that was just included for goodness' sake, the developer being convinced that it could never "possibly" fail to be satisfied.
|
||||
|
||||
By providing a precise reference (the description of what the software is supposed to do) against which to assess the reality (what the software actually does), Design by Contract™ profoundly transforms the activities of debugging, testing and quality assurance.
|
||||
|
||||
When releasing the final version of a system, it is usually appropriate to turn off assertion monitoring, or bring it down to the <code>require</code> level. The exact policy depends on the circumstances; it is a trade off between efficiency considerations, the potential cost of mistakes, and how much the developers and quality assurance team trust the product. When developing the software, however, you should always assume -- to avoid loosening your guard -- that in the end monitoring will be turned off.
|
||||
|
||||
==The contract form of a class==
|
||||
|
||||
Another application of assertions governs documentation. Environment mechanisms, such as clicking the <code>Form Contract</code> icon in Eiffelstudio, will produce, from a class text, an abstracted version which only includes the information relevant for client authors. Here is the contract form of class <code>ACCOUNT</code> in the latest version given:
|
||||
<code>
|
||||
note
|
||||
description: "Simple bank accounts"
|
||||
|
||||
class interface
|
||||
ACCOUNT
|
||||
|
||||
feature -- Access
|
||||
|
||||
balance: INTEGER
|
||||
-- Current balance
|
||||
|
||||
deposit_count: INTEGER
|
||||
-- Number of deposits made since opening
|
||||
|
||||
feature -- Element change
|
||||
|
||||
deposit (sum: INTEGER)
|
||||
-- Add `sum' to account.
|
||||
require
|
||||
non_negative: sum >= 0
|
||||
ensure
|
||||
one_more_deposit: deposit_count = old deposit_count + 1
|
||||
updated: balance = old balance + sum
|
||||
|
||||
invariant
|
||||
consistent_balance: balance = all_deposits.total
|
||||
|
||||
end -- class interface ACCOUNT
|
||||
</code>
|
||||
|
||||
The words <code>class interface</code> are used instead of just <code>class</code> to avoid any confusion with actual Eiffel text, since this is documentation, not executable software. (It is in fact possible to generate a compilable variant of the Contract Form in the form of a deferred class, a notion defined later.)
|
||||
|
||||
Compared to the full text, the Contract Form of a class (also called its "short form") retains all its interface properties, relevant to client authors: <br/>
|
||||
* Names and signatures (argument and result type information) for exported features.
|
||||
* Header comments of these features, which carry informal descriptions of their purpose. (Hence the importance, mentioned in "[[ET: Hello World|Hello World]]", of always including such comments and writing them carefully.)
|
||||
* Preconditions and postconditions of these features (at least the subclauses involving only exported features).
|
||||
* Class invariant (same observation).
|
||||
|
||||
|
||||
The following elements, however, are not in the Contract Form: any information about non-exported features; all the routine bodies (<code>do</code> clauses, or the <code>external</code> and <code>once</code> variants seen in [[ET: The Static Picture: System Organization#External_software|"External software"]] above and [[ET: Other Mechanisms#Once_routines_and_shared_objects|"Once routines and shared objects"]] below); assertion subclauses involving non-exported features; and some keywords not useful in the documentation.
|
||||
|
||||
In accordance with the Uniform Access principle (described in [[ET: The Dynamic Structure: Execution Model|"Objects, fields, values, and references"]] ), the Contract Form does not distinguish between attributes and argument-less queries. In the above example, <code>balance</code> could be one or the other, as it makes no difference to clients, except possibly for performance.
|
||||
|
||||
The Contract Form is the fundamental tool for using supplier classes in the Eiffel method. It enables client authors to reuse software elements without having to read their source code. This is a crucial requirement in large-scale industrial developments.
|
||||
|
||||
The Contract Form satisfies two key requirements of good software documentation: <br/>
|
||||
* It is truly abstract, free from the implementation details of what it describes and concentrating instead on its functionality.
|
||||
* Rather than being developed separately -- an unrealistic requirement, hard to impose on developers initially and becoming impossible in practice if we expect the documentation to remain up to date as the software evolves -- the documentation is extracted from the software itself. It is not a separate product but a different view of the same product. This prolongs the '''Single Product''' principle that lies at the basis of Eiffel's seamless development model (shown in [[ET: The Software Process in Eiffel|The Software Process in Eiffel]] ).
|
||||
|
||||
|
||||
The Contract Form is only one of the relevant views. EiffelStudio, for example, generates graphical representations of system structures, to show classes and their relations -- client, inheritance -- according to the conventions of BON (the Business Object Notation). In accordance with the principles of seamlessness and reversibility, EiffelStudio lets you both work on the text, producing the graphics on the fly, or work on the graphics, updating the text on the fly; you can alternate as you wish between these two modes. The resulting process is quite different from more traditional approaches based on separate tools: an analysis and CASE workbench, often based on UML, to deal with an initial "bubble-and-arrow" description; and a separate programming environment, to deal with implementation aspects only. In Eiffel the environment provides consistent, seamless support from beginning to end.
|
||||
|
||||
The Contract Form -- or its variant the Flat-Contract Form, which takes account of inheritance ( [[ET: Inheritance#Flat_and_Flat-Contract_Forms|"Flat and Flat-Contract Forms"]] ) are the standard form of library documentation, used extensively, for example, in the book <span> [http://www.eiffel.com/services/training/books.html Reusable Software] </span> (see bibliography). Assertions play a central role in such documentation by expressing the terms of the contract. As demonstrated a contrario by the widely publicized $500-million crash of the Ariane-5 rocket launcher in June of 1996, due to the incorrect reuse of a software module from the Ariane-4 project, '''reuse without a contract documentation''' is the path to disaster. Non-reuse would, in fact, be preferable.
|
||||
|
||||
==Exception handling==
|
||||
|
||||
Another application of Design by Contract™ governs the handling of unexpected cases. The vagueness of many discussions of this topic follows from the lack of a precise definition of terms such as "exception". With Design by Contract™ we are in a position to be specific: <br/>
|
||||
* Any routine has a contract to achieve.
|
||||
* Its body defines a strategy to achieve it -- a sequence of operations, or some other control structure involving operations. Some of these operations are calls to routines, with their own contracts; but even an atomic operation, such as the computation of an arithmetic operation, has an implicit contract, stating that the result will be representable.
|
||||
* Any one of these operations may <code>fail</code>, that is to say be unable to meet its contract; for example an arithmetic operation may produce an overflow (a non-representable result).
|
||||
* The failure of an operation is an '''exception''' for the routine that needed the operation.
|
||||
* As a result the routine may fail too -- causing an exception in its own caller.
|
||||
|
||||
|
||||
|
||||
Note the precise definitions of the two key concepts, failure and exception. Although failure is the more basic one -- since it is defined for atomic, non-routine operations -- the definitions are mutually recursive, since an exception may cause a failure of the recipient routine, and a routine's failure causes an exception in its own caller.
|
||||
|
||||
Why state that an exception "may" cause a failure? It is indeed possible to "rescue" a routine from failure in the case of an exception, by equipping it with a clause labeled <code>rescue</code>, as in:
|
||||
<code>
|
||||
read_next_character (f: FILE)
|
||||
-- Make next character available in last_character.
|
||||
-- If impossible, set failed to True.
|
||||
require
|
||||
readable: file.readable
|
||||
local
|
||||
impossible: BOOLEAN
|
||||
do
|
||||
if impossible then
|
||||
failed := True
|
||||
else
|
||||
last_character := low_level_read_function (f)
|
||||
end
|
||||
rescue
|
||||
impossible := True
|
||||
retry
|
||||
end
|
||||
</code>
|
||||
|
||||
This example includes the only two constructs needed for exception handling: <code>rescue</code> and <code>retry</code>. A <code>retry</code> instruction is only permitted in a rescue clause; its effect is to start again the execution of the routine, without repeating the initialization of local entities (such as <code>impossible</code> in the example, which was initialized to <code>False</code> on first entry). Features <code>failed</code> and <code>last_character</code> are assumed to be attributes of the enclosing class.
|
||||
|
||||
This example is typical of the use of exceptions: as a last resort, for situations that should not occur. The routine has a precondition, <code>file.readable</code>, which ascertains that the file exists and is accessible for reading characters. So clients should check that everything is fine before calling the routine. Although this check is almost always a guarantee of success, a rare combination of circumstances could cause a change of file status (because a user or some other system is manipulating the file) between the check for <code>readable</code> and the call to <code>low_level_read_function</code>. If we assume this latter function will fail if the file is not readable, we must catch the exception.
|
||||
|
||||
A variant would be
|
||||
<code>
|
||||
local
|
||||
attempts: INTEGER
|
||||
do
|
||||
if attempts < Max_attempts then
|
||||
last_character := low_level_read_function (f)
|
||||
else
|
||||
failed := True
|
||||
end
|
||||
rescue
|
||||
attempts := attempts + 1
|
||||
retry
|
||||
end
|
||||
</code>
|
||||
|
||||
which would try again up to <code>Max_attempts</code> times before giving up.
|
||||
|
||||
The above routine, in either variant, never fails: it always fulfills its contract, which states that it should either read a character or set <code>failed</code> to record its inability to do so. In contrast, consider the new variant
|
||||
<code>
|
||||
local
|
||||
attempts: INTEGER
|
||||
do
|
||||
last_character := low_level_read_function (f)
|
||||
rescue
|
||||
attempts := attempts + 1
|
||||
if attempts < Max_attempts then
|
||||
retry
|
||||
end
|
||||
end
|
||||
</code>
|
||||
|
||||
with no more role for <code>failed</code>. In this case, after <code>Max_attempts</code> unsuccessful attempts, the routine will execute its <code>rescue</code> clause to the end, with no <code>retry</code> (the <code>if</code> having no <code>else</code> clause). This is how a routine '''fails'''. It will, as noted, pass on the exception to its caller.
|
||||
|
||||
Such a rescue clause should, before terminating, restore the invariant of the class so that the caller and possible subsequent <code>retry</code>attempts from higher up find the objects in a consistent state. As a result, the rule for an absent <code>rescue</code> clause -- the case for the vast majority of routines in most systems -- is that it is equivalent to
|
||||
<code>
|
||||
rescue
|
||||
default_rescue
|
||||
</code>
|
||||
|
||||
where procedure <code>default_rescue</code> comes from <code>ANY</code>, where it is defined to do nothing; in a system built for robustness, classes subject to non-explicitly-rescued exceptions should redefine <code>default_rescue</code> (perhaps using a creation procedure, which is bound by the same formal requirement) so that it will always restore the invariant.
|
||||
|
||||
Behind Eiffel's exception handling scheme lies the principle -- at first an apparent platitude, but violated by many existing mechanisms -- that a routine should '''either succeed or fail'''. This is in turn a consequence of Design by Contract™ principles: succeeding means being able to fulfill the contract, possibly after one or more <code>retry</code>; failure is the other case, which must always trigger an exception in the caller. Otherwise it would be possible for a routine to miss its contract and yet return to its caller in a seemingly normal state. That is the worst possible way to handle an exception.
|
||||
|
||||
Concretely, exceptions may result from the following events: <br/>
|
||||
* A routine failure ( <code>rescue</code> clause executed to the end with no <code>retry</code>), as just seen.
|
||||
* Assertion violation, if for a system that runs with assertion monitoring on.
|
||||
* Attempt to call a feature on a void reference: <code>x.f (...)</code>, the fundamental computational mechanism, can only work if <code>x</code> is attached to an object, and will cause an exception otherwise.
|
||||
* Developer exception, as seen next.
|
||||
* Operating system signal:arithmetic overfolow; no memory available for a requested creation or twin -- even after garbage collection has rummaged everything to find some space. (But no C/C++-like "wrong pointer address", which cannot occur thanks to the statically typed nature of Eiffel.)
|
||||
|
||||
|
||||
It is sometimes useful, when handling exceptions in <code>rescue</code> clauses, to ascertain the exact nature of the exception that got the execution there. For this it is suffices to inherit from the Kernel Library class <code>EXCEPTIONS</code>, which provides queries such as <code>exception</code>, giving the code for the last exception, and symbolic names ( [[ET: Other Mechanisms#Constant_attributes|"Constant attributes"]] ) for all such codes, such as <code>No_more_memory</code>. You can then process different exceptions differently by testing <code>exception</code> against various possibilities. The method strongly suggests, however, that exception handling code should remain simple; a complicated algorithm in a <code>rescue</code> clause is usually a sign that the mechanism is being misused. Class <code>EXCEPTIONS</code> also provides various facilities for fine-tuning the exception facilities, such as a procedure <code>raise</code> that will explicitly trigger a "developer exception" with a code that can then be detected and processed. Exception handling helps produce Eiffel software that is not just correct but robust, by planning for cases that should not normally arise, but might out of Murphy's law, and ensuring they do not affect the software's basic safety and simplicity.
|
||||
|
||||
==Other applications of Design by Contract™==
|
||||
|
||||
The Design by Contract™ ideas pervade the Eiffel method. In addition to the applications just mentioned, they have two particularly important consequences: <br/>
|
||||
* They make it possible to use Eiffel for analysis and design. At a high level of abstraction, it is necessary to be precise too. With the exception of BON, object-oriented analysis and design methods tend to favor abstraction over precision. Thanks to assertions, it is possible to express precise properties of a system ("At what speed should the alarm start sounding?") without making any commitment to implementation. The discussion of deferred classes ( [[ET: Inheritance#Applications_of_deferred_classes|"Applications of deferred classes"]] ) will show how to write a purely descriptive, non-software model in Eiffel, using contracts to describe the essential properties of a system without any computer or software aspect.
|
||||
* Assertions also serve to control the power of inheritance-related mechanisms -- redeclaration, polymorphism, dynamic binding -- and channel them to correct uses by assigning the proper semantic limits. See [[ET: Inheritance#Inheritance_and_contracts|"Inheritance and contracts"]] .
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,697 @@
|
||||
[[Property:title|ET: The Dynamic Structure: Execution Model]]
|
||||
[[Property:weight|-10]]
|
||||
[[Property:uuid|1f3f2707-9129-4dca-76c7-157143d7ae74]]
|
||||
A system with a certain static structure describes a set of possible executions. The run-time model governs the structure of the data (objects) created during such executions.
|
||||
|
||||
The properties of the run-time model are not just of interest to implementers; they also involve concepts directly relevant to the needs of system modelers and analysts at the most abstract levels.
|
||||
|
||||
==Objects, fields, values, and references==
|
||||
|
||||
A class was defined as the static description of a type of run-time data structures. The data structures described by a ca class are called '''instances''' of the class, which in turn is called their '''generating class''' (or just "generator"). An instance of <code>ACCOUNT</code> is a data structure representing a bank account; an instance of <code>LINKED_LIST</code> is a data structure representing a linked list.
|
||||
|
||||
An '''object''', as may be created during the execution of a system, is an instance of some class of the system.
|
||||
|
||||
Classes and objects belong to different worlds: a class is an element of the software text; an object is a data structure created during execution. Although is possible to define a class whose instances represent classes, this does not eliminate the distinction between a static, compile-time notion, class, and a dynamic, run-time notion, object.
|
||||
|
||||
An object is either an atomic object (integer, real, boolean, double) or a composite object made of a number of '''fields''', represented by adjacent rectangles on the conventional run-time diagrams:
|
||||
|
||||
|
||||
[[Image:tutorial-5]]
|
||||
|
||||
|
||||
Each field is a '''value'''. A value can be either an object or an object reference: <br/>
|
||||
* When a field is an object, it will in most cases be an atomic object, as on the figure where the first field from the top is an integer and the third a character. But a field can also be a composite object, in which case it is called a '''subobject'''.
|
||||
* A '''reference''' is either void or uniquely identifies an object, to which it is said to be '''attached'''. In the preceding figure the second field from the top is a reference -- attached in this case, as represented by the arrow, to the enclosing object itself. The bottom field is a void reference.
|
||||
|
||||
|
||||
==Features==
|
||||
|
||||
|
||||
[[Image:tutorial-6]]
|
||||
|
||||
|
||||
A feature, as noted, is an operation available on instances of a class. A feature can be either an '''attribute''' or a '''routine'''. This classification, which you can follow by starting from the right on the figure above, is based on implementation considerations:
|
||||
* An attribute is a feature implemented through memory: it describes a field that will be found in all instances of the class. For example class <code>ACCOUNT</code> may have an attribute <code>balance</code>; then all instances of the class will have a corresponding field containing each account's current balance.
|
||||
* A routine describes a computation applicable to all instances of the class. <code>ACCOUNT</code> may have a routine <code>withdraw</code> .
|
||||
* Routines are further classified into '''functions''', which will return a result, and '''procedures''', which will not. Routine <code>withdraw</code> will be a procedure; an example of function may be <code>highest_deposit</code>, which returns the highest deposit made so far to the account.
|
||||
|
||||
If we instead take the viewpoint of the '''clients''' of a class (the classes relying on its feature), you can see the relevant classification by starting from the left on the figure:
|
||||
* '''Commands''' have no result, and may modify an object. They may only be procedures.
|
||||
* '''Queries''' have a result: they return information about an object. You may implement a query as either an attribute (by reserving space for the corresponding information in each instance of the class, a memory-based solution) or a function (a computation-based solution). An attribute is only possible for a query without argument, such as <code>balance</code>; a query with arguments, such as <code>balance_on (d)</code> , returning the balance at date <code>d</code>, can only be a function.
|
||||
|
||||
From the outside, there is no difference between a query implemented as an attribute and one implemented as a function: to obtain the balance of an account <code>a</code>, you will always write <code>a.balance</code>. In the implementation suggested above, <code>a</code> is an attribute, so that the notation denotes an access to the corresponding object field. But it is also possible to implement <code>a</code> as a function, whose algorithm will explore the lists of deposits and withdrawals and compute their accumulated value. To the clients of the class, and in the official class documentation as produced by the environment tools, the difference is not visible.
|
||||
|
||||
This principle of '''Uniform Access''' is central to Eiffel's goals of extendibility, reusability and maintainability: you can change the implementation without affecting clients; and you can reuse a class without having to know the details of its features' implementations. Most object-oriented languages force clients to use a different notation for a function call and an attribute access. This violates Uniform Access and is an impediment to software evolution, turning internal representation changes into interface changes that may disrupt large parts of a system.
|
||||
|
||||
==A simple class==
|
||||
|
||||
The following simple class text illustrates the preceding concepts
|
||||
<code>
|
||||
note
|
||||
description: "Simple bank accounts"
|
||||
|
||||
class
|
||||
ACCOUNT
|
||||
|
||||
feature -- Access
|
||||
|
||||
balance: INTEGER
|
||||
-- Current balance
|
||||
|
||||
deposit_count: INTEGER
|
||||
-- Number of deposits made since opening
|
||||
do
|
||||
if all_deposits /= Void then
|
||||
Result := all_deposits.count
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Element change
|
||||
|
||||
deposit (sum: INTEGER)
|
||||
-- Add `sum' to account.
|
||||
do
|
||||
if all_deposits = Void then
|
||||
create all_deposits
|
||||
end
|
||||
|
||||
all_deposits.extend (sum)
|
||||
balance := balance + sum
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
all_deposits: DEPOSIT_LIST
|
||||
-- List of deposits since account's opening.
|
||||
|
||||
invariant
|
||||
consistent_balance:
|
||||
(all_deposits /= Void) implies (balance = all_deposits.total)
|
||||
zero_if_no_deposits:
|
||||
(all_deposits = Void) implies (balance = 0)
|
||||
|
||||
end -- class ACCOUNT
|
||||
</code>
|
||||
|
||||
(The <code>{NONE}</code> qualifier and the <code>invariant</code> clause, used here to make the example closer to a real class, will be explained shortly. <code>DEPOSIT_LIST</code> refers to another class, which can be written separately using library classes.)
|
||||
|
||||
It's easy to deduce, from a feature's syntactic appearance, the category to which it belongs. Here:
|
||||
* Only <code>deposit</code> and <code>deposit_count</code>, which include a <code>do</code> ... clause, are routines.
|
||||
* <code>balance</code> and <code>all_deposits</code>, which are simply declared with a type, are attributes. Note that even for attributes it is recommended to have a header comment.
|
||||
* Routine <code>deposit_count</code> is declared as returning a result (of type <code>INTEGER</code>); so it is a function.
|
||||
* Routine <code>deposit</code> has no such result and hence is a procedure.
|
||||
|
||||
==Creating and initializing objects==
|
||||
|
||||
Classes, as noted, are a static notion. Objects appear at run time; they are created explicitly. Here is the basic instruction to create an object of type <code>ACCOUNT</code> and attach it to <code>x</code>:
|
||||
<code>
|
||||
create x
|
||||
</code>
|
||||
|
||||
assuming that <code>x</code> has been declared of type <code>ACCOUNT</code>. Such an instruction must be in a routine of some class -- the only place where instructions can appear -- and its effect at run time will be threefold: create a new object of type <code>ACCOUNT</code>; initialize its fields to default values; and attach the value of <code>x</code> to it. Here the object will have two fields corresponding to the two attributes of the generating class: an integer for <code>balance</code>, which will be initialized to 0, and a reference for <code>all_deposits</code>, which will be initialized to a void reference:
|
||||
|
||||
|
||||
[[Image:tutorial-7]]
|
||||
|
||||
|
||||
The language specifies default initialization values for all possible types:
|
||||
{|
|
||||
|-
|
||||
|
|
||||
'''Type'''
|
||||
|
||||
|
|
||||
|
|
||||
'''Default value'''
|
||||
|
||||
|-
|
||||
|
|
||||
<code>INTEGER</code>, <code>REAL</code>, <code>DOUBLE</code>
|
||||
|
||||
|
|
||||
|
|
||||
Zero
|
||||
|
||||
|-
|
||||
|
|
||||
<code>BOOLEAN</code>
|
||||
|
||||
|
|
||||
|
|
||||
False
|
||||
|
||||
|-
|
||||
|
|
||||
<code>CHARACTER</code>
|
||||
|
||||
|
|
||||
|
|
||||
Null
|
||||
|
||||
|-
|
||||
|
|
||||
Reference types (such as ACCOUNT and <code>DEPOSIT_LIST</code>)
|
||||
|
||||
|
|
||||
|
|
||||
Void reference
|
||||
|
||||
|-
|
||||
|
|
||||
Composite expanded types (see next)
|
||||
|
||||
|
|
||||
|
|
||||
Same rules, applied recursively to all fields
|
||||
|
||||
|}
|
||||
|
||||
It is possible to override the initialization values by providing -- as in the earlier example of class <code>HELLO</code> -- one or more creation procedures. For example we might change <code>ACCOUNT</code> to make sure that every account is created with an initial deposit:
|
||||
<code>
|
||||
note
|
||||
description : "Simple bank accounts, initialized with a first deposit"
|
||||
|
||||
class
|
||||
ACCOUNT1
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Initialization
|
||||
|
||||
make (sum: INTEGER)
|
||||
-- Initialize account with `sum' .
|
||||
do
|
||||
deposit (sum)
|
||||
end
|
||||
|
||||
-- The rest of the class as for ACCOUNT
|
||||
|
||||
end -- class ACCOUNT1
|
||||
</code>
|
||||
|
||||
A <code>create</code> clause may list zero or more (here just one) procedures of the class.
|
||||
|
||||
{{info|Note the use of the same keyword, <code>create</code> , for both a creation clause, as here, and creation instructions such as <code>create x</code> . }}
|
||||
|
||||
In this case the original form of creation instruction, <code>create x</code> , is not valid any more for creating an instance of <code>ACCOUNT1</code>; you must use the form
|
||||
<code>
|
||||
create x.make (2000)</code>
|
||||
|
||||
known as a creation call. Such a creation call will have the same effect as the original form -- creation, initialization, attachment to -- <code>x</code> followed by the effect of calling the selected creation procedure, which here will call <code>deposit</code> with the given argument.
|
||||
|
||||
Note that in this example all that <code>make</code> does is to call <code>deposit</code>. So an alternative to introducing a new procedure <code>make</code> would have been simply to introduce a creation clause of the form <code>create deposit</code> , elevating <code>deposit</code> to the status of creation procedure. Then a creation call would be of the form <code>create x.deposit (2000)</code> .
|
||||
|
||||
{{info|Some variants of the basic creation instruction will be reviewed later: instruction with an explicit type; creation expressions. See [[ET: Other Mechanisms#Creation_variants|"Creation variants"]] . }}
|
||||
|
||||
==Entities==
|
||||
|
||||
The example assumed <code>x</code> declared of type <code>ACCOUNT</code> (or <code>ACCOUNT1</code>). Such an <code>x</code> is an example of '''entity''', a notion generalizing the well-known concept of variable. An entity is a name that appears in a class text to represent possible run-time values (a value being, as defined earlier, an object or a reference). An entity is one of the following: <br/>
|
||||
* An attribute of the enclosing class, such as <code>balance</code> and <code>all_deposits</code>.
|
||||
* A formal argument of a routine, such as <code>sum</code> for <code>deposit</code> and <code>make</code>.
|
||||
* A local entity declared for the internal needs of a routine.
|
||||
* The special entity <code>Result</code> in a function.
|
||||
|
||||
|
||||
The third case, local entities, arises when a routine needs some auxiliary values for its computation. Here is an example of the syntax:
|
||||
<code>
|
||||
deposit (sum: INTEGER)
|
||||
-- Add sum to account.
|
||||
local
|
||||
new: AMOUNT
|
||||
do
|
||||
create new.make (sum)
|
||||
all_deposits.extend (new)
|
||||
balance := balance + sum
|
||||
end
|
||||
</code>
|
||||
|
||||
This example is a variant of <code> deposit </code> for which we assume that the elements of a <code>DEPOSIT_LIST</code> such as <code>all_deposits</code> are no longer just integers, but objects, instances of a new class, <code>AMOUNT</code>. Such an object will contain an integer value, but possibly other information as well. So for the purpose of procedure <code>deposit</code> we create an instance of <code>AMOUNT</code> and insert it, using procedure <code>extend</code>, into the list <code>all_deposits</code>. The object is identified through the local entity <code>new</code>, which is only needed within each execution of the routine (as opposed to an attribute, which yields an object field that will remain in existence for as long as the object).
|
||||
|
||||
The last case of entity, <code>Result</code>, serves to denote, within the body of a function, the final result to be returned by that function. This was illustrated by the function <code>deposit_count</code>, which read
|
||||
<code>
|
||||
deposit_count: INTEGER
|
||||
-- Number of deposits made since opening (provisional version)
|
||||
do
|
||||
if all_deposits /= Void then
|
||||
Result := all_deposits.count
|
||||
end
|
||||
end
|
||||
</code>
|
||||
|
||||
The value returned by any call will be the value of the expression <code>all_deposits.count</code> (to be explained in detail shortly) for that call, unless <code>all_deposits</code> is a <code>Void</code> reference ( <code>/=</code> means "not equal").
|
||||
|
||||
The default initialization rules seen earlier for attributes (see the table above) also serve to initialize local entities and <code>Result</code> on routine entry. So in the last example, if <code>all_deposits</code> is void (as in the case on initialization with the class as given so far), <code>Result</code> keeps its default value of 0, which will be returned as the result of the function.
|
||||
|
||||
==Calls==
|
||||
|
||||
Apart from object creation, the basic computational mechanism, in the object-oriented style of computation represented by Eiffel, is feature call. In its basic form, it appears as
|
||||
<code lang="text">
|
||||
target.feature (argument1, ...)
|
||||
</code>
|
||||
|
||||
where <code>target</code> is an entity or more generally an expression, <code lang="text">feature</code> is a feature name, and there may be zero or more <code>argument</code> expressions. In the absence of any <code>argument</code> the part in parentheses should be removed.
|
||||
|
||||
We have already seen such calls. If the <code lang="text">feature</code> denotes a procedure, the call is an instruction, as in
|
||||
<code>
|
||||
all_deposits.extend (new)
|
||||
</code>
|
||||
|
||||
If <code lang="text">feature</code> denotes a query (function or attribute), the call is an expression, as in the right-hand side of
|
||||
<code>
|
||||
Result := all_deposits.count
|
||||
</code>
|
||||
|
||||
Following the principle of Uniform Access (mentioned earlier in the section ''Objects, fields, values, and references''), this form is the same for calls to attributes and to functions without arguments. In this example, feature <code>count</code> from class <code>DEPOSIT_LIST</code> may indeed be implemented in either of these two ways: we can keep a <code>count </code> field in each list, updating it for each insertion and removal; or we can compute <code>count</code>, whenever requested, by traversing the list and counting the number of items.
|
||||
|
||||
In the case of a routine with arguments -- procedure or function -- the routine will be declared, in its class, as
|
||||
<code>
|
||||
some_feature (formal_1: TYPE_1; ...)
|
||||
do
|
||||
...
|
||||
end
|
||||
</code>
|
||||
|
||||
meaning that, at the time of each call, the value of each formal will be set to the corresponding actual (<code>formal_1</code> to <code>argument_1</code> and so on).
|
||||
|
||||
In the routine body, it is not permitted to change the value of a formal argument, although it is possible to change the value of an attached object through a procedure call such as <code>formal_1.some_procedure ( ... )</code> .
|
||||
|
||||
==Infix and prefix notations==
|
||||
|
||||
Basic types such as <code>INTEGER</code> are, as noted, full-status citizens of Eiffel's type system, and so are declared as classes (part of the Kernel Library). <code>INTEGER</code>, for example, is characterized by the features describing integer operations: plus, minus, times, division, less than, and so on.
|
||||
|
||||
With the dot notation seen so far, this would imply that simple arithmetic operations would have to be written with a syntax such as
|
||||
<code>
|
||||
i.plus (j)
|
||||
</code>
|
||||
instead of the usual
|
||||
<code>
|
||||
i + j
|
||||
</code>
|
||||
This would be awkward. Infix and prefix notations solve the problem, reconciling the object-oriented view of computation with common notational practices of mathematics. The addition function is declared in class <code>INTEGER</code> as
|
||||
<code>
|
||||
plus alias "+" (other: INTEGER): INTEGER
|
||||
do
|
||||
...
|
||||
end
|
||||
</code>
|
||||
|
||||
Such a feature has all the properties and prerogatives of both normal "identifier-dot" notation and infix notation. This allowing invoking <code>plus</code> using either notation: <code>i.plus (j)</code> or <code>i + j</code> . A feature such as <code>plus</code> allowing infix notation must be a function, and take exactly one argument.
|
||||
|
||||
Prefix notation is allowed as well. A function can be declared as <code>opposite alias "-" </code>, with no argument, permitting calls of the form <code>-3</code> rather than <code>(3).opposite</code> .
|
||||
|
||||
Predefined library classes covering basic types such as <code>INTEGER</code>, <code>CHARACTER</code>, <code>BOOLEAN</code>, <code>REAL</code>, <code>DOUBLE</code> are known to the Eiffel compiler, so that a call of the form <code>j + i</code>, although conceptually equivalent to a routine call, can be processed just as efficiently as the corresponding arithmetic expression in an ordinary programming language. This brings the best of both worlds: conceptual simplicity, enabling Eiffel developers, when they want to, to think of integers and the like as objects; and efficiency as good as in lower-level approaches.
|
||||
|
||||
Infix and prefix notations are available to any class, not just the basic types' predefined classes. For example a graphics class could use the name <code>distance alias "|-|"</code> for a function computing the distance between two points, to be used in expressions such as
|
||||
<code>
|
||||
point1 |-| point2
|
||||
</code>
|
||||
|
||||
==Type declaration==
|
||||
|
||||
Every entity appearing in an Eiffel text is declared as being of a certain type, using the syntax already encountered in the above examples:
|
||||
<code>
|
||||
entity_name: TYPE_NAME
|
||||
</code>
|
||||
|
||||
This applies to attributes, formal arguments of routines and local entities. You will also declare the result type for a function, as in the earlier example
|
||||
<code>
|
||||
deposit_count: INTEGER ...
|
||||
</code>
|
||||
|
||||
Specifying such a function result type also declares, implicitly, the type for <code>Result</code> as used in the function's body.
|
||||
|
||||
What is a type? With the elements seen so far, every type is a <code>class</code> . <code>INTEGER</code>, used in the declaration of <code>deposits_count</code>, is, as we have seen, a library class; and the declaration <code>all_deposits: DEPOSIT_LIST</code> assumes the existence of a class <code>DEPOSIT_LIST</code> .
|
||||
|
||||
Three mechanisms introduced below -- expanded types, genericity, and anchored declarations -- will generalize the notion of type slightly. But they do not change the fundamental property that '''every type is based on a class''', called the type's '''base class'''. In the examples seen so far, each type is a class, serving as its own base class.
|
||||
|
||||
An instance of a class <code>C</code> is also called "an object of type <code>C</code>".
|
||||
|
||||
==Type categories==
|
||||
|
||||
It was noted above that a value is either an object or a reference. This corresponds to two kinds of type: reference types and expanded types.
|
||||
|
||||
If a class is declared as just
|
||||
<code>
|
||||
class CLASS_NAME ...
|
||||
</code>
|
||||
|
||||
it defines a reference type. The entities declared of that type will denote references. So in the declaration
|
||||
<code>
|
||||
x: ACCOUNT
|
||||
</code>
|
||||
|
||||
the possible run-time values for <code> x </code> are references, which will be either void or attached to instances of class <code>ACCOUNT</code> .
|
||||
|
||||
Instead of <code>class</code>, however, you may use the double keyword <code>expanded class</code> , as in the EiffelBase class definition
|
||||
<code>
|
||||
note
|
||||
description : "Integer values"
|
||||
|
||||
expanded class
|
||||
INTEGER
|
||||
|
||||
feature -- Basic operations
|
||||
|
||||
plus alias "+" (other: INTEGER): INTEGER
|
||||
do
|
||||
...
|
||||
end
|
||||
|
||||
... Other feature declarations ...
|
||||
|
||||
end -- class INTEGER
|
||||
</code>
|
||||
|
||||
In this case the value of an entity declared as <code>n: INTEGER</code> is not a reference to an object, but the object itself -- in this case an atomic object, an integer value.
|
||||
|
||||
Expanded classes make it possible to construct composite objects with subobjects. Suppose that two classes, <code>ENGINE</code> and <code>PLANT</code>, are suppliers to the class <code>CAR</code>. Further, <code>ENGINE</code> is defined as <code>expanded</code>, and <code>PLANT</code> is ''not'' defined as <code>expanded</code>. So, here's an abbreviated class declaration (<code>note</code> clause and routines omitted) for <code>CAR</code>:
|
||||
<code>
|
||||
class CAR
|
||||
|
||||
feature
|
||||
|
||||
engine: ENGINE
|
||||
|
||||
originating_plant: PLANT
|
||||
|
||||
end -- class CAR
|
||||
</code>
|
||||
|
||||
We can illustrate the structure of a typical instance of <code>CAR</code> like this:
|
||||
|
||||
|
||||
[[Image:tutorial-8]]
|
||||
|
||||
|
||||
The field for the attribute <code>originating_plant</code> is a reference to an object of type <code>PLANT</code> external to the instance of <code>CAR</code>. But in the case of the attribute <code>engine</code>, the fields for the instance of <code>ENGINE</code> exist as a subobject within the instance of <code>CAR</code>, because of class <code>ENGINE</code>'s expanded nature.
|
||||
|
||||
This example also illustrates that the distinction between expanded and reference types is important not just for system implementation purposes but for high-level system modeling as well. Consider the example of a class covering the notion of car. Many cars share the same <code>originating_plant</code>, but an <code>engine</code> belongs to just one car. References represent the modeling relation "knows about"; subobjects, as permitted by expanded types, represent the relation "has part", also known as aggregation. The key difference is that sharing is possible in the former case but not in the latter.
|
||||
|
||||
==Basic operations==
|
||||
|
||||
To assign, copy and compare values, you can rely on a number of mechanisms. Two of them, assignment and equality testing, are language constructs; the others are library features, coming from the top-level class <code>ANY</code> seen earlier.
|
||||
|
||||
Assignment uses the symbol <code> := </code>. The assignment instruction
|
||||
<code>
|
||||
x := y
|
||||
</code>
|
||||
|
||||
updates the value of <code>x</code> to be the same as that of <code>y</code>. This means that for entities of reference types, the value of <code>x</code> will be a void reference if the value of <code>y</code> is void, and otherwise <code>x</code> will be attached to the same object OBJ2 as <code>y</code>:
|
||||
|
||||
|
||||
[[Image:tutorial-9]]
|
||||
|
||||
|
||||
For entities of expanded types, the values are objects; the object attached to <code>x</code> will be overwritten with the contents of the object attached to <code>y</code>. In the case of atomic objects, as in <code>n := 3</code> with the declaration <code> n: INTEGER</code> , this has the expected effect of assigning to <code>n</code> the integer value <code>3</code>; in the case of composite objects, this overwrites the fields for <code>x</code>, one by one, with the corresponding <code>y</code> fields.
|
||||
|
||||
To copy an object, use
|
||||
<code>
|
||||
x.copy (y)
|
||||
</code>
|
||||
which assumes that both <code>x</code> and <code>y</code> are non-void, and copies the contents of <code>y</code>'s attached object onto those of <code>x</code>'s. For expanded entities the effect is the same as that of the assignment <code>x := y</code>.
|
||||
|
||||
An operation performing similar duty to <code>copy</code> is <code>twin</code> . The assignment
|
||||
<code>
|
||||
x := y.twin
|
||||
</code>
|
||||
produces a newly created object (provided that <code>y</code> is non-void), initialized with a copy of the object attached to <code>y</code> and attaches the result to <code>x</code> . This means we may view <code>twin</code> as a function that performs the following two steps:
|
||||
<code>
|
||||
create Result
|
||||
Result.copy (Current)
|
||||
</code>
|
||||
The new object is created, then its content is updated to match the content of <code>y</code> to which the <code>twin</code> call is targeted.
|
||||
|
||||
So, assuming both entities of reference types and <code>y</code> not void, the assignment above will attach <code>x</code> to a '''new object''' identical to <code>y</code>'s attached object, as opposed to the assignment <code>x := y</code> which attaches <code>x</code> to the '''same object''' as <code>y</code>.
|
||||
|
||||
|
||||
To determine whether two values are equal, use the expression:
|
||||
<code>
|
||||
x = y
|
||||
</code>
|
||||
For references, this comparison will yield true if the values are either both void or both attached to the same object; this is the case in the last figure in the state after the assignment, but not before. The symbol for not equal is <code> /= </code>, as in:
|
||||
<code>
|
||||
x /= y
|
||||
</code>
|
||||
|
||||
As with assignment, there is also a form that works on objects rather than references:
|
||||
<code>
|
||||
x.is_equal (y)
|
||||
</code>
|
||||
will return true when <code>x</code> and <code>y</code> are both non-void and attached to field-by-field identical objects. This can be true even when <code>x = y</code> is not, for example, in the figure, ''before'' the assignment, if the two objects shown are field-by-field equal.
|
||||
|
||||
The expression <code>x.is_equal (y)</code> can be written alternatively, using the ''tilde'' ('~') character, in a notation similar in form to <code>x = y</code> . The expression:
|
||||
|
||||
x ~ y
|
||||
|
||||
will be true only in cases in which <code>x</code> and <code>y</code> are the same type and <code>x.is_equal (y)</code> is true.
|
||||
|
||||
A more general variant of <code>is_equal</code> is used under the form:
|
||||
<code>
|
||||
equal (x, y)
|
||||
</code>
|
||||
This is always defined, even if <code>x</code> is void, returning true whenever <code>is_equal</code> would but also if <code>x</code> and <code>y</code> are both void. (In contrast, <code>x.is_equal (y)</code> is not defined for void <code>x</code> and would, if evaluated, yield an exception as explained in [[ET: Design by Contract (tm), Assertions and Exceptions#Exception_handling|"Exception handling"]] below.)
|
||||
|
||||
{{note|The ~ operator performs an object equality comparison, using the (possibly redefined) version of feature <code>is_equal</code> that is appropriate for the operand types. The operand types must be the same, or the result will be <code>False</code>. As such, the use of ~ is preferred to over the use of direct use of either <code>x.is_equal (y)</code> or <code>equal (x, y)</code>, which can be susceptible to [[ET: Inheritance#Catcalls|catcalls]].}}
|
||||
|
||||
<code>Void</code> denotes a void reference. So you can make <code>x</code> void through the assignment
|
||||
<code>
|
||||
x := Void
|
||||
</code>
|
||||
and test whether it is void through:
|
||||
<code>
|
||||
if x = Void then ...</code>
|
||||
|
||||
Note that the assignment, <code>:=</code> , and the equality operators, <code>=</code>, <code>~</code>, <code>/~</code>, and <code>/=</code> , are language constructions, whereas <code>copy</code>, <code>twin</code>, <code>is_equal</code>, and <code>equal</code> are '''library features''' coming from class <code>ANY</code> .
|
||||
|
||||
<code>Void</code> is a language keyword with built-in characteristics, but it is not harmful to imagine <code>Void</code> as another feature declared in class <code>ANY</code>, with type of <code>NONE</code>, the "bottom" type. This convenience allows any assignment of the for <code>x := Void</code> to be valid without any making exceptions to the type rules, regardless of the type of <code>x</code> .
|
||||
|
||||
Using the redefinition mechanisms to be seen in the discussion of inheritance, a class can redefine <code>copy</code> and <code>is_equal</code> to cover specific notions of copy and equality. The assertions will ensure that the two remain compatible: after <code>x.copy (y)</code> , the property <code>x .is_equal (y)</code> must always be true. The effect of <code>twin</code> will automatically follow a redefinition of <code>copy</code>, and <code>equal</code> will follow <code>is_equal</code>.
|
||||
|
||||
To guarantee the original, non-redefined semantics you may use the variants <code>standard_copy</code>, <code>standard_twin</code>, <code>standard_equal</code>, all defined in <code>ANY</code> as "frozen", that is to say non-redefinable.
|
||||
|
||||
==Deep operations and persistence==
|
||||
|
||||
Feature <code>twin</code> only duplicates one object. If some of the object's fields are references to other objects, the references themselves will be copied, not those other objects.
|
||||
|
||||
It is useful, in some cases, to duplicate not just one object but an entire object structure. The expression <code>y.deep_twin</code> achieves this goal: assuming non-void <code>y</code>, it will produce a duplicate not just of the object attached to <code>y</code> but of the entire object structure starting at that object. The mechanism respects all the possible details of that structure, such as cyclic reference chains. Like the preceding features, <code>deep_twin</code> comes from class <code>ANY</code>.
|
||||
|
||||
A related mechanism provides a powerful '''persistence''' facility. A call of the form
|
||||
<code>
|
||||
x.store (Some_file_or_network_connection)
|
||||
</code>
|
||||
|
||||
will store a copy of the entire object structure starting at <code> x </code>, under a suitable representation. Like <code>deep_twin</code>, procedure <code>store</code> will follow all references to the end and maintain the properties of the structure. The function <code>retrieved</code> can then be used -- in the same system, or another -- to recreate the structure from the stored version.
|
||||
|
||||
As the name suggests, <code>Some_file_or_network_connection</code> can be an external medium of various possible kinds, not just a file but possibly a database or network. The EiffelNet client-server library indeed uses the <code> store </code>-<code> retrieved </code> mechanism to exchange object structures over a network, between compatible or different machine architectures, for example a Windows client and a Unix server.
|
||||
|
||||
==Memory management==
|
||||
|
||||
Reference reattachments, <code>x := y</code> , of the form illustrated by the figure just above can cause objects to become unreachable. This is the case for the object identified as OBJ1 on that figure (the object to which <code>x</code> was attached before the assignment) if no other reference was attached to it.
|
||||
|
||||
In all but toy systems, it is essential to reclaim the memory that has been allocated for such objects; otherwise memory usage could grow forever, as a result of creation instructions <code>create x</code> ... and calls to <code>twin</code> and the like, leading to thrashing and eventually to catastrophic termination.
|
||||
|
||||
The Eiffel method suggests that the task of detecting and reclaiming such unused object space should be handled by an automatic mechanism (part of the Eiffel run-time environment), not manually by developers (through calls to procedures such as Pascal's <code>dispose</code> and C/C++'s <code>free</code>). The arguments for this view are:
|
||||
|
||||
'''Simplicity''' : handling memory reclamation manually can add enormous complication to the software, especially when -- as is often the case in object-oriented development -- the system manipulates complex run-time data structures with many links and cycles.
|
||||
|
||||
'''Reliability''' : memory management errors, such as the incorrect reclamation of an object that is still referenced by a distant part of the structure, are a notorious source of dangerous and hard-to-correct bugs.
|
||||
|
||||
The Eiffel Software's implementation of Eiffel provides a sophisticated '''garbage collector''' which efficiently handles the automatic reclamation process, while causing no visible degradation of a system's performance and response time.
|
||||
|
||||
==Information hiding and the call rule==
|
||||
|
||||
The basic form of computation, it has been noted, is a call of the form <code lang="text">target.feature (...)</code> . This is only meaningful if <code lang="text">feature</code> denotes a feature of the generating class of the object to which <code>target</code> (assumed to be non-void) is attached. The precise rule is the following:
|
||||
|
||||
{{rule|name=Feature Call|text=A call of the form <code lang="text">target.feature (...)</code> appearing in a class C is only valid if <code lang="text">feature</code> is a feature of the base class of <code>target</code>'s type, and is available to C.}}
|
||||
|
||||
|
||||
The first condition simply expresses that if <code>target</code> has been declared as <code>target: A</code> then <code lang="text">feature</code> must be the name of one of the features of <code>A</code>. The second condition reflects Eiffel's application of the principles of information hiding. A <code>feature</code> clause, introducing one or more feature declarations, may appear not only as
|
||||
<code>
|
||||
feature -- Comment identifying the feature category
|
||||
|
||||
... Feature declaration ...
|
||||
|
||||
... Feature declaration ...
|
||||
|
||||
|
||||
...
|
||||
</code>
|
||||
|
||||
|
||||
but may also include a list of classes in braces, <code>feature {A, B, ... }</code> , as was illustrated for <code>ACCOUNT</code>:
|
||||
<code>
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
all_deposits: DEPOSIT_LIST
|
||||
-- List of deposits since account's opening.
|
||||
</code>
|
||||
|
||||
This form indicates that the features appearing in that clause are only '''available''' -- in the sense of available for calls, as used in the Feature Call rule -- to the classes listed. In the example feature <code> all_deposits </code> is only available to <code> NONE </code>. Because of the [[ET: The Static Picture: System Organization#The_global_inheritance_structure|global inheritance structure]], this means it is in fact available to no useful client at all, and is equivalent in practice to <code>feature { }</code> with an empty class list, although the form listing <code> NONE </code> explicitly is more visible and hence preferred.
|
||||
|
||||
|
||||
|
||||
With this specification a class text including the declaration <code>acc: ACCOUNT</code> and a call of the form
|
||||
<code>
|
||||
acc.all_deposits
|
||||
</code>
|
||||
|
||||
violates the Feature Call rule and will be rejected by the EiffelStudio compiler.
|
||||
|
||||
Besides fully exported features (introduced by <code>feature ... </code>; without further qualification) and fully secret ones (<code>feature { }</code> or <code>feature {NONE}</code> ), it is possible to export features selectively to some specified classes, using the specification
|
||||
<code>
|
||||
feature {A, B, ...}
|
||||
</code>
|
||||
|
||||
for arbitrary classes <code>A, B</code>, ... This enables a group of related classes to provide each other with privileged access, without requiring the introduction of a special module category above the class level (see [[ET: The Static Picture: System Organization#Clusters|"Clusters"]] ).
|
||||
|
||||
Exporting features selectively to a set of classes <code>A, B</code>, ... also makes them available to the descendants of these classes. So a feature clause beginning with just <code>feature</code> is equivalent to one starting with <code>feature {ANY}</code> .
|
||||
|
||||
These rules enable successive feature clauses to specify exports to different clients. In addition, the recommended style, illustrated in the examples of this chapter, suggests writing separate feature clauses -- regardless of their use for specifying export privileges -- to group features into separate categories. The standard style rules define a number of fundamental categories and the order in which they should appear; they include: <code>Initialization</code> for creation procedures, <code>Access</code> for general queries, <code>Status report</code> for boolean-valued queries, <code>Status setting</code>, <code>Element change</code>, <code>Implementation</code> (for selectively exported or secret features. Every feature in the EiffelBase library classes belongs to one of the predefined categories.
|
||||
|
||||
The Feature Call rule is the first of the rules that make Eiffel a '''statically typed''' approach, where the applicability of operations to objects is verified at compile time rather than during execution. Static typing is one of the principal components of Eiffel's support for reliability in software development.
|
||||
|
||||
==Execution scenario==
|
||||
|
||||
The preceding elements make it possible to understand the overall scheme of an Eiffel system's execution.
|
||||
|
||||
At any time during the execution of a system, one object is the '''current object''' of the execution, and one of the routines of the system, the '''current routine''', is being executed, with the current object as its target. (We will see below how the current object and current routine are determined.) The text of a class, in particular its routines, make constant implicit references to the current object. For example in the instruction
|
||||
<code>
|
||||
balance := balance + sum
|
||||
</code>
|
||||
|
||||
appearing in the body of procedure <code>deposit</code> of class <code>ACCOUNT</code>, the name of the attribute <code>balance</code>, in both occurrences, denotes the <code>balance</code> field of the current object, assumed to be an instance of <code>ACCOUNT</code>. In the same way, the procedure body that we used for the creation procedure <code>make</code> in the <code>ACCOUNT1</code> variant
|
||||
<code>
|
||||
make (sum: INTEGER)
|
||||
-- Initialize account with sum .
|
||||
do
|
||||
deposit (sum)
|
||||
end
|
||||
</code>
|
||||
|
||||
contains a call to the procedure <code>deposit</code>. Contrary to earlier calls written in dot notation as <code>target.feature (...)</code>, the call to <code>deposit</code> has no explicit target; this means its target is the current object, an instance of <code>ACCOUNT1</code>. Such a call is said to be '''unqualified'''; those using dot notations are '''qualified''' calls.
|
||||
|
||||
Although most uses of the current object are implicit, a class may need to name it explicitly. The predefined expression <code>Current</code> is available for that purpose. A typical use, in a routine <code>merge (other: ACCOUNT )</code> of class <code>ACCOUNT</code>, would be a test of the form
|
||||
<code>
|
||||
if other = Current then
|
||||
report_error ("Error: trying to merge an account with itself!")
|
||||
else
|
||||
... Normal processing (merging two different account) ...
|
||||
end
|
||||
</code>
|
||||
|
||||
With these notions it is not hard to define precisely the overall scenario of a system execution by defining which object and routine will, at each instant, be the current object and the current routine:
|
||||
|
||||
Starting a system execution, as we have seen, consists in creating an instance of the root class, the root object, and executing a designated creation procedure, the root procedure, with the root object as its target. The root object is the initial current object, and the root procedure is the initial current procedure.
|
||||
|
||||
From then on only two events can change the current object and current procedure: a qualified routine call; and the termination of a routine.
|
||||
|
||||
In a call of the form <code>target.routine (...)</code> , <code>target</code> denotes a certain object TC. (If not, that is to say, if the value of target is void, attempting to execute the call will trigger an exception, as studied below.) The generating class of TC must, as per the Feature Call rule, contain a routine of name <code>routine</code>. As the call starts, TC becomes the new current object and <code>routine</code> becomes the new current routine.
|
||||
|
||||
When a routine execution terminates, the target object and routine of the most recent non-terminated call -- which, just before the terminated call, were the current object and the current routine -- assume again the role of current object and current routine.
|
||||
|
||||
The only exception to the last rule is termination of the original root procedure call; in this case the entire execution terminates.
|
||||
|
||||
==Abstraction==
|
||||
|
||||
===Restriction of assignment targets===
|
||||
|
||||
The description of assignments stated that in <code>x := y</code> the target <code>x</code> must be an entity. More precisely it must be a '''writable''' entity. This notion excludes formal routine arguments: as noted, a routine <code>r (arg: SOME_TYPE)</code> may not assign to <code>arg</code> (reattaching it to a different object), although it can change the attached objects through calls of the form <code>arg.procedure (...)</code> .
|
||||
|
||||
Allowing only entities to be the targets of assignments precludes assignments of the form
|
||||
<code>
|
||||
obj.some_attribute := some_value
|
||||
-- This syntax is disallowed (except in the presence of an `assigner command', see below)
|
||||
</code>
|
||||
This is because the left-hand side <code>obj.some_attribute</code> is an expression (a feature call), not an entity: you may no more assign to <code>obj.some_attribute</code> than to, say, <code>b + a</code> -- another expression that is also, formally, a feature call.
|
||||
|
||||
To obtain the intended effect of such an assignment you may use a procedure call, where the base class of <code>obj</code>'s type has defined the procedure
|
||||
<code>
|
||||
set_some_attribute (v: VALUE_TYPE)
|
||||
-- Set value of some_attribute to `v'.
|
||||
do
|
||||
some_attribute := v
|
||||
end
|
||||
</code>
|
||||
|
||||
So instead of the disallowed assignment shown above, you would code:
|
||||
<code>
|
||||
obj.set_some_attribute (some_value)
|
||||
</code>
|
||||
|
||||
This rule is essential to enforcing the method. Permitting direct assignments to an object's fields -- as in C++ and Java -- would violate all the tenets of information hiding by letting clients circumvent the interface carefully crafted by the author of a supplier class.
|
||||
|
||||
===Assigner commands===
|
||||
|
||||
However, many developers have become accustomed to reading and writing code in other languages which do allow assignments of the form:
|
||||
<code>
|
||||
obj.some_attribute := some_value
|
||||
</code>
|
||||
For this reason, it is possible in Eiffel to allow such a syntax without actually enabling an end-run around the constraints of the class. It is done by using a facility called an '''assigner command'''. In the example shown in the previous section, we might expect <code>some_attribute</code> to be declared something like this:
|
||||
<code>
|
||||
some_attribute: SOME_TYPE
|
||||
</code>
|
||||
If this were the case the assignment above would cause a syntax error at compile time. But if the declaration included the specification of an assigner command, as shown below, then the assignment syntax would be valid.
|
||||
<code>
|
||||
some_attribute: SOME_TYPE assign set_some_attribute
|
||||
</code>
|
||||
The assigner command then is the procedure <code>set_some_attribute</code> coded as shown in the previous section. In the presence of the assigner command, the previously invalid assignment syntax is now valid. But it is translated by the compiler as a call to <code>set_some_attribute</code>, using the source of the assignment as an argument.
|
||||
|
||||
===Controlling modification of class fields===
|
||||
|
||||
It is the responsibility of each class author to define the exact privileges that the class gives to each of its clients, in particular field modification rights. Building a class is like building a machine: you design the internals, to give yourself the appropriate mechanisms; and you design the control panel, letting users (clients) access the desired subset of these mechanisms, safely and conveniently.
|
||||
|
||||
The levels of privilege available to the class author include, for any field:
|
||||
* Hide the field completely from clients, by exporting the corresponding attribute to <code>NONE</code>.
|
||||
* Export it, but in read-only mode, by not exporting any procedure that modifies it.
|
||||
* Export it for free read and write by any client, by also exporting a procedure of the <code>set_attribute</code> kind.
|
||||
* Export it in '''restricted-write''' mode, by exporting a procedure such as <code>deposit</code> of class <code>ACCOUNT</code>, which adds a specified amount to the <code>balance</code> field, rather than directly setting the balance.
|
||||
|
||||
The last case is particularly interesting is that it allows the class designer to set the precise way in which clients will manipulate the class instances, respecting the properties of the class and its integrity. The exported routines may, through the Design by Contract mechanism reviewed later in ( [[ET: Design by Contract (tm), Assertions and Exceptions]] ), place some further restrictions on the permitted modifications, for example by requiring the withdrawn amount to be positive.
|
||||
|
||||
These rules follow directly from the more general goals (reusability, extendibility, reliability) and principles (Uniform Access, information hiding) underlying Eiffel software design. They reflect a view that each class must denote a well-understood abstraction, defined by a set of exported features chosen by the class designer -- the "control panel".
|
||||
|
||||
The class documentation (see [[ET: Design by Contract (tm), Assertions and Exceptions#The_contract_form_of_a_class|the contract form of a class]] ) makes this view clear to client authors; no violation of that interface is permitted. This approach also paves the way for future '''generalization''' -- the final step of the cluster lifecycle, seen earlier in the section [[ET: The Software Process in Eiffel#Generalization_and_reuse|Generalization and reuse]] -- of the most promising components, and their inclusion into reusable libraries.
|
||||
|
||||
===Attribute specializations===
|
||||
|
||||
In certain situations it is beneficial to be able to declare class attributes which behave in specialized ways.
|
||||
|
||||
====Attribute specializations useful in void-safe programming====
|
||||
|
||||
Part of the strategy to ensure void-safety makes it necessary to be able to declare attributes as either [[Void-safety: Background, definition, and tools#Types as "attached" or "detachable"|'''detachable''' or '''attached''']].
|
||||
|
||||
'''[[Void-safety: Background, definition, and tools#Self-initializing attributes|Self-initializing attributes]]''' and '''[[Void-safety: Background, definition, and tools#Stable attributes|stable attributes]]''' are other tools for making void-safe programming more convenient.
|
||||
|
||||
These attribute specializations are presented in the [[Void-safe programming in Eiffel|void-safe programming]] chapter.
|
||||
|
||||
====Transient attributes====
|
||||
|
||||
Another special type of attribute supported by Eiffel Software's compiler is the '''transient attribute'''. When an instance of a class to which a transient attribute belongs is saved to persistent storage, the field for the transient attribute is not included. So, transient attributes are transient in the sense that they are part of the object at runtime, but not when the object is stored on disk.
|
||||
|
||||
This type of attribute has benefits when using the persistence mechanisms provided with EiffelStudio, like [http://eiffel.com/developers/learning_maps/Training/Maps/PersistenceCanPayOff/Serialization.html SED]. Because transient attributes are not stored, they need not be accounted for upon retrieval. So, objects stored before changes to a class that only affect transient attributes will still be retrievable using the new class definition (whereas, if non-transient attributes were changed, a mismatch would occur during retrieval).
|
||||
|
||||
An attribute is marked as transient by including a note option in its declaration:
|
||||
|
||||
<code>
|
||||
transient_attribute: detachable STRING
|
||||
note
|
||||
option: transient
|
||||
attribute
|
||||
end
|
||||
</code>
|
||||
|
||||
Only certain attributes can be marked as transient. Specifically, if attribute ''a'' is declared of type '''T''', it can be marked as transient only if it satisfies the following conditions:
|
||||
# If '''T''' is a reference type, '''T''' must be detachable
|
||||
# '''T''' is not a formal generic parameter
|
||||
# '''T''' is not a user defined expanded type
|
||||
# ''a'' is not an attribute of a user defined expanded class.
|
||||
|
||||
The EiffelBase class <code>INTERNAL</code> includes features which are used to distinguish object fields as either persistent or transient and to reveal how many transient fields an object has.
|
||||
|
||||
|
||||
{{note|Prior to version 6.6, support for transient attributes was limited to the C storable mechanism. In version 6.6, support was added for the Eiffel storable mechanism (SED) on both classic and .NET system targets.}}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
[[Property:title|ET: Eiffel Tutorial Copyright]]
|
||||
[[Property:weight|0]]
|
||||
[[Property:uuid|105e9956-f168-b060-a6be-e78b221f269a]]
|
||||
====MANUAL IDENTIFICATION AND COPYRIGHT====
|
||||
|
||||
Title: An Eiffel Tutorial, Eiffel Software Technical Report TR-EI-66/TU.
|
||||
|
||||
Publication history
|
||||
|
||||
First published July 2001. Corresponds to release 5.0 of the EiffelStudio environment.
|
||||
|
||||
Author
|
||||
|
||||
Software credits
|
||||
|
||||
See acknowledgments in book <span>[[Eiffel: The Language]]</span>.
|
||||
|
||||
Cover design
|
||||
|
||||
Rich Ayling.
|
||||
|
||||
Copyright notice and proprietary information
|
||||
|
||||
Copyright Interactive Software Engineering Inc. (Eiffel Software), 2001. May not be reproduced in any form (including electronic storage) without the written permission of Eiffel Software . "Eiffel Power" and the Eiffel Power logo are trademarks of Eiffel Software .
|
||||
|
||||
All uses of the product documented here are subject to the terms and conditions of the Eiffel Software Eiffel user license. Any other use or duplication is a violation of the applicable laws on copyright, trade secrets and intellectual property.
|
||||
|
||||
Special duplication permission for educational institutions
|
||||
|
||||
Degree-granting educational institutions using Eiffel Software Eiffel for teaching purposes as part of the <span> [http://www.eiffel.com/educators/resources.html Eiffel University Partnership Program] </span> may be permitted under certain conditions to copy specific parts of this book. Contact Eiffel Software for details.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
[[Property:title|ET: General Properties]]
|
||||
[[Property:weight|-14]]
|
||||
[[Property:uuid|1ad0b1d5-7ac6-9f55-92ec-ba6f42aee690]]
|
||||
Here is an overview of the facilities supported by Eiffel: <br/>
|
||||
* Completely ''object-oriented'' approach. Eiffel is a full-fledged application of object technology, not a "hybrid" of O-O and traditional concepts.
|
||||
* ''External interfaces''. Eiffel is a software composition tool and is easily interfaced with software written in such languages as C, C++, Java and C#.
|
||||
* ''Full lifecycle support''. Eiffel is applicable throughout the development process, including analysis, design, implementation and maintenance.
|
||||
* ''Classes'' as the basic structuring tool. A class is the description of a set of run-time objects, specified through the applicable operations and abstract properties. An Eiffel system is made entirely of classes, serving as the only module mechanism.
|
||||
* ''Consistent type system''. Every type is based on a class, including basic types such as integer, boolean, real, character, string, array.
|
||||
* ''Design by Contract''. Every system component can be accompanied by a precise specification of its abstract properties, governing its internal operation and its interaction with other components.
|
||||
* ''Assertions''. The method and notation support writing the logical properties of object states, to express the terms of the contracts. These properties, known as assertions, can be monitored at run-time for testing and quality assurance. They also serve as documentation mechanism. Assertions include preconditions, postconditions, class invariants, loop invariants, and also appear in "check" instructions.
|
||||
* ''Exception handling''. You can set up your software to detect abnormal conditions, such as unexpected operating system signals and contract violations, correct them, and recover
|
||||
* ''Information hiding''. Each class author decides, for each feature, whether it is available to all client classes, to specific clients only, or just for internal purposes.
|
||||
* ''Self-documentation''. The notation is designed to enable environment tools to produce abstract views of classes and systems, textual or graphical, and suitable for reusers, maintainers and client authors.
|
||||
* ''Inheritance''. You can define a class as extension or specialization of others.
|
||||
* ''Redefinition''. An inherited feature (operation) can be given a different implementation or signature.
|
||||
* ''Explicit redefinition''. Any feature redefinition must be explicitly stated.
|
||||
* ''Subcontracting''. Redefinition rules require new assertions to be compatible with inherited ones.
|
||||
* ''Deferred features and classes''. It is possible for a feature, and the enclosing class, to be specified -- including with assertions -- but not implemented. Deferred classes are also known as abstract classes.
|
||||
* ''Polymorphism''. An entity (variable, argument etc.) can become attached to objects of many different types.
|
||||
* ''Dynamic binding''. Calling a feature on an object always triggers the version of the feature specifically adapted to that object, even in the presence of polymorphism and redefinition.
|
||||
* ''Static typing''. A compiler can check statically that all type combinations will be valid, so that no run-time situation will occur in which an attempt will be made to apply an inexistent feature to an object.
|
||||
* ''Object test'' ("type narrowing"). It is possible to check at run time whether the type of an object conforms to a certain expectation, for example if the object comes from a database or a network.
|
||||
* ''Multiple inheritance''. A class can inherit from any number of others.
|
||||
* ''Feature renaming''. To remove name clashes under multiple inheritance, or to give locally better names, a class can give a new name to an inherited feature.
|
||||
* ''Repeated inheritance'': ''sharing and replication''. If, as a result of multiple inheritance, a class inherits from another through two or more paths, the class author can specify, for each repeatedly inherited feature, that it yields either one feature (sharing) or two (replication).
|
||||
* ''No ambiguity under repeated inheritance''. Conflicting redefinitions under repeated inheritance are resolved through a "selection" mechanism.
|
||||
* ''Unconstrained genericity''. A class can be parameterized, or "generic", to describe containers of objects of an arbitrary type.
|
||||
* ''Constrained genericity''. A generic class can be declared with a generic constraint, to indicate that the corresponding types must satisfy some properties, such as the presence of a particular operation.
|
||||
* ''Garbage collection''. The dynamic model is designed so that memory reclamation, in a supporting environment, can be automatic rather than programmer-controlled.
|
||||
* ''No-leak modular structure''. All software is built out of classes, with only two inter-class relations, client and inheritance.
|
||||
* ''Once routines''. A feature can be declared s "once", so that it is executed only for its first call, subsequently returning always the same result (if required). This serves as a convenient initialization mechanism, and for shared objects.
|
||||
* ''Standardized library''. The Kernel Library, providing essential abstractions, is standardized across implementations.
|
||||
* ''Other libraries''. Eiffel development is largely based on high-quality libraries covering many common needs of software development, from general algorithms and data structures to networking and databases.
|
||||
|
||||
|
||||
It is also useful, as in any design, to list some of what is '''not''' present in Eiffel. The approach is indeed based on a small number of coherent concepts so as to remain easy to master. Eiffel typically takes a few hours to a few days to learn, and users seldom need to return to the reference manual once they have understood the basic concepts. Part of this simplicity results from the explicit decision to exclude a number of possible facilities: <br/>
|
||||
* ''No global variables'', which would break the modularity of systems and hamper extendibility, reusability and reliability.
|
||||
* ''No union types'' (or record type with variants), which force the explicit enumeration of all variants; in contrast, inheritance is an open mechanism which permits the addition of variants at any time without changing existing code.
|
||||
* ''No in-class overloading'', which by assigning the same name to different features within a single context, causes confusions, errors, and conflicts with object-oriented mechanisms such as dynamic binding. (Dynamic binding itself is a powerful form of inter-class overloading, without any of these dangers.)
|
||||
* ''No goto instructions'' or similar control structures (break, exit, multiple-exit loops) which break the simplicity of the control flow and make it harder or impossible to reason about the software (in particular through loop invariants and variants).
|
||||
* ''No exceptions to the type rules''. To be credible, a type system must not allow unchecked "casts" converting from a type to another. (Safe cast-like operations are available through object test.)
|
||||
* ''No side-effect expression operators'' confusing computation and modification.
|
||||
* ''No low-level pointers, no pointer arithmetic'', a well-known source of bugs. (There is however a type ''POINTER'', used for interfacing Eiffel with C and other languages.)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
[[Property:title|ET: Genericity and Arrays]]
|
||||
[[Property:weight|-9]]
|
||||
[[Property:uuid|7f3bd1d7-357e-031d-9faa-b00594aa9ae0]]
|
||||
Some of the classes that we will need, particularly in libraries, are '''container''' classes, describing data structures made of a number of objects of the same or similar types. Examples of containers include arrays, stacks and lists. The class <code>DEPOSIT_LIST</code> posited in earlier examples describes containers.
|
||||
|
||||
It is not hard, with the mechanisms seen so far, to write the class <code>DEPOSIT_LIST</code>, which would include such features as <code>count</code> (query returning the number of deposit objects in the list) and <code>put</code> (command to insert a new deposit object).
|
||||
|
||||
Most of the operations, however, would be the same for lists of objects other than deposits. To avoid undue replication of efforts and promote reuse, we need a way to describe '''generic''' container classes, which we can use to describe containers containing elements of many different types.
|
||||
|
||||
==Making a class generic==
|
||||
|
||||
The notation
|
||||
<code>
|
||||
class C [G]
|
||||
... The rest as for any other class declaration ...</code>
|
||||
|
||||
introduces a generic class. A name such as <code>G</code> appearing in brackets after the class name is known as a '''formal generic parameter'''; it represents an arbitrary type.
|
||||
|
||||
Within the class text, feature declarations can freely use <code>G</code> even though it is not known what type <code>G</code> stands for. Class <code>LIST</code> of EiffelBase, for example, includes features
|
||||
<code>
|
||||
first: G
|
||||
-- Value of first list item
|
||||
|
||||
extend (val: G)
|
||||
-- Add a new item of value val at end of list
|
||||
...
|
||||
</code>
|
||||
|
||||
The operations available on an entity such as <code>first</code> and <code>val</code>, whose type is a formal generic parameter, are the operations available on all types: use as source <code>y</code> of an assignment <code>x := y</code>, use as target <code>x</code> of such an assignment (although not for <code>val</code>, which as a formal routine argument is not writable), use in equality comparisons <code>x = y</code> or <code>x /= y</code>, and application of universal features from <code>ANY</code> such as <code>twin</code>, <code>is_equal</code> and <code>copy</code>.
|
||||
|
||||
To use a generic class such as list, a client will provide a type name as '''actual generic parameter'''. So instead of relying on a special purpose class <code>DEPOSIT_LIST</code>, the class <code>ACCOUNT</code> could include the declaration
|
||||
<code>all_deposits: LIST [DEPOSIT]</code>
|
||||
|
||||
using <code>LIST</code> as a generic class and <code>DEPOSIT</code> as the actual generic parameter. Then all features declared in <code>LIST</code> as working on values of type <code>G</code> will work, when called on the target <code>all_deposits</code>, on values of type <code>DEPOSIT</code>. With the target
|
||||
<code>all_accounts: LIST [ACCOUNT]</code>
|
||||
|
||||
these features would work on values of type <code>ACCOUNT</code>.
|
||||
|
||||
{{info|A note of terminology: to avoid confusion, Eiffel always uses the word '''argument''' for routine arguments, reserving '''parameter''' for the generic parameters of classes. }}
|
||||
|
||||
Genericity reconciles extendibility and reusability with the static type checking demanded by reliability. A typical error, such as confusing an account and a deposit, will be detected immediately at compile time, since the call <code>all_accounts</code>. <code>extend</code> <code>(</code> <code>dep</code> <code>)</code> is invalid for <code>dep</code> declared of type <code>DEPOSIT</code>. What is valid is something like <code>all_accounts</code>. <code>extend</code> <code>(</code> <code>acc</code> <code>)</code> for <code>acc</code> of type <code>ACCOUNT</code>. In other approaches, the same effect might require costly run-time checks (as in Java, C# or Smalltalk), with the risk of run-time errors.
|
||||
|
||||
{{info|This form of genericity is known as '''unconstrained''' because the formal generic parameter, <code>G</code> in the example, represents an arbitrary type. You may also want to use types that are guaranteed to have certain operations available. This is known as '''constrained''' genericity and will be studied with inheritance. }}
|
||||
|
||||
==Arrays==
|
||||
|
||||
An example of generic class from the Kernel Library is <code>ARRAY</code> <code>[G]</code>, which describes direct-access arrays. Features include:
|
||||
* <code>put</code> to replace an element's value, as in <code>my_array.put (val, 25)</code> which replaces by <code>val</code> the value of the array entry at index 25.
|
||||
* <code>item</code> to access an entry, as in <code>my_array.item (25)</code> yielding the entry at index 25. A synonym is <code>infix</code> <code>"@"</code>, so that you may also write more tersely, for the same result, <code>my_array</code> <code>@</code> <code>25</code> .
|
||||
* <code>lower</code>, <code>upper</code> and <code>count</code>: queries yielding the bounds and the number of entries.
|
||||
* The creation procedure <code>make</code>, as in <code>create my_array.make (1, 50)</code> which creates an array with the given index bounds. It is also possible to resize an array through <code>resize</code>, retaining the old elements. In general, the Eiffel method abhors built-in limits, favoring instead structures that resize themselves when needed, either from explicit client request or automatically.
|
||||
|
||||
The comment made about <code>INTEGER</code> and other basic classes applies to <code>ARRAY</code> too: Eiffel compilers know about this class, and will be able to process expressions of the form <code>my_array.put (val, 25)</code> and <code>my_array</code> <code>@</code> <code>25</code> in essentially the same way as a C or Fortran array access -- <code>my_array</code> <code>[25]</code> in C. But it is consistent and practical to let developers treat <code>ARRAY</code> as a class and arrays as objects; many library classes in EiffelBase, for example, inherit from <code>ARRAY</code>. Once again the idea is to get the best of both worlds: the convenience and uniformity of the object-oriented way of thinking; and the efficiency of traditional approaches.
|
||||
|
||||
A similar technique applies to another Kernel Library class, that one not generic: <code>STRING</code>, describing character strings with a rich set of string manipulation features.
|
||||
|
||||
==Generic derivation==
|
||||
|
||||
The introduction of genericity brings up a small difference between classes and types. A generic class <code>C</code> is not directly a type since you cannot declare an entity as being of type <code>C</code>: you must use some actual generic parameter <code>T</code> -- itself a type. <code>C</code> <code>[T]</code> is indeed a type, but class <code>C</code> by itself is only a type template.
|
||||
|
||||
The process of obtaining a type <code>C</code> <code>[T]</code> from a general class <code>C</code> is known as a '''generic derivation'''; <code>C</code> <code>[T]</code> is a '''generically derived type'''. Type <code>T</code> itself is, recursively, either a non-generic class or again a generically derived type <code>D</code> <code>[U]</code> for some <code>D</code> and <code>U</code>, as in <code>LIST</code> <code>[ARRAY [INTEGER]]</code>.)
|
||||
|
||||
It remains true, however, that every type is based on a class. The base class of a generically derived type <code>C</code> <code>[T]</code> is <code>C</code>.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
[[Property:title|ET: Hello World]]
|
||||
[[Property:weight|-12]]
|
||||
[[Property:uuid|5b286f94-dd63-1169-a64e-74b5f8c5ef14]]
|
||||
When discovering any approach to software construction, however ambitious its goals, it is reassuring to see first a small example of the big picture -- a complete program to print the famous "Hello World" string. Here is how to perform this fascinating task in the Eiffel notation.
|
||||
|
||||
You write a class <code>HELLO</code> with a single procedure, say <code>make</code>, also serving as creation procedure. If you like short texts, here is a minimal version:
|
||||
<code>
|
||||
class
|
||||
HELLO
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature
|
||||
|
||||
make
|
||||
do
|
||||
print ("Hello World%N")
|
||||
end
|
||||
|
||||
end
|
||||
</code>
|
||||
|
||||
In practice, however, the Eiffel style rules suggest a better documented version:
|
||||
<code>
|
||||
note
|
||||
description: "Root for trivial system printing a message"
|
||||
author: "Elizabeth W. Brown"
|
||||
|
||||
class
|
||||
HELLO
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature
|
||||
|
||||
make
|
||||
-- Print a simple message.
|
||||
do
|
||||
io.put_string ("Hello World")
|
||||
io.put_new_line
|
||||
end
|
||||
|
||||
end -- class HELLO
|
||||
</code>
|
||||
|
||||
The two versions perform identically; the following comments will cover the more complete second one.
|
||||
|
||||
Note the absence of semicolons and other syntactic clatter or clutter. You may in fact use semicolons to separate instructions and declarations. But the language's syntax is designed to make the semicolon optional (regardless of text layout) and it's best for readability to omit it, except in the special case of successive elements on a single line.
|
||||
|
||||
The <code>note</code> clause does not affect execution semantics; you may use it to associate documentation with the class, so that browsers and other indexing and retrieval tools can help users in search of reusable components satisfying certain properties. Here we see two notes, labeled <code>description</code> and <code>author</code>.
|
||||
|
||||
The name of the class is <code>HELLO</code>. Any class may contain "features"; <code>HELLO </code>has just one, called <code>make</code>. The <code>create</code> clause indicates that <code>make</code> is a "creation procedure", that is to say an operation to be executed at class instantiation time. The class could have any number of creation procedures.
|
||||
|
||||
The definition of <code>make</code> appears in a <code>feature</code> clause. There may be any number of such clauses (to separate features into logical categories), and each may contain any number of feature declarations. Here we have only one.
|
||||
|
||||
The line starting with <code>--</code> (two hyphen signs) is a comment; more precisely it is a "header comment", which style rules invite software developers to write for every such feature, just after the point at which the feature is named. As will be seen in [[ET: Design by Contract (tm), Assertions and Exceptions#The_contract_form_of_a_class|"The contract form of a class"]], the tools of EiffelStudio know about this convention and use it to include the header comment in the automatically generated class documentation.
|
||||
|
||||
The body of the feature is introduced by the <code>do</code> keyword and terminated by <code>end</code>. It consists of two output instructions. They both use <code>io</code>, a generally available reference to an object that provides access to standard input and output mechanisms; the notation <code>io.f</code>, for some feature <code>f</code> of the corresponding library class (<code>STD_FILES</code>, in this case), means "apply <code>f</code> to <code>io</code>". Here we use two such features:
|
||||
* <code>put_string</code> outputs a string, passed as argument, here <code>"Hello World"</code>.
|
||||
* <code>put_new_line</code> terminates the line.
|
||||
|
||||
Rather than using a call to <code>put_new_line</code>, the first version of the class simply includes a new-line character, denoted as <code>%N</code> (the percent sign is used to introduce codes for [[Eiffel programming language syntax#Special characters|special characters]]), at the end of the string. Either technique is acceptable.
|
||||
|
||||
You may have noticed another difference between the two versions. The first version uses a call to <code>print</code> where the second uses <code>io.put_string</code> . Here too, the effect is identical and either technique is acceptable. In the next section, you will begin to see how things like <code>io</code> and <code>print</code> become available for use in a class like <code>HELLO</code>.
|
||||
|
||||
To build the system and execute it:
|
||||
* Start EiffelStudio
|
||||
* Create a new ''Basic application'' project
|
||||
* Specify <code>HELLO</code> as the "root class" and <code>make</code> as the "root procedure".
|
||||
* You can either use EiffelStudio to type in the above class text, or you may use any text editor and store the result into a file <code>hello.e</code> in the current directory.
|
||||
* Click the "Compile" icon.
|
||||
* Click the "Run" icon.
|
||||
|
||||
Execution starts and outputs <code>Hello World</code> on the appropriate medium: under Windows, a Console; under Unix or OpenVMS, the windows from which you started EiffelStudio.
|
||||
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,410 @@
|
||||
[[Property:modification_date|Tue, 10 Sep 2019 21:54:55 GMT]]
|
||||
[[Property:publication_date|Tue, 10 Sep 2019 21:54:55 GMT]]
|
||||
[[Property:title|ET: Instructions]]
|
||||
[[Property:weight|-6]]
|
||||
[[Property:uuid|628bf3db-728f-0b3c-bdbb-fe52deaae5b7]]
|
||||
==Instructions==
|
||||
|
||||
Eiffel has a remarkably small set of instructions. The basic computational instructions have been seen: creation, assignment, procedure call, retry. They are complemented by control structures: conditional, multi-branch, loop, as well as debug and check.
|
||||
|
||||
===Assignment and attachment===
|
||||
|
||||
As noted above we have already introduced assignment. But let's take another look at the assignment in the context of the more abstract concept of '''attachment'''. Attachment can occur with reference types by assignment such as:
|
||||
<code>
|
||||
x := y
|
||||
</code>
|
||||
In this assignment, <code>x</code> is the target of the assignment and <code>y</code> is the source. The object associated with <code>y</code> becomes ''attached'' to the entity <code>x</code>.
|
||||
|
||||
Attachment also occurs in other contexts. For example, when actual arguments are substituted for formal arguments in a call to a routine.
|
||||
<code>
|
||||
f (w)
|
||||
</code>
|
||||
In the call to <code>f</code> above, the object associated with the actual argument <code>w</code> will be ''attached'' to the formal argument for the duration of the execution of <code>f</code>. So, in this case, <code>w</code> can be viewed as the source of the attachment and the formal argument of <code>f</code> is the target.
|
||||
|
||||
Other situations in which attachment occurs include [[ET: The Dynamic Structure: Execution Model#Creating and initializing objects|creation instructions]], attachment of [[Void-safety: Background, definition, and tools#The attached syntax (object test)|object test local variables]], and the attachment of local iteration cursors in the iteration form of the [[ET: Instructions#Loop|loop construct]].
|
||||
|
||||
We learned in the section on [[ET: Inheritance#Polymorphism|polymorphism]], that the type of the source of an assignment must conform to the type of the assignment's target.
|
||||
|
||||
The rule that governs validity of assignments expands upon this and is generalized to apply to all attachments.
|
||||
|
||||
|
||||
{{Rule|name=Assignment|text=An assignment is valid if and only if the type of its source expression is '''compatible''' with the type of its target entity. }}
|
||||
|
||||
|
||||
The phrase "'''compatible with'''" in this rule means that either it "'''conforms to'''" or "'''converts to'''".
|
||||
|
||||
We saw conformance defined in the section on [[ET: Inheritance#Polymorphism|Polymorphism]]. [[ET: Other Mechanisms#Convertibility|Convertibility]] is explained in the section on [[ET: Other Mechanisms#Convertibility|Other Mechanisms]].
|
||||
|
||||
|
||||
===Conditional===
|
||||
|
||||
A conditional instruction has the form
|
||||
<code>
|
||||
if ... then
|
||||
...
|
||||
elseif ... then
|
||||
...
|
||||
else
|
||||
...
|
||||
end
|
||||
</code>
|
||||
The <code>elseif</code> ... <code>then</code> ... part (of which there may be more than one) and the <code>else</code> ... part are optional. After <code>if</code> and <code>elseif</code> comes a boolean expression; after <code>then</code> and <code>else</code> come zero or more instructions.
|
||||
|
||||
===Multi-branch===
|
||||
|
||||
A multi-branch instruction has the form
|
||||
<code>
|
||||
inspect
|
||||
exp
|
||||
when v1 then
|
||||
inst
|
||||
when v2 then
|
||||
inst2
|
||||
...
|
||||
else
|
||||
inst0
|
||||
end
|
||||
</code>
|
||||
|
||||
where the <code>else inst0</code> part is optional, <code>exp</code> is a character or integer expression, <code>v1</code>, <code>v1</code>, ... are constant values of the same type as <code>exp</code>, all different, and <code>inst0</code>, <code>inst1</code>, <code>inst2</code>, ... are sequences of zero or more instructions.
|
||||
|
||||
The effect of such a multi-branch instruction, if the value of <code>exp</code> is one of the <code>vi</code>, is to execute the corresponding <code>insti</code>. If none of the <code>vi</code> matches, the instruction executes <code>inst0</code>, unless there is no <code>else</code> part, in which case it triggers an exception.
|
||||
|
||||
{{note|Raising an exception is the proper behavior, since the absence of an <code>else</code> indicates that the author asserts that one of the values will match. If you want an instruction that does nothing in this case, rather than cause an exception, use an <code>else</code> part with an empty <code>inst0</code>. In contrast, <code>if c then</code> <code>inst</code> <code>end</code> with no <code>else</code> part does nothing in the absence of an <code>else</code> part, since in this case there is no implied claim that <code>c</code> must hold. }}
|
||||
|
||||
===Loop===
|
||||
|
||||
The loop construct provides a flexible framework for iterative computation. Its flexibility lies in how the complete form can be tailored and simplified for certain purposes by including or omitting optional parts.
|
||||
|
||||
You'll learn that the loop construct is always used in one of two forms: a '''base''' form which allows precise control over details of all loop aspects, and an '''iteration''' form which abstracts many of the details and provides a concise notation, ideal for traversing data structures and other objects which support iteration.
|
||||
|
||||
We will explore the entire mechanism, but let's approach things a little at a time.
|
||||
|
||||
====Two forms -- two examples====
|
||||
|
||||
First let's take a look at two examples. These examples accomplish the same goal: they both use a loop to visit and print the content of each node of a linked list of character strings. So, the list in question might be declared like this:
|
||||
|
||||
<code>
|
||||
my_list: LINKED_LIST [STRING]
|
||||
</code>
|
||||
|
||||
Here's one example:
|
||||
|
||||
<code>
|
||||
from
|
||||
my_list.start
|
||||
until
|
||||
my_list.off
|
||||
loop
|
||||
print (my_list.item)
|
||||
my_list.forth
|
||||
end
|
||||
</code>
|
||||
''Loop example 1.''
|
||||
|
||||
|
||||
and the other:
|
||||
|
||||
<code>
|
||||
across my_list as ic loop print (ic.item) end
|
||||
</code>
|
||||
''Loop example 2.''
|
||||
|
||||
|
||||
At first observation, it may not appear that both of these examples are using the same language construct. But, indeed, they are simply two different forms of a single language construct, as you will see.
|
||||
|
||||
Incidentally, there is no requirement that ''Loop example 1'' occupy multiple lines, and ''Loop example 2'' occupy only one line. ''Loop example 1'' could have been written like this:
|
||||
<code>
|
||||
from my_list.start until my_list.off
|
||||
loop print (my_list.item) my_list.forth end
|
||||
</code>
|
||||
just as ''Loop example 2'' could have been written to take multiple lines. It comes down to a matter of balance among traditional style, conciseness, and readability.
|
||||
|
||||
In fact, these two examples illustrate the two basic usage forms of the loop construct in Eiffel. The two basic forms can be differentiated by the parts of the construct with which they begin.
|
||||
|
||||
The form shown in ''Loop example 1'' begins with an ''Initialization part'' ( <code>from my_list.start</code> ), which starts with the keyword <code>from</code>. Let's call this form the '''base''' form. So, the type of loop you see in ''Loop example 1'' has been the traditional mechanism for accomplishing iterative computation, including iterating across data structures. However, extensions to Eiffel's loop construct have provided a more concise way of expressing traversing "iterable" structures.
|
||||
|
||||
This is the form shown in ''Loop example 2''. It begins with an ''Iteration part'' ( <code>across my_list as c</code> ), which starts with the keyword <code>across</code>. We'll call this form the '''iteration''' form.
|
||||
|
||||
====A closer look at the ''base'' form====
|
||||
|
||||
What is happening in ''Loop example 1''? Let's dissect it and see.
|
||||
|
||||
First there is the ''initialization'' part:
|
||||
|
||||
<code>
|
||||
from
|
||||
my_list.start
|
||||
</code>
|
||||
''Initialization part.''
|
||||
|
||||
|
||||
The first thing to occur in the execution of the base loop is the execution of any instructions in the initialization part (it is permissible for the initialization part to be empty of instructions, but the keyword <code>from</code> must be present to distinguish the base loop form). In our example, the feature <code>start</code> is applied to <code>my_list</code> which will attempt to set the list cursor to the first element in <code>my_list</code>.
|
||||
|
||||
The ''Exit condition part'':
|
||||
|
||||
<code>
|
||||
until
|
||||
my_list.off
|
||||
</code>
|
||||
''Exit condition part.''
|
||||
|
||||
|
||||
The exit condition part of the loop construct defines the conditions under which the loop body (explained below) should no longer be executed. In our example, the loop will no longer execute if the cursor is "off", that is, there is no current item. So, if the list is empty, the loop body will not execute at all.
|
||||
|
||||
The ''<code>loop</code> body'' part:
|
||||
|
||||
<code>
|
||||
loop
|
||||
print (my_list.item)
|
||||
my_list.forth
|
||||
</code>
|
||||
''<code>loop</code> body part.''
|
||||
|
||||
|
||||
The loop body part contains the sequence of instructions to be executed during each iteration. In the example, that includes printing the current list item and then advancing the cursor. At some point, the cursor will pass the last item in the list, causing the exit condition to become true and stop the loop's execution. So, at the risk of stating the obvious, the key to loops that always complete is to ensure that there is something in the loop body that is guaranteed always to cause the exit condition eventually to become true. Loop correctness will discussed in more detail [[#Loop invariants and variants|later]].
|
||||
|
||||
And finally, there's the ''End'' part:
|
||||
|
||||
<code>
|
||||
end
|
||||
</code>
|
||||
''<code>end</code> part.''
|
||||
|
||||
|
||||
====A closer look at the ''iteration'' form====
|
||||
|
||||
Now let's examine the iteration form (sometimes called the "across syntax") used in ''Loop example 2.''
|
||||
|
||||
The example begins with an iteration part:
|
||||
|
||||
<code>
|
||||
across my_list as ic
|
||||
</code>
|
||||
''Iteration part.''
|
||||
|
||||
|
||||
The iteration form is special in the sense that it is designed to work with objects which are ''iterable'', usually data structures. The iteration form always targets a particular object (usually a data structure) based on a class that inherits, either directly or indirectly from the library class <code>ITERABLE</code>. The iteration part specifies such a target for the iteration, in the case of our example, the target is <code>my_list</code>.
|
||||
|
||||
The "<code>as ic</code>" indicates that a local iteration cursor object referenced by the name <code>ic</code>, and available only for the scope of the iteration, will be created to effect the iteration. The element of <code>my_list</code> which is currently referenced by the cursor <code>ic</code> is accessed through <code>ic.item</code> as you see in the loop body:
|
||||
|
||||
<code>
|
||||
loop print (ic.item)
|
||||
</code>
|
||||
''<code>loop</code> body part''.
|
||||
|
||||
|
||||
Notice that the loop body does not contain the call to the structure's <code>forth</code> feature, as our example in base form did. Neither do you see the call to <code>start</code> nor the check of <code>off</code> in the exit condition. The iteration form abstracts these for you, relieving you of their burden ... while eliminating some opportunities for error.
|
||||
|
||||
Notice also that the call "<code>print (ic.item)"</code>" accesses the current item as "<code>ic.item"</code>" versus "<code>my_list.item"</code>" in the base form. This is because in the iteration form, access to the current item is through the cursor variable, "<code>ic</code>" in the case of ''Loop example 2''.
|
||||
|
||||
Concerning cursors, both ways of using the loop construct to traverse a structure employ a cursor. In the base form, the cursor is internal to the structure object. In the case of the example, that would be the instance of <code>LINKED_LIST [STRING]</code> called <code>my_list</code>. Applying the feature <code>item</code> to <code>my_list</code> retrieves the list element currently referenced by the cursor. In the iteration version of traversal, the variable <code>ic</code> holds the iteration cursor, external to the list object. So, you apply <code>ic.item</code> to get the current list element. The advantage to the external cursor is that multiple traversals of the structure can occur simultaneously without interfering with one another. This is possible in the base form, but only by saving and restoring the structure's cursor.
|
||||
|
||||
|
||||
{{recommended|The ''iteration'' form of the loop construct is not appropriate for use in cases in which the target structure may be changed during the traversal. Therefore, if you choose to alter the structure during traversal, you must use the ''base'' loop form with explicit cursor manipulation. This is still tricky business, so you should be certain to protect your work with appropriate contracts.}}
|
||||
|
||||
|
||||
Lastly, of course, the iteration form includes an ''<code>end</code> part'' ... at the end.
|
||||
|
||||
|
||||
====The ''iteration'' form as a boolean expression====
|
||||
|
||||
In ''Loop example 2'', the loop behaves as an instruction. But it is possible to have the iteration loop form behave as a boolean expression. This is helpful in cases in which you might want to ask a question that can be answered by traversing all or part of a structure.
|
||||
|
||||
To get this effect, you use the iteration form with one of two alternative body notations, the ''<code>all</code> body part'' or the ''<code>some</code> body part'' in place of the ''<code>loop</code> body''. When you use either of these notations, the ''body'' is a boolean expression. So, the forms for these body parts are:
|
||||
|
||||
<code>
|
||||
all boolean_expression
|
||||
</code>
|
||||
''<code>all</code> body part.''
|
||||
|
||||
<code>
|
||||
some boolean_expression
|
||||
</code>
|
||||
''<code>some</code> body part.''
|
||||
|
||||
|
||||
So, to know if all the strings in <code>my_list</code> have lengths greater than three characters, we could code:
|
||||
|
||||
<code>
|
||||
across my_list as ic all ic.item.count > 3 end
|
||||
</code>
|
||||
''Loop example 3.''
|
||||
|
||||
|
||||
To know if at least one string in <code>my_list</code> has a length greater than three characters, we would use the ''<code>some</code> body part'':
|
||||
|
||||
<code>
|
||||
across my_list as ic some ic.item.count > 3 end
|
||||
</code>
|
||||
''Loop example 4.''
|
||||
|
||||
Of course you can use iteration loops with "<code>all</code>" or "<code>some</code>" bodies in the same way that you would any other boolean expression; in [[#Conditional|conditionals]], for example.
|
||||
|
||||
|
||||
====Loop anatomy and rules for constructing loops====
|
||||
|
||||
Now that we've seen examples of the two forms of loops and broken down their component parts, we're ready to examine the anatomy of the entire construct in more detail. You may remember from the beginning of this discussion that the flexibility of the loop construct lies in its ability to use or omit its various parts to gain certain effects.
|
||||
|
||||
Here are all the possible loop parts, most of which we've seen in examples, in the order in which they must appear when we code them:
|
||||
|
||||
|
||||
{| border="2" cellpadding="8"
|
||||
! This loop part:
|
||||
! Has this pattern:
|
||||
|-
|
||||
| ''Iteration part'' || <code>across expression as identifier</code>
|
||||
|-
|
||||
| ''Initialization part'' || <code>from zero_or_more_instructions</code>
|
||||
|-
|
||||
| ''Invariant part'' || <code>invariant assertion</code>
|
||||
|-
|
||||
| ''Exit condition part'' || <code>until boolean_expression</code>
|
||||
|-
|
||||
| rowspan="3" | ''Body part'' || <code>loop</code> ''<code>zero_or_more_instructions </code>'' '''or'''
|
||||
|-
|
||||
| <code>all</code> ''<code>boolean_expression</code>'' '''or'''
|
||||
|-
|
||||
| <code>some</code> ''<code>boolean_expression</code>''
|
||||
|-
|
||||
| ''Variant part'' || <code>variant optional_tag: integer_expression</code>
|
||||
|-
|
||||
| ''<code>end</code> part'' || <code>end</code>
|
||||
|}
|
||||
|
||||
|
||||
Apart from seeing examples, it is useful to understand some of the rules of constructing loops from these parts. Here's an informal summary of what you should know about putting together valid loops:
|
||||
|
||||
# Any loop parts being used must appear in the order shown in the table above.
|
||||
# Every loop used will assume one of the two forms mentioned early. As a result, every loop will begin either with the <code>across</code> keyword (''iteration'' form) or the <code>from</code> keyword (''base'' form).
|
||||
# A ''Body part'' and an ''End part'' are both required for every loop.
|
||||
## ''Body parts'' using either the <code>all</code> keyword or the <code>some</code> keyword are only allowed in the absence of an ''initialization part''.
|
||||
# An ''exit condition part'' is required for all loops of ''base'' form.
|
||||
# The expression you use in an ''iteration'' part, must have a type that is based on a class that inherits from the library class <code>ITERABLE</code>.
|
||||
# The identifier you choose for the internal cursor used in loops of the ''iteration'' form shouldn't be the same as another identifier you are using.
|
||||
|
||||
There are implications of these rules that are worth understanding. Let's look at some of them.
|
||||
|
||||
Consider that all parts must appear in order (1) and that every loop starts with one of two keywords: either <code>across</code> or <code>from</code> (2). Taken together, these imply that it would be invalid for a loop in ''base'' form to include an ''iteration part''. However, the opposite is not true. Because the ''initialization part'' falls after the ''iteration part'' it is possible for a loop in ''iteration'' form to contain an ''initialization'' part. Imagine for example, that we wanted to compute the sum of the number of characters in all elements of the list of strings in our examples. The ''initialization'' part could be used to initialize the sum entity before starting the iteration:
|
||||
<code>
|
||||
across
|
||||
my_list as ic
|
||||
from
|
||||
sum := 0
|
||||
loop
|
||||
sum := sum + ic.item.count
|
||||
end
|
||||
</code>
|
||||
|
||||
Loops of the ''base'' form require an ''exit condition part'' (4). This allows the possibility that ''Iteration'' loops ''may'' contain an ''exit condition part''. Indeed they may, but it is not required. Using an ''exit condition part'' in a loop of the ''iteration'' can be useful if you want to impose an early exit condition on an iteration. So, extending the previous example, if we wanted to sum the length of elements, but only until we reached an element whose content matched a certain criterion, we could add the ''exit condition part'':
|
||||
<code>
|
||||
across
|
||||
my_list as ic
|
||||
from
|
||||
sum := 0
|
||||
until
|
||||
ic.item ~ "Stop now"
|
||||
loop
|
||||
sum := sum + ic.item.count
|
||||
end
|
||||
</code>
|
||||
|
||||
For loops of the ''iteration'' form, types of iteration targets must be based on classes inheriting from <code>ITERABLE</code> (5). What classes meet this criterion? All the appropriate classes in the EiffelBase library: lists, hash tables, arrays, intervals, etc. Although the details are beyond the scope of this tutorial, you also should recognize the implication that your own classes could be made iterable.
|
||||
|
||||
One useful descendant of <code>ITERABLE</code> is the integer interval. The general operator "<code>|..|</code>" provides a concise way of creating the interval between two integers. So, you can use this to loop across a range of integers without a lot of setup. This example:
|
||||
<code>
|
||||
across
|
||||
5 |..| 15 as ic
|
||||
loop
|
||||
print (ic.item.out+"%N")
|
||||
end
|
||||
</code>
|
||||
prints the integers in the interval 5 through 15.
|
||||
|
||||
Also descending from <code>ITERABLE</code> are the iteration cursors themselves. This means that a cursor can be the target of a loop of the ''iteration'' form. Consider this example that prints the items in <code>my_list</code> in reverse order:
|
||||
<code>
|
||||
across
|
||||
my_list.new_cursor.reversed as ic
|
||||
loop
|
||||
print (ic.item)
|
||||
end
|
||||
</code>
|
||||
Here the feature <code>new_cursor</code> is applied to <code>my_list</code>. The result is a new iteration cursor for traversing <code>my_list</code>. Then the <code>reversed</code> feature is applied to that result, which itself results in an iteration cursor having the order of the elements reversed. It is this cursor that is used for <code>ic</code> in the traversal.
|
||||
|
||||
|
||||
====Loop invariants and variants====
|
||||
|
||||
The only loop parts that we have yet to address are the ''invariant part'' and the ''variant part''. These two optional loop parts exist to help guarantee the correctness of loops. The ''invariant part'' expresses a loop invariant (not to be confused with [[ET: Design by Contract (tm), Assertions and Exceptions#Class invariants|class invariants]]). For the loop to be correct, the instructions in ''initialization part'' must ensure that the loop invariant assertion is true, and then every execution of the loop body must preserve the invariant; so the effect of the loop is to yield a state, eventually, in which both the loop invariant and the exit condition are true.
|
||||
|
||||
The loop must terminate after a finite number of iterations, of course. This can be guaranteed by including the loop ''variant part''. The ''variant part'' provides an integer expression whose value is non-negative after the execution of the instructions in the ''initialization part''. The value of the variant is then decreased by at least one, while remaining non-negative, by any execution of the loop body. Because a non-negative integer cannot be decreased forever, this guarantees that the loop will terminate.
|
||||
|
||||
When assertion monitoring is enabled for loop invariants and variants, the integrity of these properties is checked after initialization and after each loop iteration. An exception will be triggered if the loop invariant does not hold, or if the variant either becomes negative or does not decrease.
|
||||
|
||||
|
||||
===Debug===
|
||||
|
||||
An occasionally useful instruction is <code>debug</code> <code>(</code>''Debug_key'', ... <code>)</code> ''instructions'' <code>end</code> where ''instructions'' is a sequence of zero or more instructions and the part in parentheses is optional, containing if present one or more strings, called debug keys. The EiffelStudio compiler lets you specify the corresponding <code>debug</code> compilation option: <code>yes</code>, <code>no</code>, or an explicit debug key. The ''instructions'' will be executed if and only if the corresponding option is on. The obvious use is for instructions that should be part of the system but executed only in some circumstances, for example to provide extra debugging information.
|
||||
|
||||
===Check===
|
||||
|
||||
The final instruction is connected with Design by Contract™. The instruction
|
||||
<code>
|
||||
check
|
||||
Assertion
|
||||
end
|
||||
</code>where Assertion is a sequence of zero or more assertions, will have no effect unless assertion monitoring is turned on at the <code>Check</code> level or higher. If so it will evaluate all the assertions listed, having no further effect if they are all satisfied; if any one of them does not hold, the instruction will trigger an exception.
|
||||
|
||||
This instruction serves to state properties that are expected to be satisfied at some stages of the computation -- other than the specific stages, such as routine entry and exit, already covered by the other assertion mechanisms such as preconditions, postconditions and invariants. A recommended use of <code>check</code> involves calling a routine with a precondition, where the call, for good reason, does not explicitly test for the precondition. Consider a routine of the form
|
||||
<code>
|
||||
r (a_count: INTEGER)
|
||||
require
|
||||
valid_count: a_count >= minimum_allowable
|
||||
do
|
||||
...
|
||||
end
|
||||
</code>
|
||||
|
||||
This routine will only work if its precondition is satisfied on entry. To guarantee this precondition, the caller may protect it by the corresponding test, as in
|
||||
<code>
|
||||
if my_count >= a.minimum_allowable then
|
||||
a.r (my_count)
|
||||
end
|
||||
</code>
|
||||
|
||||
In effect, this says that if the value of <code>my_count</code> meets <code>r</code>'s precondition requirement, then call <code>r</code>, otherwise continue execution. This implies that there is something useful to be done in the case that the call to <code>r</code> could not be executed because the value of <code>my_count</code> did not meet the precondition.
|
||||
|
||||
But suppose that due to previous processing, it is reasonably expected that <code>my_count</code> should always have a value that complies with <code>r</code>'s precondition. In other words, it would always be expected that the call to <code>r</code> should proceed without failure. In this case it might be a good idea to use a <code>check</code> to document this property,
|
||||
<code>
|
||||
check
|
||||
my_count_is_large_enough: my_count >= a.minimum_allowable
|
||||
-- Should always be large enough because ...
|
||||
end
|
||||
</code>
|
||||
if only to make sure that a reader of the code will realize that the omission of an explicit test was not a mistake.
|
||||
|
||||
In production (finalized) mode, when assertion monitoring is typically turned off, this instruction will have no effect. But it will be precious for a maintainer of the software who is trying to figure out what it does, and in the process to reconstruct the original developer's reasoning. (The maintainer might of course be the same person as the developer, six months later.) And if the rationale is wrong somewhere, turning assertion checking on will immediately uncover the bug.
|
||||
|
||||
There is, however, one form of <code>check</code> that continues to be monitored even when assertion monitoring is turned off.
|
||||
<code>
|
||||
check Assertion then
|
||||
Compound
|
||||
end
|
||||
</code>
|
||||
Here <code>Assertion</code> is a list of assertions as above, and <code>Compound</code> is a list of zero or more executable instructions.
|
||||
|
||||
This variant is used often when ensuring [[Void-safe programming in Eiffel|void-safety]]. It is used make certain that certain detachable entities are actually attached to objects when expected, and to create a new void-safe scope for accessing the objects. For example:
|
||||
<code>
|
||||
check attached my_detachable as l_temp then
|
||||
l_temp.do_something
|
||||
end
|
||||
</code>
|
||||
In cases in which <code>my_detachable</code> is attached to an object (as is expected), the local entity l_temp will allow controlled access to the object during the scope of the <code>check</code> instruction. If a case occurs in which <code>my_detachable</code> is not attached to an object, then an exception is triggered. As noted above, for this variant of <code>check</code>, assertion monitoring is always in effect, even if it has been turned off for other cases.
|
||||
|
||||
So, the form <code>check ... then ... end</code> is somewhat similar to <code>if ... then ... end</code>. The difference is that the <code>if ... then ... end</code> allows the possibility that valid cases might occur in which the boolean expression is not true, and processing continues. The <code>check ... then ... end</code> does not allow such a possibility. The boolean expression is expected always to hold. In fact, if the expression is not true, then like other assertion violations, this is indicative of a bug, and will cause an exception to be raised.
|
||||
|
||||
|
||||
{{recommended|An extra indentation of the <code>check</code> part to separate it from the algorithm proper; and inclusion of a comment listing the rationale behind the developer's decision not to check explicitly for the precondition. }}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
[[Property:title|ET: To Learn More]]
|
||||
[[Property:weight|-1]]
|
||||
[[Property:uuid|74a5c826-8f21-8cf2-4f2c-dee4ee28ead5]]
|
||||
Beyond this introduction, you will find the following books essential to a mastery of the method and language: <br/>
|
||||
* [[Touch of class: Learning to Program Well with Objects and Contracts]], Bertrand Meyer, Springer-Verlag, 2009. A modern guide to developing durable skills for software professionals.
|
||||
* [[Object-Oriented Software Construction, 2nd Edition]], Bertrand Meyer, Prentice Hall, 2nd edition 1997. (Be sure to get the second edition.) About object technology in general; presents the method behind Eiffel.
|
||||
* [[Eiffel: The Language]], Bertrand Meyer, Prentice Hall, 1992. Language manual and reference.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
[[Property:title|ET: Lexical Conventions and Style Rules]]
|
||||
[[Property:weight|-2]]
|
||||
[[Property:uuid|60fdf029-8626-166d-cc4f-9069aacdda7f]]
|
||||
Eiffel software texts are free-format: distribution into lines is not semantically significant, and any number of successive space and line-return characters is equivalent to just one space. The style rules suggest indenting software texts as illustrated by the examples in this chapter.
|
||||
|
||||
Successive declarations or instructions may be separated by semicolons. Eiffel's syntax has been so designed, however, that (except in rare cases) '''the semicolon is optional'''. Omitting semicolons for elements appearing on separate lines lightens text and is the recommended practice since semicolons, as used by most programming languages, just obscure the text by distracting attention from the actual contents. Do use semicolons if you occasionally include successive elements on a single line.
|
||||
|
||||
56 names -- all unabbreviated single English words, except for <code>elseif</code> which is made of two words -- are reserved, meaning that you cannot use them to declare new entities. Here is the list:
|
||||
|
||||
|
||||
{| border="1"
|
||||
|-
|
||||
| <code>agent</code>
|
||||
| <code>alias</code>
|
||||
| <code>all</code>
|
||||
| <code>and</code>
|
||||
| <code>as</code>
|
||||
| <code>assign</code>
|
||||
| <code>check</code>
|
||||
|-
|
||||
| <code>class</code>
|
||||
| <code>convert</code>
|
||||
| <code>create</code>
|
||||
| <code>Current</code>
|
||||
| <code>debug</code>
|
||||
| <code>deferred</code>
|
||||
| <code>do</code>
|
||||
|-
|
||||
| <code>else</code>
|
||||
| <code>elseif</code>
|
||||
| <code>end</code>
|
||||
| <code>ensure</code>
|
||||
| <code>expanded</code>
|
||||
| <code>export</code>
|
||||
| <code>external</code>
|
||||
|-
|
||||
| <code>False</code>
|
||||
| <code>feature</code>
|
||||
| <code>from</code>
|
||||
| <code>frozen</code>
|
||||
| <code>if</code>
|
||||
| <code>implies</code>
|
||||
| <code>indexing</code>
|
||||
|-
|
||||
| <code>infix</code>
|
||||
| <code>inherit</code>
|
||||
| <code>inspect</code>
|
||||
| <code>invariant</code>
|
||||
| <code>is</code>
|
||||
| <code>like</code>
|
||||
| <code>local</code>
|
||||
|-
|
||||
| <code>loop</code>
|
||||
| <code>not</code>
|
||||
| <code>obsolete</code>
|
||||
| <code>old</code>
|
||||
| <code>once</code>
|
||||
| <code>or</code>
|
||||
| <code>prefix</code>
|
||||
|-
|
||||
| <code>Precursor</code>
|
||||
| <code>pure</code>
|
||||
| <code>redefine</code>
|
||||
| <code>reference</code>
|
||||
| <code>rename</code>
|
||||
| <code>require</code>
|
||||
| <code>rescue</code>
|
||||
|-
|
||||
| <code>Result</code>
|
||||
| <code>retry</code>
|
||||
| <code>separate</code>
|
||||
| <code>then</code>
|
||||
| <code>True</code>
|
||||
| <code>TUPLE</code>
|
||||
| <code>undefine</code>
|
||||
|}
|
||||
|
||||
|
||||
Since this tutorial has covered all the essential mechanisms, you may ignore the keywords not encountered; they are reserved for future use.
|
||||
|
||||
Most of the reserved words are keywords, serving only as syntactic markers, and written in boldface in typeset texts such as the present one: <code>class</code>, <code>feature</code>, <code>inherit</code>. The others, such as <code>Current</code>, directly carry a semantic denotation; they start with an upper-case letter and are typeset in boldface.
|
||||
|
||||
These conventions about letter case are only style rules. Eiffel is case-insensitive, since it is foolish to assume that two identifiers denote two different things just on the basis of a letter written in lower or upper case. The obvious exception is manifest character constants (appearing in single quotes, such as <code>'A'</code>) and manifest character strings (appearing in double quotes, such as <code>"UPPER and lower"</code>).
|
||||
|
||||
The style rules, however, are precise, and any serious Eiffel project will enforce them; the tools of EiffelStudio also observe them in the texts they output (although they will not mess up with your source text unless you ask them to reformat it). Here are the conventions, illustrated by the examples of this tutorial:
|
||||
* Class names in upper case, as <code>ACCOUNT</code>.
|
||||
* Non-constant feature names and keywords in lower case, as <code>balance</code> and <code>class</code>.
|
||||
* Constant features and predefined entities and expressions with an initial upper case, as <code>Avogadro</code> and <code>Result</code>.
|
||||
|
||||
In typeset documents including Eiffel texts, the standard for font styles is also precise. You should use <code>bold face</code> for keywords and <code>italics</code> for all other Eiffel elements. Comments, however, are typeset in <code>roman</code>. This lets a software element, such as an identifier, stand out clearly in what is otherwise a comment text expressed in English or another human language, as in the earlier example
|
||||
<code>
|
||||
-- Add `sum' to account.
|
||||
</code>
|
||||
|
||||
which makes clear that <code>sum</code> is a software element, not the English word.
|
||||
|
||||
There is also an Eiffel style to the choice of identifiers. For features, stay away from abbreviations and use full words. In multi-word identifiers, separate the constituents by underscores, as in <code>LINKED_LIST</code> and <code>set_owner</code>. The competing style of no separation but mid-identifier upper-case, as in <code>linkedList</code> or <code>setOwner</code>, is less readable and not in line with standard Eiffel practices.
|
||||
|
||||
Features of reusable classes should use consistent names. A set of standard names -- <code>put</code> for the basic command to add or replace an element, <code>count</code> for the query that returns the number of element in a structure, <code>item</code> to access an element -- is part of the style rules, and used systematically in EiffelBase. Use them in your classes too.
|
||||
|
||||
For local entities and formal arguments of routines, it is all right to use abbreviated names, since these identifiers only have a local scope, and choosing a loud name would give them too much pretense, leading to potential conflicts with features.
|
||||
|
||||
The complete set of style rules applied by ISE is available on the web in both <span> [http://archive.eiffel.com/doc/manuals/language/style/page.html HTML] </span> and <span> [http://archive.eiffel.com/doc/manuals/language/style/style.pdf PDF] </span> forms. These rules are an integral part of the Eiffel method; in quality software, there is no such thing as a detail. Applying them systematically promotes consistency between projects in the Eiffel world, enhances reusability, and facilitates everyone's work.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
[[Property:title|ET: Once routines and shared objects]]
|
||||
[[Property:weight|-5]]
|
||||
[[Property:uuid|bc42c52f-c668-6036-9540-55d0f48b05cb]]
|
||||
==About ''once'' routines==
|
||||
|
||||
The Eiffel's method obsession with extendibility, reusability and maintainability yields, as has been seen, modular and decentralized architectures, where inter-module coupling is limited to the strictly necessary, interfaces are clearly delimited, and all the temptations to introduce obscure dependencies, in particular global variables, have been removed. There is a need, however, to let various components of a system access common objects, without requiring their routines to pass these objects around as arguments (which would only be slightly better than global variables). For example various classes may need to perform output to a common "console window", represented by a shared object.
|
||||
|
||||
Eiffel addresses this need through an original mechanism that also takes care of another important issue, poorly addressed by many design and programming approaches: initialization. The idea is simple: if instead of <code>do</code> the implementation of an effective routine starts with the keyword <code>once</code>, it will only be executed the first time the routine is called during a system execution (or, in a multi-threaded environment, the first time in each thread), regardless of what the caller was. Subsequent calls from the same caller or others will have no effect; if the routine is a function, it will always return the result computed by the first call -- object if an expanded type, reference otherwise.
|
||||
|
||||
In the case of procedures, this provides a convenient initialization mechanism. A delicate problem in the absence of a <code>once</code> mechanism is how to provide the users of a library with a set of routines which they can call in any order, but which all need, to function properly, the guarantee that some context had been properly set up. Asking the library clients to precede the first call with a call to an initialization procedure <code>setup</code> is not only user-unfriendly but silly: in a well-engineered system we will want to check proper set-up in every one of the routines, and report an error if necessary; but then if we were able to detect improper set-up we might as well shut up and set up ourselves (by calling <code>setup</code>). This is not easy, however, since the object on which we call <code>setup</code> must itself be properly initialized, so we are only pushing the problem further. Making <code>setup</code> a <code>once</code> procedure solves it: we can simply include a call
|
||||
<code>
|
||||
setup
|
||||
</code>
|
||||
|
||||
at the beginning of each affected routine; the first one to come in will perform the needed initializations; subsequent calls will have, as desired, no effect.
|
||||
|
||||
Once functions will give us shared objects. A common scheme is
|
||||
<code>
|
||||
console: WINDOW
|
||||
-- Shared console window
|
||||
once
|
||||
create Result.make (...)
|
||||
end
|
||||
</code>
|
||||
|
||||
Whatever client first calls this function will create the appropriate window and return a reference to it. Subsequent calls, from anywhere in the system, will return that same reference. The simplest way to make this function available to a set of classes is to include it in a class <code>SHARED_STRUCTURES</code> which the classes needing a set of related shared objects will simply inherit.
|
||||
|
||||
For the classes using it, <code>console</code>, although a function, looks very much as if it were an attribute -- only one referring to a shared object.
|
||||
|
||||
The "[[ET: Hello World|Hello World]]" system at the beginning of this discussion 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.
|
||||
|
||||
==Adjusting once semantics with "once keys"==
|
||||
|
||||
Sometimes it is helpful to adjust the way that once routines work, and that is done by applying '''once keys'''. For example, in multithreaded mode, it is reasonable most often for once routines to be executed once per ''thread'', versus once per ''process''. Therefore, the default <code>once</code> syntax, as shown in the example above, would behave as once per thread in multithreaded mode.
|
||||
|
||||
Sometimes, however, it is useful in multithreaded mode to create an object which can be shared among threads. To do this, once per process is needed. To create effects like this which are outside the default behavior, we can use once "keys". In following example, a once key is used to specify that the once routine is executed only once per process:
|
||||
|
||||
<code>
|
||||
shared_object: SOME_TYPE
|
||||
-- An object that can be shared among threads
|
||||
-- without being reinitialized.
|
||||
once ("PROCESS")
|
||||
create Result.make (...)
|
||||
end
|
||||
</code>
|
||||
|
||||
Other valid once keys are "THREAD" and "OBJECT". Of course, "THREAD" ensures that the once routine executes only the first time it is called during the execution of a particular process thread. "OBJECT" is used when it is desirable to have a once routine executed on a once per object basis.
|
||||
|
||||
|
||||
{| border="2"
|
||||
|+ '''How once keys affect once routine execution'''
|
||||
! Once key ... !! Routine executed the first time it is called ...
|
||||
|-
|
||||
| PROCESS || During process execution
|
||||
|-
|
||||
| THREAD || During each process thread execution
|
||||
|-
|
||||
| OBJECT || By each instance
|
||||
|}
|
||||
|
||||
|
||||
THREAD is the default once key if none is specified (which for single threaded systems will have the same effect as PROCESS).
|
||||
|
||||
The concept of once keys is open ended, so additional keys may become supported in the future to allow even finer grained control of once routine behavior.
|
||||
|
||||
==Once per object internal implementation warning ==
|
||||
|
||||
{{warning|As of version 6.6, once per object is implemented using 2 or 3 implementation attributes (these are used to store whether the once routine has already called or not, the eventual exception if any, and the result value if any).<br/><br/>The implementation attributes are named starting with an underscore '_', and if you use the class <code>INTERNAL</code>, the implementation attributes will be included in the <code>field_count</code>, and available through the <code>INTERNAL</code> features. <br/>However this might change in the future, and the implementation attributes might be hidden, so you should not rely on them for your applications. <br/>One last technical detail is that for now a once per object is transient (i.e the associated implementation attributes are [[ET: The Dynamic Structure: Execution Model#Transient attributes|transient]]).<br/>}}
|
||||
|
||||
|
||||
==Once routines and exceptions==
|
||||
|
||||
It is possible that during the execution that happens when a once routine is called for the first time, an exception may occur. If this happens, then the '''same exception will be raised on each subsequent''' call to the once routine.
|
||||
|
||||
==Syntax from previous versions==
|
||||
|
||||
The syntax shown above is the current standard syntax. However in Eiffel code written for previous versions, you may run across once keys for multithreaded systems which are expressed in a different syntax. Specifically, the older syntax used a feature's <code>note</code> clause to specify a once key, as in the following example.
|
||||
|
||||
<code>
|
||||
shared_object: SOME_TYPE
|
||||
-- Obsolete syntax
|
||||
-- An object that can be shared among threads
|
||||
-- without being reinitialized.
|
||||
note
|
||||
once_status: global
|
||||
once
|
||||
create Result.make (...)
|
||||
end
|
||||
</code>
|
||||
|
||||
|
||||
@@ -0,0 +1,345 @@
|
||||
[[Property:modification_date|Tue, 10 Sep 2019 23:07:44 GMT]]
|
||||
[[Property:publication_date|Tue, 10 Sep 2019 23:07:44 GMT]]
|
||||
[[Property:title|ET: Other Mechanisms]]
|
||||
[[Property:weight|-4]]
|
||||
[[Property:uuid|c0a01664-194c-4e84-0517-8e7c1ca61dec]]
|
||||
We now examine a few important mechanisms that complement the preceding picture.
|
||||
|
||||
|
||||
==Manifest constants==
|
||||
|
||||
Sometimes we want to provide in software text a self-denoting value of a particular type. In Eiffel this is what we call a '''manifest constant'''. For example, if we are searching an indexed structure, we might have an integer variable that we would want to initialize to reference the first item in the structure:
|
||||
|
||||
<code>
|
||||
my_index := 1
|
||||
</code>
|
||||
|
||||
In this case we used a manifest constant, <code>1</code>, to provide an initial value for <code>my_index</code>. In particular, this is a manifest integer.
|
||||
|
||||
Eiffel also supports [[Eiffel_programming_language_syntax#Manifest_constants|manifest constants]] for real (and double) numbers (ex: <code>3.1415</code>), boolean values (ex: <code>True</code>, <code>False</code>), and characters (ex: <code>'A'</code>, with [[Eiffel programming language syntax#Special characters|special characters]] expressed using a percent sign as in <code>'%N'</code> for new line, <code>'%B'</code> for backspace, <code>'%"'</code> for double quote, and <code>'%U'</code> for null).
|
||||
|
||||
Manifest constants are also available for strings, using double quotes as in: <code>"Hello world!"</code>. As with character constants, special characters are denoted using the <code>%</code> codes.
|
||||
|
||||
===Verbatim strings===
|
||||
|
||||
You may occasionally have a need for a manifest string that represents a multi-line formatted string. In Eiffel we call this type of manifest string a '''verbatim string''', and there is a special syntax for specifying verbatim strings in Eiffel code. Verbatim strings are either '''aligned''' or '''non-aligned'''. Aligned verbatim strings will automatically be adjusted so that their leftmost line (the line with text characters closest to the left margin) contains no "white space" to the left of the first text character. For non-aligned verbatim strings, the white space is left untouched. You use a slightly different way of specifying each type of string. For example, this aligned verbatim string:
|
||||
|
||||
<code>
|
||||
my_aligned_string: STRING =
|
||||
"[
|
||||
Thrice hail the still unconquered King of Song!
|
||||
For all adore and love the Master Art
|
||||
That reareth his throne in temple of the heart;
|
||||
And smiteth chords of passion full and strong
|
||||
Till music sweet allures the sorrowing throng!
|
||||
]"
|
||||
</code>
|
||||
|
||||
will print like this:
|
||||
|
||||
<code lang="text">
|
||||
Thrice hail the still unconquered King of Song!
|
||||
For all adore and love the Master Art
|
||||
That reareth his throne in temple of the heart;
|
||||
And smiteth chords of passion full and strong
|
||||
Till music sweet allures the sorrowing throng!
|
||||
</code>
|
||||
|
||||
The same string, declared as a non-aligned verbatim string:
|
||||
|
||||
<code>
|
||||
my_non_aligned_string: STRING =
|
||||
"{
|
||||
Thrice hail the still unconquered King of Song!
|
||||
For all adore and love the Master Art
|
||||
That reareth his throne in temple of the heart;
|
||||
And smiteth chords of passion full and strong
|
||||
Till music sweet allures the sorrowing throng!
|
||||
}"
|
||||
</code>
|
||||
|
||||
will print like this:
|
||||
|
||||
<code lang="text">
|
||||
Thrice hail the still unconquered King of Song!
|
||||
For all adore and love the Master Art
|
||||
That reareth his throne in temple of the heart;
|
||||
And smiteth chords of passion full and strong
|
||||
Till music sweet allures the sorrowing throng!
|
||||
</code>
|
||||
|
||||
The difference in declaration is that the aligned verbatim string uses as its "opener" the double-quote plus bracket combination, " <code>"[</code> ", and the bracket plus double quote, " <code>]"</code> ", as its "closer". The non-aligned verbatim string uses braces, " <code>{</code> " and " <code>}</code> " instead of the bracket.
|
||||
|
||||
The syntax for specifying verbatim strings allows an option for the situation in which the specified string might conflict with the "closer". You can include a simple string between the double quote and the bracket on each end of the verbatim string to guarantee uniqueness. Here's our aligned verbatim string with the simple string " <code>*?</code> " inserted in the opener and closer:
|
||||
|
||||
<code>
|
||||
my_aligned_string: STRING =
|
||||
"*?[
|
||||
Thrice hail the still unconquered King of Song!
|
||||
For all adore and love the Master Art
|
||||
That reareth his throne in temple of the heart;
|
||||
And smiteth chords of passion full and strong
|
||||
Till music sweet allures the sorrowing throng!
|
||||
]*?"
|
||||
</code>
|
||||
|
||||
|
||||
==Constant attributes==
|
||||
|
||||
The attributes studied earlier were variable: each represents a field present in each instance of the class and changeable by its routines.
|
||||
|
||||
It is also possible to declare '''constant attributes''', as in
|
||||
<code>
|
||||
Solar_system_planet_count: INTEGER = 8
|
||||
</code>
|
||||
|
||||
These will have the same value for every instance and hence do not need to occupy any space in objects at execution time. (In other approaches similar needs would be addressed by symbolic constants, as in Pascal or Ada, or macros, as in C.)
|
||||
|
||||
What comes after the <code>=</code> is a manifest constant. So you can declare a constant attribute for any type for which there is a manifest constant.
|
||||
|
||||
|
||||
==Obsolete features and classes==
|
||||
|
||||
One of the conditions for producing truly great reusable software is to recognize that although you should try to get everything right the first time around you won't always succeed. But if "good enough" may be good enough for application software, it's not good enough, in the long term, for reusable software. The aim is to get ever closer to the asymptote of perfection. If you find a better way, you must implement it. The activity of generalization, discussed as part of the lifecycle, doesn't stop at the first release of a reusable library.
|
||||
|
||||
This raises the issue of backward compatibility: how to move forward with a better design, without compromising existing applications that used the previous version?
|
||||
|
||||
The notion of obsolete class and feature helps address this issue. By declaring a feature as <code>obsolete</code>, using the syntax
|
||||
<code>
|
||||
enter (i: INTEGER; x: G)
|
||||
obsolete
|
||||
"Use ` put (x, i)' instead "
|
||||
require
|
||||
...
|
||||
do
|
||||
put (x, i)
|
||||
end
|
||||
</code>
|
||||
|
||||
you state that you are now advising against using it, and suggest a replacement through the message that follows the keyword <code>obsolete</code>, a mere string. The obsolete feature is still there, however; using it will cause no other harm than a warning message when someone compiles a system that includes a call to it. Indeed, you don't want to hold a gun to your client authors' forehead (''"Upgrade now or die !"''); but you do want to let them know that there is a new version and that they should upgrade at their leisure.
|
||||
|
||||
Besides routines, you may also mark classes as obsolete.
|
||||
|
||||
The example above is a historical one, involving an early change of interface for the EiffelBase library class <code>ARRAY</code>; the change affected both the feature's name, with a new name ensuring better consistency with other classes, and the order of arguments, again for consistency. It shows the recommended style for using <code>obsolete</code>: <br/>
|
||||
* In the message following the keyword, explain the recommended replacement. This message will be part of the warning produced by the compiler for a system that includes the obsolete element.
|
||||
* In the body of the routine, it is usually appropriate, as here, to replace the original implementation by a call to the new version. This may imply a small performance overhead, but simplifies maintenance and avoids errors.
|
||||
|
||||
|
||||
It is good discipline not to let obsolete elements linger around for too long. The next major new release, after a suitable grace period, should remove them.
|
||||
|
||||
The design flexibility afforded by the <code>obsolete</code> keyword is critical to ensure the harmonious long-term development of ambitious reusable software.
|
||||
|
||||
|
||||
==Creation variants==
|
||||
|
||||
The basic forms of creation instruction, and the one most commonly used, are the two illustrated earlier ( [[ET: The Dynamic Structure: Execution Model#Creating_and_initializing_objects|"Creating and initializing objects"]] ):
|
||||
<code>
|
||||
create x.make (2000)
|
||||
create x
|
||||
</code>
|
||||
|
||||
the first one if the corresponding class has a <code>create</code> clause, the second one if not. In either form you may include a type name in braces, as in
|
||||
<code>
|
||||
create {SAVINGS_ACCOUNT} x.make (2000)
|
||||
</code>
|
||||
|
||||
which is valid only if the type listed, here <code>SAVINGS_ACCOUNT</code>, conforms to the type of <code>x</code>, assumed here to be <code>ACCOUNT</code>. This avoids introducing a local entity, as in
|
||||
<code>
|
||||
local
|
||||
xs: SAVINGS_ACCOUNT
|
||||
do
|
||||
create xs.make (2000)
|
||||
x := xs
|
||||
...
|
||||
</code>
|
||||
|
||||
and has exactly the same effect. Another variant is the '''creation expression''', which always lists the type, but returns a value instead of being an instruction. It is useful in the following context:
|
||||
<code>
|
||||
some_routine (create {ACCOUNT}.make (2000))
|
||||
</code>
|
||||
|
||||
which you may again view as an abbreviation for a more verbose form that would need a local entity, using a creation instruction:
|
||||
<code>
|
||||
local
|
||||
x: ACCOUNT
|
||||
do
|
||||
create x.make (2000)
|
||||
some_routine (x)
|
||||
...
|
||||
</code>
|
||||
|
||||
Unlike creation instructions, creation expressions must always list the type explicitly, <code>{ACCOUNT}</code> in the example. They are useful in the case shown: creating an object that only serves as an argument to be passed to a routine. If you need to retain access to the object through an entity, the instruction <code>create x</code> ... is the appropriate construct.
|
||||
|
||||
The creation mechanism gets an extra degree of flexibility through the notion of <code>default_create</code>. The simplest form of creation instruction, <code>create x</code> without an explicit creation procedure, is actually an abbreviation for <code>create x.default_create</code>, where <code>default_create</code> is a procedure defined in class <code>ANY</code> to do nothing. By redefining <code>default_create</code> in one of your classes, you can ensure that <code>create x</code> will take care of non-default initialization (and ensure the invariant if needed). When a class has no <code>create</code> clause, it's considered to have one that lists only <code>default_create</code>. If you want to allow <code>create x</code> as well as the use of some explicit creation procedures, simply list <code>default_create</code> along with these procedures in the <code>create</code> clause. To disallow creation altogether, include an empty <code>create</code> clause, although this technique is seldom needed since most non-creatable classes are deferred, and one can't instantiate a deferred class.
|
||||
|
||||
One final twist is the mechanism for creating instances of formal generic parameters. For <code>x</code> of type <code>G</code> in a class <code>C [G]</code>, it wouldn't be safe to allow <code>create x</code>, since <code>G</code> stands for many possible types, all of which may have their own creation procedures. To allow such creation instructions, we rely on constrained genericity. You may declare a class as
|
||||
<code>
|
||||
[G -> T create cp end]
|
||||
</code>
|
||||
|
||||
to make <code>G</code> constrained by <code>T</code>, as we learned before, and specify that any actual generic parameter must have <code>cp</code> among its creation procedures. Then it's permitted to use <code>create x.cp</code>, with arguments if required by <code>cp</code>, since it is guaranteed to be safe. The mechanism is very general since you may use <code>ANY</code> for <code>T</code> and <code>default_create</code> for <code>cp</code>. The only requirement on <code>cp</code> is that it must be a procedure of <code>T</code>, not necessarily a creation procedure; this permits using the mechanism even if <code>T</code> is deferred, a common occurrence. It's only descendants of <code>T</code> that must make <code>cp</code> a creation procedure, by listing it in the <code>create</code> clause, if they want to serve as actual generic parameters for <code>C</code>.
|
||||
|
||||
|
||||
==Non-object calls==
|
||||
|
||||
The Eiffel model for object-oriented computation involves the application of some feature <code>f</code> to some object <code>x</code>, and possibly passing arguments <code>a</code>:
|
||||
|
||||
<code>
|
||||
x.f (a)
|
||||
</code>
|
||||
|
||||
This type of feature call is known as an '''object call''' because it applies the feature to a target object, in this case <code>x</code>. However, under certain circumstances we may apply a feature of a class in a fashion that does not involve a target object. This type of call is a '''non-object call'''. In place of the target object, the syntax of the non-object call uses the type on which the feature can be found.
|
||||
|
||||
<code>
|
||||
circumference := radius * 2.0 * {MATH_CONST}.Pi
|
||||
</code>
|
||||
|
||||
In the sample above, the call to feature <code>{MATH_CONST}.Pi</code> is a non-object call. This case illustrates one of the primary uses of non-object calls: constants. The library class <code>MATH_CONST</code> contains commonly used mathematical constants. Non-object calls make it possible to use the constants in <code>MATH_CONST</code> without having to create an instance of <code>MATH_CONST</code> or inherit from it.
|
||||
|
||||
The other primary use is for external features. One example is when we use Microsoft .NET classes from Eiffel code and have to access mechanisms for which there is no direct analog in Eiffel. Microsoft .NET supports so-called "static" methods and enumeration types. To access these, we use non-object calls. In the example below, a non-object call is used to access the enumeration <code lang="text">CreateNew</code> from the .NET enumeration type <code lang="text">System.IO.FileMode</code>.
|
||||
|
||||
<code>
|
||||
create my_file_stream.make ("my_file.txt", {FILE_MODE}.create_new)
|
||||
</code>
|
||||
|
||||
The validity of a non-object call is restricted in ways that mirror these primary uses. That is, any feature called in a non-object call must be either a constant attribute or an external feature. See the [[ECMA Standard 367|ISO/ECMA Eiffel standard document]] for additional details.
|
||||
|
||||
|
||||
==Convertibility==
|
||||
|
||||
It is useful at times to designate that instances of one type can be created through the controlled conversion of instances of some other type. This can be done through a safe Eiffel type conversion mechanism called '''convertibility'''.
|
||||
|
||||
Convertibility is useful when refactoring, moving from one design to another, or, as you will see in the example, accommodating external technologies over which we have no control.
|
||||
|
||||
|
||||
{{Definition|Convertibility: converts to, converts from|<br/>
|
||||
<br/>
|
||||
A type U based on a class CU '''converts to''' a type T based on a class CT (and T '''converts from''' U) if either<br/>
|
||||
<br/>
|
||||
CT has a ''conversion procedure'' using U as a conversion type, or <br/>
|
||||
<br/>
|
||||
CU has a ''conversion query'' listing T as a conversion type,<br/>
|
||||
<br/>
|
||||
but not both.}}
|
||||
|
||||
|
||||
Before we get into an example of convertibility, let's list some of its underlying principles:
|
||||
# Conversion Principle: No type may both ''conform'' and ''convert'' to another.
|
||||
# Conversion Asymmetry Principle: No type may convert to another through both a ''conversion procedure'' and a ''conversion query''.
|
||||
# Conversion Non-transitivity Principle: That V converts to U and U converts to T does not imply that V converts to T.
|
||||
|
||||
Let's look at an example with which you may already be familiar.
|
||||
|
||||
<code>
|
||||
my_string: STRING_8 -- Native Eiffel string
|
||||
my_system_string: SYSTEM_STRING -- Native Microsoft .Net string
|
||||
|
||||
…
|
||||
|
||||
my_string := my_system_string
|
||||
</code>
|
||||
|
||||
In the snippet above, we have attributes declared of type <code>STRING_8</code> and <code>SYSTEM_STRING</code>.
|
||||
|
||||
We know that if we have a attribute of type <code>STRING_8</code> that we can make a direct assignment of a .Net type of string (that is, the .Net type <code>System.String</code> which we see as class <code>SYSTEM_STRING</code>) to our <code>STRING_8</code> attribute.
|
||||
|
||||
We know also that <code>SYSTEM_STRING</code> does not conform to <code>STRING_8</code>, so according to the definition of [[ET: Instructions#Assignment and attachment|compatibility]], this must happen through conversion.
|
||||
|
||||
Therefore <code>SYSTEM_STRING</code> converts to <code>STRING_8</code>. And according to the definition above this means that either:
|
||||
|
||||
# Class <code>SYSTEM_STRING</code> has a conversion query listing <code>STRING_8</code> as a conversion type, or
|
||||
# Class <code>STRING_8</code> has a conversion procedure listing <code>SYSTEM_STRING</code> as a conversion type
|
||||
|
||||
In this case <code>STRING_8</code> has a conversion procedure for objects of type <code>SYSTEM_STRING</code>. Conversion procedures are always [[ET: The Dynamic Structure: Execution Model#Creating and initializing objects|creation procedures]]. So they appear in both the <code>create</code> and the <code>convert</code> parts of the class.
|
||||
|
||||
<code>
|
||||
class STRING_8
|
||||
…
|
||||
create
|
||||
make_from_cil
|
||||
…
|
||||
convert
|
||||
make_from_cil ({SYSTEM_STRING})
|
||||
…
|
||||
</code>
|
||||
|
||||
We won't show the implementation of the conversion procedure, but as you can imagine, it initializes its target with the content of its argument.
|
||||
|
||||
Because of convertibility, this code:
|
||||
|
||||
<code>
|
||||
my_string := my_system_string
|
||||
</code>
|
||||
|
||||
is equivalent to:
|
||||
|
||||
<code>
|
||||
create my_string.make_from_cil (my_system_string)
|
||||
</code>
|
||||
|
||||
So, we've seen how <code>SYSTEM_STRING</code> converts to <code>STRING_8</code>. But, in the context of our example, we could also do this:
|
||||
|
||||
<code>
|
||||
my_system_string := my_string
|
||||
</code>
|
||||
|
||||
Which means that <code>STRING_8</code> converts to <code>SYSTEM_STRING</code>. The <code>convert</code> part of class <code>STRING_8</code> also has a conversion query listing <code>SYSTEM_STRING</code> as a conversion type:
|
||||
|
||||
<code>
|
||||
class STRING_8
|
||||
…
|
||||
create
|
||||
make_from_cil
|
||||
…
|
||||
convert
|
||||
make_from_cil ({SYSTEM_STRING})
|
||||
to_cil: {SYSTEM_STRING}
|
||||
…
|
||||
</code>
|
||||
|
||||
Because of convertibility, this code:
|
||||
|
||||
<code>
|
||||
my_system_string := my_string
|
||||
</code>
|
||||
|
||||
is equivalent to:
|
||||
|
||||
<code>
|
||||
my_system_string := my_string.to_cil
|
||||
</code>
|
||||
|
||||
You should bear in mind that assignments are not the only situation in which conversions take place. Convertibility works for other types of attachments as well. For example, if a routine calls for an argument of type <code>SYSTEM_STRING</code>, and you supply an actual argument of type <code>STRING_8</code>, this constitutes an attachment, and the conversion from <code>STRING</code> to <code>SYSTEM_STRING</code> will occur.
|
||||
|
||||
|
||||
==Tuple types==
|
||||
|
||||
The study of genericity described arrays. Another common kind of container objects bears some resemblance to arrays: sequences, or "tuples", of elements of specified types. The difference is that all elements of an array were of the same type, or a conforming one, whereas for tuples you will specify the types we want for each relevant element. A typical tuple type is of the form
|
||||
<code>
|
||||
TUPLE [X, Y, Z]
|
||||
</code>
|
||||
|
||||
denoting a tuple of at least three elements, such that the type of the first conforms to <code>X</code>, the second to <code>Y</code>, and the third to <code>Z</code>.
|
||||
|
||||
You may list any number of types in brackets, including none at all: <code>TUPLE</code>, with no types in brackets, denotes tuples of arbitrary length.
|
||||
|
||||
{{info|The syntax, with brackets, is intentionally reminiscent of generic classes, but <code>TUPLE</code> is a reserved word, not the name of a class; making it a class would not work since a generic class has a fixed number of generic parameters. You may indeed use <code>TUPLE</code> to obtain the effect of a generic class with a variable number of parameters. }}
|
||||
|
||||
To write the tuples themselves -- the sequences of elements, instances of a tuple type -- you will also use square brackets; for example
|
||||
<code>
|
||||
[x1, y1, z1]
|
||||
</code>
|
||||
|
||||
with <code>x1</code> of type <code>X</code> and so on is a tuple of type <code>TUPLE [X, Y, Z]</code>.
|
||||
|
||||
The definition of tuple types states that <code>TUPLE [X1 ... Xn]</code> denotes sequences of at least <code>n</code> elements, of which the first <code>n</code> have types respectively conforming to <code>X1, ..., Xn</code>. Such a sequence may have more than <code>n</code> elements.
|
||||
|
||||
Features available on tuple types include <code>count: INTEGER</code>, yielding the number of elements in a tuple, <code>item (i: INTEGER): ANY</code> which returns the <code>i</code>-th element, and <code>put</code> which replaces an element.
|
||||
|
||||
Tuples are appropriate when these are the only operations you need, that is to say, you are using sequences with no further structure or properties. Tuples give you "anonymous classes" with predefined features <code>count</code>, <code>item</code> and <code>put</code>. A typical example is a general-purpose output procedure that takes an arbitrary sequence of values, of arbitrary types, and prints them. It may simply take an argument of type <code>TUPLE</code>, so that clients can call it under the form
|
||||
<code>
|
||||
write ([your_integer, your_real, your_account])
|
||||
</code>
|
||||
|
||||
As soon as you need a type with more specific features, you should define a class.
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
[[Property:title|ET: Overview]]
|
||||
[[Property:weight|-15]]
|
||||
[[Property:uuid|0eaddff9-5d72-87fc-663a-2fc8a9152c25]]
|
||||
Eiffel is a method and language for the efficient description and development of quality systems.
|
||||
|
||||
As a language, Eiffel is more than a programming language. It covers not just programming in the restricted sense of implementation but the whole spectrum of software development: <br/>
|
||||
* ''Analysis, modeling and specification'', where Eiffel can be used as a purely descriptive tool to analyze and document the structure and properties of complex systems (even non-software systems).
|
||||
* ''Design and architecture'', where Eiffel can be used to build solid, flexible system structures.
|
||||
* ''Implementation'', where Eiffel provides practical software solutions with an efficiency comparable to solutions based on such traditional approaches as C and Fortran.
|
||||
* ''Maintenance'', where Eiffel helps thanks to the architectural flexibility of the resulting systems.
|
||||
* ''Documentation'', where Eiffel permits automatic generation of documentation, textual and graphical, from the software itself, as a partial substitute for separately developed and maintained software documentation.
|
||||
|
||||
|
||||
Although the language is the most visible part, Eiffel is best viewed as a '''method''', which guides system analysts and developers through the process of software construction. The Eiffel method is focused on both productivity (the ability to produce systems on time and within budget) and quality, with particular emphasis on the following quality factors: <br/>
|
||||
* ''Reliability:'' producing bug-free systems, which perform as expected.
|
||||
* ''Reusability:'' making it possible to develop systems from prepackaged, high-quality components, and to transform software elements into such reusable components for future reuse.
|
||||
* ''Extendibility:'' developing software that is truly soft -- easy to adapt to the inevitable and frequent changes of requirements and other constraints.
|
||||
* ''Portability:'' freeing developers from machine and operating system peculiarities, and enabling them to produce software that will run on many different platforms.
|
||||
* ''Maintainability:'' yielding software that is clear, readable, well structured, and easy to continue enhancing and adapting.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
[[Property:title|ET: The Software Process in Eiffel]]
|
||||
[[Property:weight|-13]]
|
||||
[[Property:uuid|eef7f31a-25de-93cc-9a33-41d991c51ccb]]
|
||||
Eiffel, as noted, supports the entire lifecycle. The underlying view of the system development lifecycle is radically different not only from the traditional "Waterfall" model (implying a sequence of discrete steps, such as analysis, global design, detailed design, implementation, separated by major changes of method and notation) but also from its more recent variants such as the spiral model or "rapid prototyping", which remain predicated on a synchronous, full-product process, and retain the gaps between successive steps.
|
||||
|
||||
Clearly, not everyone using Eiffel will follow to the letter the principles outlined below; in fact, some highly competent and successful Eiffel developers may disagree with some of them and use a different process model. In the author's mind, however, these principles fit best with the language and the rest of the method, even if practical developments may fall short of applying their ideal form.
|
||||
|
||||
==Clusters and the cluster model==
|
||||
|
||||
Unlike earlier approaches, the Eiffel model assumes that the system is divided into a number of subsystems or '''clusters'''. It keeps from the Waterfall a sequential approach to the development of each cluster (without the gaps), but promotes '''concurrent engineering''' for the overall process, as suggested by the following picture:
|
||||
|
||||
|
||||
[[Image:tutorial-2]]
|
||||
|
||||
|
||||
The Eiffel techniques developed below, in particular information hiding and Design by Contract, make the concurrent engineering process possible by letting the clusters rely on each other through clearly defined interfaces, strictly limiting the amount of knowledge that one must acquire to use the cluster, and permitting separate testing. When the inevitable surprises of a project happen, the project leader can take advantage of the model's flexibility, advancing or delaying various clusters and steps through dynamic reallocation of resources.
|
||||
|
||||
==The Single Product Principle==
|
||||
|
||||
Each of the individual cluster life cycles is based on a continuous progression of activities, from the more abstract to the more implementation-oriented:
|
||||
|
||||
|
||||
[[Image:tutorial-3]]
|
||||
|
||||
|
||||
You may view this picture as describing a process of accretion (as with a stalactite), where each steps enriches the results of the previous one. Unlike traditional views, which emphasize the multiplicity of software products -- analysis document, global and detailed design documents, program, maintenance reports... --, the principle is here to treat the software as a '''single product''' which will be repeatedly refined, extended and improved. The Eiffel programming language supports this view by providing high-level notations that can be used throughout the lifecycle, from the most general and software-independent activities of system modeling to the most exacting details of implementation tuned for optimal run-time performance.
|
||||
|
||||
These properties make Eiffel span the scope of both "object-oriented methods", with their associated notations such as UML and supporting CASE tools (whereas most such solutions do not yield an executable result), and "programming languages" (whereas most such languages are not suitable for design and analysis).
|
||||
|
||||
Additionally, within the EiffelStudio development environment, the concept of '''single product''' is extended to documents external to the software itself, by the [[Eiffel Information System|Eiffel Information System (EIS)]] which allows the linking elements of the software text to portions of external documents and vice versa.
|
||||
|
||||
==Seamlessness and reversibility==
|
||||
|
||||
The preceding ideas define the '''seamless approach''' embodied by Eiffel. With seamlessness goes '''reversibility''': the ability to go back, even late in the process, to earlier stages. Because the developers work on a single product, they can take advantages of bouts of late wisdom -- such as a great idea for adding a new function, discovered only at implementation time -- and integrate them in the product. Traditional approaches tend to discourage reversibility because it is difficult to guarantee that the analysis and design will be updated with the late changes. With the single-product principle, this is much easier to achieve.
|
||||
|
||||
Seamlessness and reversibility enhance extendibility by providing a direct mapping from the structure of the solution to the structure of the problem description, making it easier to take care of customers' change requests quickly and efficiently. They promote reliability, by avoiding possible misunderstandings between customers' and developers' views. They are a boost to maintainability. More generally, they yield a smooth, consistent software process that helps both quality and productivity.
|
||||
|
||||
==Generalization and reuse==
|
||||
|
||||
The last step of the cluster, Generalization, is unheard of in traditional models. Its task is to prepare the results of a cluster for reuse across projects by looking for elements of general applicability, and transform them for inclusion in libraries.
|
||||
|
||||
Recent object-oriented literature has used the term "refactoring" to describe a process of continuous improvement of released software. Generalization includes refactoring, but also pursues a more ambitious goal: helping turn program elements (software modules useful only as part of a certain program) into software components -- reusable parts with a value of their own, ready to be used by diverse programs that can benefit from their capabilities.
|
||||
|
||||
Of course not all companies using the method will be ready to include a Generalization phase in their. But those which do will see the reusability of their software greatly improved.
|
||||
|
||||
==Constant availability==
|
||||
|
||||
Complementing the preceding principles is the idea that, in the cluster lifecycle, the development team (under the responsibility of the project leader) should at all times maintain a current working demo which, although covering only a part of the final system, works well, and can be demonstrated or -- starting at a suitable time -- shipped as an early release. It is not a "prototype" in the sense of a meant to be thrown away, but an initial iteration towards the final product; the successive iterations will progress continuously towards until they become that final product.
|
||||
|
||||
==Compilation technology==
|
||||
|
||||
The preceding goals benefit from the ability to check frequently that the current iteration is correct and robust. Eiffel supports efficient compilation mechanisms through such mechanisms as the '''Melting Ice Technology''' in EiffelStudio. The Melting Ice achieves immediate recompilation after a change, guaranteeing a recompilation time that's a function of the size of the changes, not of the system's overall size. Even for a system of several thousand classes and several hundred thousand lines, the time to get restarted after a change to a few classes is, on a typical modern computer, a few seconds.
|
||||
|
||||
Such a "melt" (recompilation) will immediately catch (along with any syntax errors) the type errors -- often the symptoms of conceptual errors that, if left undetected, could cause grave damage later in the process or even during operation. Once the type errors have been corrected, the developers should start testing the new functionalities, relying on the power of '''assertions''' -- explained in [[ET: Design by Contract (tm), Assertions and Exceptions|"Design By Contract™ Assertions, Exceptions"]] -- to kill the bugs while they are still larvae. Such extensive unit and system testing, constantly interleaved with development, plays an important part in making sure that the "current demo" is trustworthy and will eventually yield a correct and robust product.
|
||||
|
||||
==Quality and functionality==
|
||||
|
||||
Throughout the process, the method suggests maintaining a constant '''quality''' level: apply all the style rules, put in all the assertions, handle erroneous cases (rather than the all too common practice of thinking that one will "make the product robust" later on), enforce the proper architecture. This applies to all the quality factors except possibly reusability (since one may not know ahead of time how best to generalize a component, and trying to make everything fully general may conflict with solving the specific problem at hand quickly). All that varies is '''functionality''': as the project progresses and clusters come into place, more and more of the final product's intended coverage becomes available. The project's most common question, "Can we ship something yet?", translates into "Do we cover enough?", not "Is it good enough?" (as in "Will it not crash?").
|
||||
|
||||
Of course not everyone using Eiffel can, any more than in another approach, guarantee that the ideal just presented will always hold. But it is theoretical scheme to which the method tends. It explains Eiffel's emphasis on getting everything right: the grandiose and the mundane, the structure and the details. Regarding the details, the Eiffel books cited in the bibliography include many rules, some petty at first sight, about such low-level aspects as the choice of names for classes and features (including their grammatical categories), the indentation of software texts, the style for comments (including the presence or absence of a final period), the use of spaces. Applying these rules does not, of course, guarantee quality; but they are part of a quality-oriented process, along with the more ambitious principles of design. In addition they are particularly important for the construction of quality libraries, one of the central goals of Eiffel.
|
||||
|
||||
Whenever they are compatible with the space constraints, the present chapter and the rest of this book apply these rules to their Eiffel examples.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
[[Property:title|ET: The Static Picture: System Organization]]
|
||||
[[Property:weight|-11]]
|
||||
[[Property:uuid|46d3f41e-d41c-a443-4574-403dfebb60aa]]
|
||||
We now look at the overall organization of Eiffel software.
|
||||
|
||||
References to Eiffel Software's libraries appearing in subsequent examples include: '''EiffelBase''', the fundamental open-source library covering data structures and algorithms; the '''kernel library''', a subset of EiffelBase covering the most basic notions such as arrays and strings; and '''EiffelVision 2''', an advanced graphics and GUI library providing full compatibility across platforms (Unix, Windows, OpenVMS) with native look-and-feel on each.
|
||||
|
||||
==Systems==
|
||||
|
||||
An Eiffel system is a collection of classes, one of which is designated as the root class. One of the features of the root class, which must be one of its creation procedures, is designated as the root procedure.
|
||||
|
||||
To execute such a system is to create an instance of the root class (an object created according to the class description) and to execute the root procedure. In anything more significant than "Hello World" systems, this will create new objects and apply features to them, in turn triggering further creations and feature calls.
|
||||
|
||||
For the system to make sense, it must contains all the classes on which the root '''depends''' directly or indirectly. A class <code>B</code> depends on a class <code>A</code> if it is either a '''client''' of <code>A</code>, that is to say uses objects of type <code>A</code>, or an '''heir''' of <code>A</code>, that is to say extends or specializes <code>A</code>. (These two relations, client and inheritance, are covered below.)
|
||||
|
||||
==Classes==
|
||||
|
||||
The notion of class is central to the Eiffel approach. A class is the description of a type of run-time data structures (objects), characterized by common operations features) and properties. Examples of classes include:
|
||||
* In a banking system, a class <code>ACCOUNT</code> may have features such as <code>deposit</code>, adding a certain amount to an account, <code>all_deposits</code>, yielding the list of deposits since the account's opening, and <code>balance</code>, yielding the current balance, with properties stating that <code>deposit</code> must add an element to the <code>all_deposits</code> list and update <code>balance</code> by adding the sum deposited, and that the current value of <code>balance</code> must be consistent with the lists of deposits and withdrawals.
|
||||
* A class <code>COMMAND</code> in an interactive system of any kind may have features such as <code>execute</code> and <code>undo</code> , as well as a feature <code>undoable</code> which indicates whether a command can be undone, with the property that <code>undo</code> is only applicable if <code>undoable</code> yields the value true.
|
||||
* A class <code>LINKED_LIST</code> may have features such as <code>put</code>, which adds an element to a list, and <code>count</code>, yielding the number of elements in the list, with properties stating that <code>put</code> increases <code>count</code> by one and that <code>count</code> is always non-negative.
|
||||
|
||||
We may characterize the first of these examples as an analysis class, directly modeling objects from the application domain; the second one as a design class, describing a high-level solution; and the third as an implementation class, reused whenever possible from a library such as EiffelBase. In Eiffel, however, there is no strict distinction between these categories; it is part of the approaches seamlessness that the same notion of class, and the associated concepts, may be used at all levels of the software development process.
|
||||
|
||||
==Class relations==
|
||||
|
||||
Two relations may exist between classes:
|
||||
* You can define a class <code>C</code> as a '''client''' of a class <code>A</code> to enable the features of <code>C</code> to rely on objects of type <code>A</code>.
|
||||
* You may define a class <code>B</code> as an '''heir''' of a class <code>A</code> to provide <code>B</code> with all the features and properties of <code>A</code>, letting <code>B</code> add its own features and properties and modify some of the inherited features if appropriate.
|
||||
|
||||
If <code>C</code> is a client of <code>A</code>, <code>A</code> is a '''supplier''' of <code>C</code>. If <code>B</code> is an heir of <code>A</code>, <code>A</code> is a '''parent''' of <code>B</code>. A '''descendant''' of <code>A</code> is either <code>A</code> itself or, recursively, a descendant of an heir of <code>A</code>; in more informal terms a descendant is a direct or indirect heir, or the class itself. To exclude <code>A</code> itself we talk of '''proper descendant'''. In the reverse direction the terms are '''ancestor''' and '''proper ancestor'''.
|
||||
|
||||
The client relation can be cyclic; an example involving a cycle would be classes <code>PERSON</code> and <code>HOUSE</code>, modeling the corresponding informal everyday "object" types and expressing the properties that every person has a home and every home has an architect. The inheritance (heir) relation may not include any cycle.
|
||||
|
||||
In modeling terms, client roughly represents the relation "has" and heir roughly represents "is". For example we may use Eiffel classes to model a certain system and express that every child <code>has</code> a birth date (client relation) and is a person (inheritance).
|
||||
|
||||
Distinctive of Eiffel is the rule that classes can only be connected through these two relations. This excludes the behind-the-scenes dependencies often found in other approaches, such as the use of global variables, which jeopardize the modularity of a system. Only through a strict policy of limited and explicit inter-class relations can we achieve the goals of reusability and extendibility.
|
||||
|
||||
==The global inheritance structure==
|
||||
|
||||
An Eiffel class that you write does not come into a vacuum but fits in a preordained structure, shown in the figure and involving two library classes: <code>ANY</code> and <code>NONE</code>.
|
||||
|
||||
|
||||
[[Image:tutorial-4]]
|
||||
|
||||
|
||||
Any class that does not explicitly inherit from another is considered to inherit from <code>ANY</code>, so that every class is a descendant, direct or indirect, of <code>ANY</code>. <code>ANY</code> introduces a number of general-purpose features useful everywhere, such as copying, cloning and equality testing operations (see [[ET: The Dynamic Structure: Execution Model|The Dynamic Structure: Execution Model]] ) and default input-output. The procedure <code>print</code> used in the first version of our "[[ET: Hello World|Hello World]]" comes from <code>ANY</code>.
|
||||
|
||||
<code>NONE</code> inherits from any class that has no explicit heir. Since inheritance has no cycles, <code>NONE</code> cannot have proper descendants. This makes it useful, as we will see, to specify non-exported features, and to denote the type of void values. Unlike <code>ANY</code>, class <code>NONE</code> doesn't have an actual class text; instead, it's a convenient fiction.
|
||||
|
||||
==Clusters==
|
||||
|
||||
Classes are the only form of module in Eiffel. As will be explained in more detail, they also provide the basis for the only form of type. This module-type identification is at the heart of object technology and of the fundamental simplicity of the Eiffel method.
|
||||
|
||||
Above classes, you will find the concept of cluster. A cluster is a group of related classes. Clusters are a property of the method, enabling managers to organize the development into teams. As we have already seen (in [[ET: The Software Process in Eiffel|The Software Process in Eiffel]] ) they also play a central role in the lifecycle model. Clusters are an organizational concept, not a form of module, and do not require an Eiffel programming language construct.
|
||||
|
||||
==External software==
|
||||
|
||||
The subsequent sections will show how to write Eiffel classes with their features. In an Eiffel system, however, not everything has to be written in Eiffel: some features may be '''external''' , coming from languages such as C, C++, Java, C# Fortran and others. For example a feature declaration may appear (in lieu of the forms seen later) as
|
||||
<code>
|
||||
file_status (filedesc: INTEGER): INTEGER
|
||||
-- Status indicator for filedesc
|
||||
external
|
||||
"C" alias "_fstat"
|
||||
end
|
||||
</code>
|
||||
|
||||
to indicate that it is actually an encapsulation of a C function whose original name is <code>_fstat</code>. The <code>alias</code> clause is optional, but here it is needed because the C name, starting with an underscore, is not valid as an Eiffel identifier.
|
||||
|
||||
Similar syntax exists to interface with C++ classes. EiffelStudio includes a tool called Legacy++ which will automatically produce, from a C++ class, an Eiffel class that encapsulates its facilities, making them available to the rest of the Eiffel software as bona fide Eiffel features.
|
||||
|
||||
These mechanisms illustrate one of the roles of Eiffel: as an system architecturing and software composition tool, used at the highest level to produce systems with robust, flexible structures ready for extendibility, reusability and maintainability. In these structures not everything must be written in the Eiffel programming language: existing software elements and library components can play their part, with the structuring capabilities of Eiffel (classes, information hiding, inheritance, clusters, contracts and other techniques seen in this presentation) serving as the overall wrapping mechanism.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
[[Property:title|An Eiffel Tutorial (ET)]]
|
||||
[[Property:link_title|Tutorial]]
|
||||
[[Property:weight|2]]
|
||||
[[Property:uuid|4dbc41e2-ecfc-8c50-9288-fce30f4abd90]]
|
||||
This Eiffel Tutorial (ET) should provide you with a broad understanding of what Eiffel is all about and why it is different from other technologies. Still more detail is available in [[Object-Oriented Software Construction, 2nd Edition]].
|
||||
|
||||
13
documentation/19.12/eiffel/Tutorials/index.wiki
Normal file
13
documentation/19.12/eiffel/Tutorials/index.wiki
Normal file
@@ -0,0 +1,13 @@
|
||||
[[Property:title|Eiffel Tutorials]]
|
||||
[[Property:link_title|Tutorials]]
|
||||
[[Property:weight|2]]
|
||||
[[Property:uuid|f443f10d-9dbc-4d6b-b9fb-c59af76abde9]]
|
||||
|
||||
== Tutorials about Eiffel ==
|
||||
|
||||
* [[Invitation to Eiffel (I2E)]]
|
||||
* [[An Eiffel Tutorial (ET)]]
|
||||
* [https://webcourses.inf.ethz.ch/se_courses/edx/eiffel_tutorial/] from Marco Piccioni
|
||||
* ...
|
||||
|
||||
{{Note|Please, don't hesitate to comment this page to add link to other existing tutorials on the web.This list is for now far from being complete.}}
|
||||
@@ -0,0 +1,110 @@
|
||||
[[Property:title|I2E: Classes]]
|
||||
[[Property:weight|-11]]
|
||||
[[Property:uuid|218bead9-428e-f61d-1e45-7eea4291d895]]
|
||||
A class, it was said above, is an implementation of an abstract data type. This means that it describes a set of run-time objects, characterized by the ''' features''' (operations) applicable to them, and by the formal properties of these features.
|
||||
|
||||
Such objects are called the '''direct instances''' of the class. Classes and objects should not be confused: "class" is a compile-time notion, whereas objects only exist at run time. This is similar to the difference that exists in classical programming between a program and one execution of that program, or between a type and a run-time value of that type.
|
||||
|
||||
{{info|"Object-Oriented" is a misnomer; "Class-Oriented Analysis, Design and Programming" would be a more accurate description of the method. }}
|
||||
|
||||
To see what a class looks like, let us look at a simple example, <code>ACCOUNT</code>, which describes bank accounts. But before exploring the class itself it is useful to study how it may be used by other classes, called its '''clients'''.
|
||||
|
||||
A class <code>X</code> may become a client of <code>ACCOUNT</code> by declaring one or more '''entities''' of type <code>ACCOUNT</code>. Such a declaration is of the form:
|
||||
<code>acc: ACCOUNT</code>
|
||||
|
||||
The term "entity" generalizes the more common notion of "variable". An entity declared of a reference type, such as <code>acc</code>, may at any time during execution become " '''attached to''' " an object; the type rules imply that this object must be a direct instance of <code>ACCOUNT</code> -- or, as seen below, of a "descendant" of that class.
|
||||
|
||||
[[Image:invitation-2]]
|
||||
|
||||
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>
|
||||
|
||||
|
||||
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>
|
||||
|
||||
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.
|
||||
* '''Attributes''' represent data items associated with these instances.
|
||||
|
||||
Routines are further divided into '''procedures''' (commands, which do not return a value) and '''functions''' (queries, returning a value). Here <code>may_withdraw</code> is a function returning a boolean; the other three-routines called are procedures.
|
||||
|
||||
{{info|A note on syntax: you may separate instructions by semicolons, and indeed you should when, as on the next-to-last line of the example, two or more instructions appear on a line. But the language's syntax has been designed so that the semicolon is almost always optional, regardless of the layout. Indeed the practice is to omit it between instructions or declarations on separate lines, as this results in lighter, clearer software texts. }}
|
||||
|
||||
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 {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
|
||||
|
||||
feature
|
||||
|
||||
balance: INTEGER
|
||||
owner: PERSON
|
||||
minimum_balance: INTEGER = 1000
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
feature {NONE}
|
||||
|
||||
add (sum: INTEGER)
|
||||
-- Add sum to the balance
|
||||
do
|
||||
balance := balance + sum
|
||||
end
|
||||
|
||||
end -- ACCOUNT
|
||||
</code>
|
||||
|
||||
Let us examine the features in sequence. The <code>do</code> <code>...</code> <code>end</code> distinguishes routines from attributes. So here the class has implemented <code>balance</code> as an attribute, although, as noted, a function would also have been acceptable. Feature <code>owner</code> is also an attribute.
|
||||
|
||||
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 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>.
|
||||
|
||||
In Eiffel's object-oriented programming style any operation is relative to a certain object. A client invoking the operation specifies this object by writing the corresponding entity on the left of the dot, as <code>acc</code> in <code>acc.open ("Jill")</code>. Within the class, however, the "current" instance to which operations apply usually remains implicit, so that unqualified feature names, such as <code>owner</code> in procedure <code>open</code> or <code>add</code> in <code> deposit</code>, mean "the <code>owner</code> attribute or <code>add</code> routine relative to the current instance".
|
||||
|
||||
If you need to denote the current object explicitly, you may use the special entity <code>Current</code>. For example the unqualified occurrences of <code>add</code> appearing in the class text above are equivalent to <code>Current</code>. <code>add</code>.
|
||||
|
||||
In some cases, infix or prefix notation will be more convenient than dot notation. For example, if a class <code>VECTOR</code> offers an addition routine, most people will feel more comfortable with calls of the form <code>v + w</code> than with the dot-notation call <code>v.plus (w)</code>. To make this possible it suffices to give the routine a "<code>+</code>" alias. The operation is still a normal routine call which can be invoked with either the infix form or the dot-notation.
|
||||
|
||||
The above simple example has shown the basic structuring mechanism of the language: the class. A class describes objects accessible to clients through an official interface comprising some of the class features. Features are implemented as attributes or routines; the implementation of exported features may rely on other, secret ones.
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
[[Property:title|I2E: Combining Genericity and Inheritance]]
|
||||
[[Property:weight|-3]]
|
||||
[[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
|
||||
<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 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
|
||||
<code>
|
||||
LIST [ANY]</code>
|
||||
|
||||
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>
|
||||
|
||||
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
|
||||
<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>twin</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 perceived type of the <code>Void</code> keyword which represents a void reference.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
[[Property:title|I2E: Deferred Classes and Seamless Development]]
|
||||
[[Property:weight|-2]]
|
||||
[[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'''.
|
||||
|
||||
For example, a system used by a Department of Motor Vehicles to register vehicles might include a class of the form
|
||||
<code>
|
||||
deferred class
|
||||
VEHICLE
|
||||
|
||||
feature
|
||||
|
||||
dues_paid (year: INTEGER): BOOLEAN
|
||||
do ... end
|
||||
|
||||
valid_plate (year: INTEGER): BOOLEAN
|
||||
do ... end
|
||||
|
||||
register (year: INTEGER)
|
||||
-- Register vehicle for year.
|
||||
require
|
||||
dues_paid (year)
|
||||
deferred
|
||||
ensure
|
||||
valid_plate (year)
|
||||
end
|
||||
|
||||
... Other features, deferred or effective ...
|
||||
|
||||
end -- VEHICLE
|
||||
</code>
|
||||
|
||||
This example assumes that no single registration algorithm applies to all kinds of vehicle; passenger cars, motorcycles, trucks etc. are all registered differently. But the same precondition and postcondition apply in all cases. The solution is to treat register as a deferred routine, making VEHICLE a deferred class. Descendants of class VEHICLE, such as CAR or TRUCK, effect this routine, that is to say, give effective versions. An effecting is similar to a redefinition; only here there is no effective definition in the original class, just a specification in the form of a deferred routine. The term '''redeclaration''' covers both redefinition and effecting.
|
||||
|
||||
|
||||
[[Image:invitation-5]]
|
||||
|
||||
|
||||
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>
|
||||
|
||||
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.
|
||||
|
||||
Deferred classes are particularly useful at the design stage. The first version of a module may be a deferred class, which will later be refined into one or more effective classes. Eiffel's Design by Contract™ mechanisms are essential here: you may a precondition and a postcondition with a routine even though it is a deferred routine (as with register above), and an invariant with a class even though it is a deferred class. This enables you, as a designer, to attach precise semantics to a module at the design stage long before you will make any implementation choices.
|
||||
|
||||
Beyond design and implementation, these techniques extend to the earliest stage of development, analysis. Deferred classes written at that stage describe not software objects, but objects from the external world being modeled -- documents, airplanes, investments. Here again the presence of contracts to express constraints, and the language's other structuring facilities, provide an attractive combination.
|
||||
|
||||
Eiffel appears here in its full role of a lifecycle approach, covering areas traditionally considered separate: program implementation, the traditional province of development environments; system modeling and architecture, the traditional province of CASE tools based on UML or similar notations disconnected from the rest of the lifecycle. Eiffel instead emphasizes the fundamental unity of the software process and the usefulness of a single set of notations, concepts and tools applicable throughout. Such a seamless approach is indispensable to support the inevitable reversals that occur during the process of building software, such as detecting at implementation time a problem that leads to a change in the system's functionality, set at analysis time. The use of separate tools and notations, such as UML on one side and a programming language on the other, makes such round-trips difficult at best and often leads to monolithic, hard-to-change software. Eiffel lets you focus on the issues, without interposing artificial barriers between different software development activities. You'll use the fundamental problem-solving techniques -- data abstraction through classes, precise specification through contracts, modularity through information hiding, rational organization through inheritance, decentralized architecture through dynamic binding, parameterization of the solution through genericity, reusability through all these techniques -- all along; only the level of abstraction changes.
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
[[Property:title|I2E: Design by Contract and Assertions]]
|
||||
[[Property:weight|-9]]
|
||||
[[Property:uuid|f563aa75-3a5a-5110-b4f1-07da5448f668]]
|
||||
If classes are to deserve their definition as abstract data type implementations, they must be known not just by the available operations, but also by the formal properties of these operations, which did not yet appear in the preceding example.
|
||||
|
||||
==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.
|
||||
|
||||
|
||||
With appropriate assertions, the class <code>ACCOUNT</code> becomes:
|
||||
<code>
|
||||
class
|
||||
ACCOUNT
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature
|
||||
... 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
|
||||
|
||||
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
|
||||
|
||||
feature {NONE}
|
||||
|
||||
add ...
|
||||
|
||||
make (initial: INTEGER)
|
||||
-- Initialize account with balance initial.
|
||||
require
|
||||
initial >= minimum_balance
|
||||
do
|
||||
balance := initial
|
||||
end
|
||||
|
||||
invariant
|
||||
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.
|
||||
|
||||
==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
|
||||
<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.
|
||||
|
||||
{{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.
|
||||
|
||||
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>
|
||||
|
||||
so it would be invalid for a client to include a call such as
|
||||
<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.
|
||||
|
||||
==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.
|
||||
|
||||
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.
|
||||
|
||||
The underlying theory of Design by Contract™, the centerpiece of the Eiffel method, views software construction as based on contracts between clients (callers) and suppliers (routines), relying on mutual obligations and benefits made explicit by the assertions.
|
||||
|
||||
==The Contract Form==
|
||||
|
||||
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:
|
||||
<code>
|
||||
class interface ACCOUNT
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature
|
||||
|
||||
balance: INTEGER
|
||||
...
|
||||
|
||||
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
|
||||
|
||||
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 (in [[I2E: Classes|Classes]]), 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.
|
||||
|
||||
==Contracts for testing and debugging==
|
||||
|
||||
Under EiffelStudio you may also set up compilation options, for the whole system or specific classes only, to evaluate assertions at run time, to uncover potential errors ("bugs"). EiffelStudio provides several levels of assertion monitoring: preconditions only, postconditions etc. When monitoring is on, an assertion which evaluates to true has no further effect on the execution. An assertion that evaluates to false will trigger an exception, as described next; unless you have written an appropriate exception handler, the exception will cause an error message and termination with a precise message and a call trace.
|
||||
|
||||
This ability to check assertions provides a powerful testing and debugging mechanism, in particular because the classes of the EiffelBase Libraries, widely used in Eiffel software development, are protected by carefully written assertions.
|
||||
|
||||
Run-time monitoring, however, is only one application of assertions, whose role as design and documentation aids, as part of the theory of Design by Contract™, exerts a pervasive influence on the Eiffel style of software development.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
[[Property:title|I2E: Design Principles]]
|
||||
[[Property:weight|-13]]
|
||||
[[Property:uuid|529659bd-ec13-5805-87f2-2fd9318ad442]]
|
||||
The aim of Eiffel is to help specify, design, implement and modify quality software. This goal of quality in software is a combination of many factors; the language design concentrated on the three factors which, in the current state of the industry, are in direct need of improvements: reusability, extendibility and reliability. Also important were other factors such as efficiency, openness and portability.
|
||||
|
||||
'''Reusability''' is the ability to produce components that may serve in many different applications. Central to the Eiffel approach is the presence of predefined libraries such as EiffelBase, and the language's support for the production of new libraries.
|
||||
|
||||
'''Extendibility''' is the ability to produce easily modifiable software. "Soft" as software is supposed to be, it is notoriously hard to modify software systems, especially large ones.
|
||||
|
||||
Among quality factors, reusability and extendibility play a special role: satisfying them means having less software to write -- and hence more time to devote to other important goals such as efficiency, ease of use or integrity.
|
||||
|
||||
The third fundamental factor is '''reliability, ''' the ability to produce software that is correct and robust -- that is to say, bug-free. Eiffel techniques such as static typing, assertions, disciplined exception handling and automatic garbage collection are essential here.
|
||||
|
||||
Three other factors are also part of Eiffel's principal goals: <br/>
|
||||
* The language enables implementers to produce high '''efficiency''' compilers, so that systems developed with Professional Eiffel may run under speed and space conditions similar to those of programs written in lower-level languages.
|
||||
* Ensuring '''openness''', so that Eiffel software may cooperate with programs written in other languages.
|
||||
* Guaranteeing '''portability''' by a platform-independent language definition, so that the same semantics may be supported on many different platforms.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
[[Property:title|I2E: Event-Driven Programming and Agents]]
|
||||
[[Property:weight|-7]]
|
||||
[[Property:uuid|16fdab60-ae42-1bb8-f4bb-89e34d18a842]]
|
||||
The division of roles in object technology is clear: of the two principal constituents of a system, object types and operations, the first dominates. Classes, representing object types, determines the structure of the software; every routine, representing an operation, belongs to a class.
|
||||
|
||||
In some circumstances it is useful to define an object that denotes an operation. This is especially useful if you want to build an object structure that refers to operations, so that you can later traverse the structure and execute the operations encountered. A typical application is '''event-driven programming''' for Graphical User Interfaces (GUI), including Web programming. In GUI programming you will want to record properties of the form
|
||||
<code>
|
||||
"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
|
||||
<code>
|
||||
[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.
|
||||
|
||||
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
|
||||
<code>
|
||||
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'''.
|
||||
|
||||
{{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>
|
||||
|
||||
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 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.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
[[Property:title|I2E: Exceptions]]
|
||||
[[Property:weight|-8]]
|
||||
[[Property:uuid|e3e10dac-0dd7-bbe1-240c-6a6985c7376a]]
|
||||
Whenever there is a contract, the risk exists that someone will break it. This is where exceptions come in.
|
||||
|
||||
Exceptions -- contract violations -- may arise from several causes. One is an assertion violation, if you've selected run-time assertion monitoring. Another is a signal triggered by the hardware or operating system to indicate an abnormal condition such as arithmetic overflow, or an attempt to create a new object when there's not enough memory available.
|
||||
|
||||
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.)
|
||||
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
</code>
|
||||
|
||||
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 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.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
[[Property:title|I2E: Genericity]]
|
||||
[[Property:weight|-6]]
|
||||
[[Property:uuid|091c0b65-73de-b454-b3f2-d8752983780e]]
|
||||
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: genericity (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>
|
||||
|
||||
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>
|
||||
|
||||
As the last example indicates, an actual generic parameter may itself be generically derived.
|
||||
|
||||
It would not be possible, without genericity, to have static type checking in a realistic object-oriented language.
|
||||
|
||||
A variant of this mechanism, constrained genericity, will enable a class to place specific requirements on possible actual generic parameters. Constrained genericity will be described after inheritance.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
[[Property:title|I2E: Inheritance]]
|
||||
[[Property:weight|-5]]
|
||||
[[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:
|
||||
<code>
|
||||
class ARRAYED_LIST [G]
|
||||
inherit
|
||||
LIST [G]
|
||||
ARRAY [G]
|
||||
|
||||
export ... See below ... end
|
||||
|
||||
feature
|
||||
... Specific features of fixed-size lists ...
|
||||
|
||||
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.
|
||||
|
||||
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:
|
||||
|
||||
|
||||
[[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:
|
||||
<code>
|
||||
class ARRAYED_LIST [G]
|
||||
inherit
|
||||
LIST [G]
|
||||
ARRAY [G]
|
||||
|
||||
export {NONE} all end
|
||||
|
||||
... 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>.
|
||||
|
||||
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.
|
||||
|
||||
The very power of inheritance demands adequate means to keep it under control. Multiple inheritance, in particular, raises the question of name conflicts between features inherited from different parents; this case will inevitably arise in practice, especially for classes contributed by independent developers. You may remove such a name conflict through '''renaming''', as in
|
||||
<code>
|
||||
class C
|
||||
inherit
|
||||
A
|
||||
rename
|
||||
x as x1,
|
||||
y as y1
|
||||
end
|
||||
|
||||
B
|
||||
rename
|
||||
x as x2,
|
||||
y as y2
|
||||
end
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
[[Property:title|I2E: Invitation to Eiffel Copyright]]
|
||||
[[Property:weight|0]]
|
||||
[[Property:uuid|ce7b4af4-b669-9fec-92e1-c24c4f089336]]
|
||||
Title: Invitation to Eiffel, Eiffel Software Technical Report TR-EI-67/IV.
|
||||
|
||||
==Publication history==
|
||||
First published 1987. Some revisions (in particular Web versions) have used the title "Eiffel in a Nutshell" <br/>
|
||||
This version: July 2001. Introduces coverage of agents; several other improvements. Corresponds to release 5.0 of the EiffelStudio environment.
|
||||
==Author==
|
||||
Bertrand Meyer
|
||||
==Software Credits==
|
||||
See acknowledgments in book ''[[Eiffel: The Language]]''.
|
||||
==Cover Design==
|
||||
Rich Ayling.
|
||||
==Copyright notice and proprietary information==
|
||||
Copyright Interactive Software Engineering Inc. (Eiffel Software), 2001. May not be reproduced in any form (including electronic storage) without the written permission of Eiffel Software . "Eiffel Power" and the Eiffel Power logo are trademarks of Eiffel Software . All uses of the product documented here are subject to the terms and conditions of the Eiffel Software Eiffel user license. Any other use or duplication is a violation of the applicable laws on copyright, trade secrets and intellectual property.
|
||||
==Special duplication permission for educational institutions==
|
||||
Degree-granting educational institutions using Eiffel Software Eiffel for teaching purposes as part of the [http://www.eiffel.com/educators/resources.html Eiffel University Partnership Program] may be permitted under certain conditions to copy specific parts of this book. Contact Eiffel Software for details.
|
||||
{{info|About Eiffel Software (Interactive Software Engineering) helps you produce software better, faster and cheaper. Eiffel Software provides a wide range of products and services based on object technology, including Eiffel Software Eiffel, a complete development environment for the full system life cycle. Eiffel Software's training courses, available worldwide, cover key management and technical topics. Eiffel Software's consultants are available to address your project needs at all levels. Eiffel Software's TOOLS (Technology of Object-Oriented Languages and Systems) conferences, [http://www.tools-conferences.com http://www.tools-conferences.com] , are the meeting point for anyone interested in the software technologies of the future. Eiffel Software originated one of the earliest . NET products and offers a full range of . NET services and training at [http://www.dotnetexperts.com http://www.dotnetexperts.com] . }}
|
||||
|
||||
==For more information==
|
||||
Interactive Software Engineering Inc. <br/>
|
||||
Eiffel Software Building, <br/>
|
||||
356 Storke Road, <br/>
|
||||
Goleta, <br/>
|
||||
CA, 93117 <br/>
|
||||
USA. <br/>
|
||||
<br/>
|
||||
Telephone 805-685-1006, <br/>
|
||||
Fax 805-685-6869
|
||||
==Internet and email==
|
||||
Eiffel Software maintains a rich source of information at [http://eiffel.com http://eiffel.com] , with more than 1200 Web pages including online documentation, downloadable files, product descriptions, links to Eiffel Software partners, University Partnership program, mailing list archives, announcements, press coverage, Frequently Asked Questions, Support pages, and much more. Visit [http://www.eiffel.com/general/contact_details.html http://www.eiffel.com/general/contact_details.html] to request information about products and services. To subscribe to the Eiffel Software Eiffel user list, go to [http://groups.eiffel.com/join http://groups.eiffel.com/join] .
|
||||
==Support programs==
|
||||
Eiffel Software offers a variety of support options tailored to the diverse needs of its customers. See [http://support.eiffel.com http://support.eiffel.com] for details.
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
[[Property:title|I2E: Object-Oriented Design]]
|
||||
[[Property:weight|-12]]
|
||||
[[Property:uuid|e0a1f24e-5dd7-e5f8-8be8-8da32cc6a91c]]
|
||||
To achieve reusability, extendibility and reliability, the principles of object-oriented design provide the best known technical answer.
|
||||
|
||||
An in-depth discussion of these principles fall beyond the scope of this introduction but here is a short definition:
|
||||
|
||||
{{info|Object-oriented design is the construction of software systems as structured collections of abstract data type implementations, or "classes". }}
|
||||
|
||||
|
||||
The following points are worth noting in this definition:<br/>
|
||||
* The emphasis is on structuring a system around the types of objects it manipulates (not the functions it performs on them) and on reusing whole data structures together with the associated operations (not isolated routines).
|
||||
* Objects are described as instances of abstract data types -- that is to say, data structures known from an official interface rather than through their representation.
|
||||
* The basic modular unit, called the class, describes one implementation of an abstract data type (or, in the case of "deferred" classes, as studied below, a set of possible implementations of the same abstract data type).
|
||||
* The word ''collection'' reflects how classes should be designed: as units which are interesting and useful on their own, independently of the systems to which they belong, and may be reused by many different systems. Software construction is viewed as the assembly of existing classes, not as a top-down process starting from scratch.
|
||||
* Finally, the word ''structured'' reflects the existence of two important relations between classes: the client and inheritance relations.
|
||||
|
||||
|
||||
Eiffel makes these techniques available to developers in a simple and practical way.
|
||||
|
||||
As a language, Eiffel includes more than presented in this introduction, but not much more; it is a small language, not much bigger (by such a measure as the number of keywords) than Pascal. It was meant to be a member of the class of languages which programmers can master entirely -- as opposed to languages of which most programmers know only a subset. Yet it is appropriate for the development of industrial software systems, as has by now been shown by many full-scale projects, some in the thousands of classes and hundreds of thousands of lines, in companies around the world.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
[[Property:title|I2E: Polymorphism and Dynamic Binding]]
|
||||
[[Property:weight|-4]]
|
||||
[[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 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:
|
||||
<code>
|
||||
class RECTANGLE
|
||||
inherit
|
||||
POLYGON
|
||||
redefine
|
||||
perimeter
|
||||
end
|
||||
|
||||
feature -- Specific features of rectangles, such as:
|
||||
|
||||
side1: REAL
|
||||
side2: REAL
|
||||
|
||||
perimeter: REAL
|
||||
-- Rectangle-specific version
|
||||
do
|
||||
Result := 2 * (side1 + side2)
|
||||
end
|
||||
|
||||
... 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).
|
||||
|
||||
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
|
||||
...
|
||||
|
||||
create p
|
||||
create r
|
||||
...
|
||||
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'''.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
[[Property:title|I2E: Putting a System Together]]
|
||||
[[Property:weight|-1]]
|
||||
[[Property:uuid|97460714-8ae1-a7cb-8216-235827045ea6]]
|
||||
We have now studied the constituents of Eiffel software. It remains to see how you can combine these elements into executable '''systems''' (the Eiffel concept closest to the traditional notion of program) and libraries.
|
||||
|
||||
How do you get an executable system? All you need is to <br/>
|
||||
* Provide a set of classes, called a '''universe'''.
|
||||
* Designate one of these classes as the '''root class'''.
|
||||
* Designate one of its creation procedures as the '''root procedure'''.
|
||||
|
||||
|
||||
This defines what it means to execute the system: create one direct instance of the root class (the execution's '''root object'''); and call the root procedure on it. That's all.
|
||||
|
||||
In any practical case, the root procedure will create other objects, call other routines on them, leading to further creations and calls.
|
||||
|
||||
For the system to be valid, it must include all the classes which the root '''needs''' directly or indirectly; a class "needs" another if it is one of its heirs or clients.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
* 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.
|
||||
|
||||
|
||||
Such a system specification is written in an ecf (eiffel configuration file) which is an xml based file format. It can be created by using the project settings in EiffelStudio.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
[[Property:title|I2E: Types]]
|
||||
[[Property:weight|-10]]
|
||||
[[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>.
|
||||
|
||||
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 [[I2E: Classes]] ), 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.)
|
||||
|
||||
|
||||
[[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 "<".
|
||||
|
||||
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.
|
||||
|
||||
In the case of basic types, for obvious reasons of efficiency, the compilation mechanism implements the standard arithmetic and boolean operations directly through the corresponding machine operations, not through routine calls. But this is only a compiler optimization, which does not hamper the conceptual homogeneity of the type edifice.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
[[Property:title|I2E: What Must I Know First?]]
|
||||
[[Property:weight|-14]]
|
||||
[[Property:uuid|f40b4a30-87f7-2c27-b6e7-ef2f2a74661b]]
|
||||
This Invitation assumes that you have some experience of software development, but that's all. Previous exposure to object technology is not required. If you've had it, it will help; but if it has all been to notations like UML or programming languages like C++ and Java, you should not let it guide your study of this Invitation. Although Eiffel shares a number of properties with these other approaches, it takes a fresh path to object technology, based on a small number of simple, far-reaching concepts.
|
||||
|
||||
Once you are familiar with the basic ideas you may want to try them with EiffelStudio, which provides a direct implementation of the Eiffel concepts, available in a completely portable way across Windows, Linux, many versions of Unix and OpenVMS.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
[[Property:title|Invitation to Eiffel (I2E)]]
|
||||
[[Property:link_title|Invitation to Eiffel]]
|
||||
[[Property:weight|-15]]
|
||||
[[Property:uuid|7a606387-2653-b122-b4ef-e283a389656e]]
|
||||
The Invitation to Eiffel (I2E) is a short set of pages that should provide you with the essence of the Eiffel way, without taking too much of your time. Enjoy this profoundly different way of thinking about developing software! When you are done, move on to the more detailed Eiffel tutorial.
|
||||
|
||||
Reference in New Issue
Block a user