mirror of
https://github.com/EiffelSoftware/eiffel-org.git
synced 2025-12-09 00:02:53 +01:00
Author:halw
Date:2008-10-20T22:11:16.000000Z git-svn-id: https://svn.eiffel.com/eiffel-org/trunk@90 abb3cda0-5349-4a8f-a601-0c33ac3a8c38
This commit is contained in:
@@ -1,6 +1,20 @@
|
|||||||
[[Property:title|Community]]
|
[[Property:title|Community]]
|
||||||
[[Property:weight|2]]
|
[[Property:weight|2]]
|
||||||
[[Property:uuid|75c75712-7c3e-2757-51b7-77b404471e2e]]
|
[[Property:uuid|75c75712-7c3e-2757-51b7-77b404471e2e]]
|
||||||
The Community book is a vehicle through which community members will be able to submit and/or revise documentation content.
|
EiffelStudio community:
|
||||||
|
:'''You can become a contributor!'''
|
||||||
|
|
||||||
|
|
||||||
|
EiffelStudio documentation is available in a convenient Wiki format. This makes it possible to improve the documentation continuously and make sure it is always up to date.
|
||||||
|
|
||||||
|
Community input is welcome. If you are interested in improving and developing EiffelStudio documentation, you can become:
|
||||||
|
|
||||||
|
* A contributor: authorized to edit existing pages to any book, and to add pages to the "Community contributions" book.
|
||||||
|
**"Community contributions" book pages can be added to other books by an editor.
|
||||||
|
|
||||||
|
* An editor: authorized to modify and add pages in any book.
|
||||||
|
|
||||||
|
To become a contributor, you should be proficient in Eiffel technology and have good written English skills. To become an editor, you should already be a contributor and have contributed significant improvements or additions to the documentation.
|
||||||
|
|
||||||
|
If you are interested in becoming a contributor, please send an email to info@eiffel.com with a short description of your Eiffel experience and any other relevant background.
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ Eiffel directly implements the ideas of Design by Contract™ , which enhance
|
|||||||
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.
|
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:
|
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"
|
{| border="1"
|
||||||
|-
|
|-
|
||||||
| style="width=10%" |provide telephone service
|
| style="width=10%" |provide telephone service
|
||||||
@@ -28,6 +30,7 @@ Provide telephone service.
|
|||||||
No need to provide anything if bill not paid.
|
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.
|
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'''.
|
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'''.
|
||||||
@@ -36,7 +39,7 @@ In addition to preconditions and postconditions, contract clauses include '''cla
|
|||||||
|
|
||||||
==Expressing assertions==
|
==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 [[10 Other Mechanisms|"Instructions", page 84]] ): loop invariants and variants, check instructions.
|
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 [[10 Other Mechanisms#Instructions|"Instructions"]] ): loop invariants and variants, check instructions.
|
||||||
|
|
||||||
Here is a partial update of class <code>ACCOUNT</code> with more assertions:
|
Here is a partial update of class <code>ACCOUNT</code> with more assertions:
|
||||||
<code>
|
<code>
|
||||||
@@ -51,7 +54,7 @@ feature -- Access
|
|||||||
balance: INTEGER
|
balance: INTEGER
|
||||||
-- Current balance
|
-- Current balance
|
||||||
|
|
||||||
deposit_count: INTEGER is
|
deposit_count: INTEGER
|
||||||
-- Number of deposits made since opening
|
-- Number of deposits made since opening
|
||||||
do
|
do
|
||||||
... As before ...
|
... As before ...
|
||||||
@@ -59,7 +62,7 @@ feature -- Access
|
|||||||
|
|
||||||
feature -- Element change
|
feature -- Element change
|
||||||
|
|
||||||
deposit (sum: INTEGER) is
|
deposit (sum: INTEGER)
|
||||||
-- Add `sum' to account.
|
-- Add `sum' to account.
|
||||||
require
|
require
|
||||||
non_negative: sum >= 0
|
non_negative: sum >= 0
|
||||||
@@ -93,6 +96,8 @@ The precondition of a routine expresses conditions that the routine is imposing
|
|||||||
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 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:
|
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"
|
{| border="1"
|
||||||
|-
|
|-
|
||||||
| style="width=10%" |<code>deposit</code>
|
| style="width=10%" |<code>deposit</code>
|
||||||
@@ -112,18 +117,21 @@ Update deposits list and balance.
|
|||||||
No need to handle negative arguments.
|
No need to handle negative arguments.
|
||||||
|}
|
|}
|
||||||
|
|
||||||
|
|
||||||
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.
|
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.
|
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
|
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>
|
<code>
|
||||||
if x.n > 0 then x.r end
|
if x.n > 0 then
|
||||||
|
x.r
|
||||||
|
end
|
||||||
</code>
|
</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.
|
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 ( [[10 Other Mechanisms|"Instructions", page 84]] ) is recommended if the reason for omitting the test is non-trivial. }}
|
{{note|In such a case, a <code>check</code> instruction as introduced later ( [[10 Other Mechanisms#Instructions|"Instructions"]] ) is recommended if the reason for omitting the test is non-trivial. }}
|
||||||
|
|
||||||
==Using contracts for built-in reliability==
|
==Using contracts for built-in reliability==
|
||||||
|
|
||||||
@@ -137,28 +145,27 @@ Contracts in Eiffel are not just wishful thinking. They can be monitored at run
|
|||||||
|
|
||||||
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:
|
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:
|
||||||
|
|
||||||
{{note| '''Assertion Violation rule '''A run-time assertion violation is the manifestation of a bug. }}
|
{{rule|name=Assertion Violation|text=A run-time assertion violation is the manifestation of a bug. }}
|
||||||
|
|
||||||
To be more precise: <br/>
|
To be more precise: <br/>
|
||||||
* A precondition violation signals a bug in the client, which did not observe its part of the deal.
|
* 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.
|
* 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.
|
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/>
|
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>no</code> : assertions have no run-time effect.
|
||||||
* <code>require</code> : monitor preconditions only, on routine entry.
|
* <code>require</code> : monitor preconditions only, on routine entry.
|
||||||
* <code>ensure</code> : preconditions on entry, postconditions on exit.
|
* <code>ensure</code> : preconditions on entry, postconditions on exit.
|
||||||
* <code>invariant</code> : like <code>ensure</code>, plus class invariant on both entry and exit for qualified calls.
|
* <code>invariant</code> : same as <code>ensure</code>, plus class invariant on both entry and exit for qualified calls.
|
||||||
* <code>all</code> : like <code>invariant</code>, plus <code>check</code> instructions, loop invariants and loop variants ( [[10 Other Mechanisms|"Exception handling", page 46]] ).
|
* <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 ( [[8 Design by Contract (tm), Assertions and Exceptions|"Instructions", page 84]] ). Unless the software has an explicit "retry" plan as explained in the discussion of exceptions, the violation will cause 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.
|
An assertion violation, if detected at run time under one of these options other than the first, will cause an exception ( [[8 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 cause 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.
|
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 [[3 The Software Process in Eiffel|"Quality and functionality", page 10]] , 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.
|
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 [[3 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.
|
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.
|
||||||
|
|
||||||
@@ -202,25 +209,25 @@ The words <code>interface class</code> are used instead of just <code>class</cod
|
|||||||
|
|
||||||
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/>
|
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.
|
* 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 section [[4 Hello World|4]] , of always including such comments and writing them carefully.)
|
* Header comments of these features, which carry informal descriptions of their purpose. (Hence the importance, mentioned in section [[4 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).
|
* Preconditions and postconditions of these features (at least the subclauses involving only exported features).
|
||||||
* Class invariant (same observation).
|
* 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 [[5 The Static Picture: System Organization|"External software", page 16]] above and [[10 Other Mechanisms|"Once routines and shared objects", page 82]] below); assertion subclauses involving non-exported features; and some keywords not useful in the documentation, such as <code>is</code> for a routine.
|
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 [[5 The Static Picture: System Organization#External_software|"External software"]] above and [[10 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 (page [[6 The Dynamic Structure: Execution Model|19]] ), 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.
|
In accordance with the Uniform Access principle (described in [[6 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 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/>
|
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.
|
* 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 (section [[3 The Software Process in Eiffel|3]] ).
|
* 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 [[3 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 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 ( [[9 Inheritance|"Flat and Flat-Contract Forms", page 72]] ) are the standard form of library documentation, used extensively, for example, in the book <span> [http://www.eiffel.com/doc/page.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.
|
The Contract Form -- or its variant the Flat-Contract Form, which takes account of inheritance ( [[9 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/doc/page.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==
|
==Exception handling==
|
||||||
|
|
||||||
@@ -232,11 +239,12 @@ Another application of Design by Contract™ governs the handling of unexpect
|
|||||||
* As a result the routine may fail too -- causing an exception in its own caller.
|
* 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.
|
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:
|
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>
|
<code>
|
||||||
read_next_character (f: FILE) is
|
read_next_character (f: FILE)
|
||||||
-- Make next character available in last_character.
|
-- Make next character available in last_character.
|
||||||
-- If impossible, set failed to True.
|
-- If impossible, set failed to True.
|
||||||
require
|
require
|
||||||
@@ -311,13 +319,13 @@ Concretely, exceptions may result from the following events: <br/>
|
|||||||
* Operating system signal:arithmetic overfolow; no memory available for a requested creation or clone -- 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.)
|
* Operating system signal:arithmetic overfolow; no memory available for a requested creation or clone -- 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 ( [[10 Other Mechanisms|"Constant and unique attributes", page 83]] ) 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 than 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.
|
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 ( [[10 Other Mechanisms#Constant_and_unique_attributes|"Constant and unique 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 than 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™==
|
==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/>
|
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 ( [[9 Inheritance|"Applications of deferred classes", page 60]] ) 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.
|
* 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 ( [[9 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 [[9 Inheritance|"Inheritance and contracts", page 66]] .
|
* 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 [[9 Inheritance#Inheritance_and_contracts|"Inheritance and contracts"]] .
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -22,9 +22,9 @@ feature
|
|||||||
...
|
...
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
This makes <code> D </code> an heir of <code> A </code>, <code> B </code> and any other class listed. Eiffel supports '''multiple''' inheritance: a class may have as many parents as it needs. Later sections ( [[9 Inheritance|"Multiple inheritance and renaming", page 64]] and [[9 Inheritance|"Repeated inheritance and selection", page 73]] ) will explain how to handle possible conflicts between parent features.
|
This makes <code>D</code> an heir of <code>A</code>, <code>B</code> and any other class listed. Eiffel supports '''multiple''' inheritance: a class may have as many parents as it needs. Later sections ( [[9 Inheritance#Multiple_inheritance_and_renaming|"Multiple inheritance and renaming"]] and [[9 Inheritance#Repeated_inheritance_and_selection|"Repeated inheritance and selection"]] ) will explain how to handle possible conflicts between parent features.
|
||||||
|
|
||||||
{{note|This discussion will rely on the terminology introduced on page [[5 The Static Picture: System Organization|14]] : descendants of a class are the class itself, its heirs, the heirs of its heirs and so on. Proper descendants exclude the class itself. The reverse notions are ancestors and proper ancestors. }}
|
{{note|This discussion will rely on the terminology introduced in [[5 The Static Picture: System Organization|The Static Picture: System Organization]]: descendants of a class are the class itself, its heirs, the heirs of its heirs and so on. Proper descendants exclude the class itself. The reverse notions are ancestors and proper ancestors. }}
|
||||||
|
|
||||||
By default <code>D</code> will simply include all the original features of <code>A</code>, <code>B</code>, ..., to which it may add its own through its <code>feature</code> clauses if any. But the inheritance mechanism is more flexible, allowing <code>D</code> to adapt the inherited features in many ways. Each parent name -- <code>A</code>, <code>B</code>, ... in the example -- can be followed by a Feature Adaptation clause, with subclauses, all optional, introduced by keywords <code>rename</code>, <code>export</code>, <code>undefine</code>, <code>redefine</code> and <code>select</code>, enabling the author of <code>A</code> to make the best use of the inheritance mechanism by tuning the inherited features to the precise needs of <code>D</code>. This makes inheritance a principal tool in the Eiffel process, mentioned earlier, of carefully crafting each individual class, like a machine, for the benefit of its clients. The next sections review the various Feature Adaptation subclauses.
|
By default <code>D</code> will simply include all the original features of <code>A</code>, <code>B</code>, ..., to which it may add its own through its <code>feature</code> clauses if any. But the inheritance mechanism is more flexible, allowing <code>D</code> to adapt the inherited features in many ways. Each parent name -- <code>A</code>, <code>B</code>, ... in the example -- can be followed by a Feature Adaptation clause, with subclauses, all optional, introduced by keywords <code>rename</code>, <code>export</code>, <code>undefine</code>, <code>redefine</code> and <code>select</code>, enabling the author of <code>A</code> to make the best use of the inheritance mechanism by tuning the inherited features to the precise needs of <code>D</code>. This makes inheritance a principal tool in the Eiffel process, mentioned earlier, of carefully crafting each individual class, like a machine, for the benefit of its clients. The next sections review the various Feature Adaptation subclauses.
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ inherit
|
|||||||
|
|
||||||
feature -- Element change
|
feature -- Element change
|
||||||
|
|
||||||
deposit (sum: INTEGER) is
|
deposit (sum: INTEGER)
|
||||||
-- Add sum to account.
|
-- Add sum to account.
|
||||||
do
|
do
|
||||||
... New implementation (see below) ...
|
... New implementation (see below) ...
|
||||||
@@ -59,6 +59,16 @@ feature -- Element change
|
|||||||
... Other features ...
|
... Other features ...
|
||||||
|
|
||||||
end -- class SAVINGS_ACCOUNT
|
end -- class SAVINGS_ACCOUNT
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Without the <code>redefine</code> subclause, the declaration of <code>deposit</code> would be invalid, yielding two features of the same name, the inherited one and the new one. The subclause makes this valid by specifying that the new declaration will override the old one.
|
Without the <code>redefine</code> subclause, the declaration of <code>deposit</code> would be invalid, yielding two features of the same name, the inherited one and the new one. The subclause makes this valid by specifying that the new declaration will override the old one.
|
||||||
@@ -70,7 +80,10 @@ Precursor (sum)
|
|||||||
... Instructions to update the interest ...
|
... Instructions to update the interest ...
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Besides changing the implementation of a routine, a redefinition can turn an argument-less function into an attribute; for example a proper descendant of <code> ACCOUNT </code> could redefine <code> deposits_count </code>, originally a function, as an attribute. The Uniform Access Principle (page [[6 The Dynamic Structure: Execution Model|19]] ) guarantees that the redefinition makes no change for clients, which will continue to use the feature under the form <code> acc.deposits_count </code>.
|
Besides changing the implementation of a routine, a redefinition can turn an argument-less function into an attribute; for example a proper descendant of <code>ACCOUNT</code> could redefine <code>deposits_count</code>, originally a function, as an attribute. The Uniform Access Principle (introduced in [[6 The Dynamic Structure: Execution Model|The Dynamic Structure: Execution Model]] ) guarantees that the redefinition makes no change for clients, which will continue to use the feature under the form
|
||||||
|
<code>
|
||||||
|
acc.deposits_count
|
||||||
|
</code>
|
||||||
|
|
||||||
==Polymorphism==
|
==Polymorphism==
|
||||||
|
|
||||||
@@ -78,7 +91,7 @@ The inheritance mechanism is relevant to both roles of classes: module and type.
|
|||||||
|
|
||||||
'''Polymorphic assignment''' supports this second role. In an assignment <code>x := y</code>, the types of <code>x</code> and <code>y</code> do not have, with inheritance, to be identical; the rule is that the type of <code>y</code> must simply '''conform''' to the type of <code>x</code>. A class <code>D</code> conforms to a class <code>A</code> if and only if it is a descendant of <code>A</code> (which includes the case in which <code>A</code> and <code>D</code> are the same class); if these classes are generic, conformance of <code>D</code> <code>[</code> <code>U</code> <code>]</code> to <code>C</code> <code>[</code> <code>T</code> <code>]</code> requires in addition that type <code>U</code> conform to type <code>T</code> (through the recursive application of the same rules).
|
'''Polymorphic assignment''' supports this second role. In an assignment <code>x := y</code>, the types of <code>x</code> and <code>y</code> do not have, with inheritance, to be identical; the rule is that the type of <code>y</code> must simply '''conform''' to the type of <code>x</code>. A class <code>D</code> conforms to a class <code>A</code> if and only if it is a descendant of <code>A</code> (which includes the case in which <code>A</code> and <code>D</code> are the same class); if these classes are generic, conformance of <code>D</code> <code>[</code> <code>U</code> <code>]</code> to <code>C</code> <code>[</code> <code>T</code> <code>]</code> requires in addition that type <code>U</code> conform to type <code>T</code> (through the recursive application of the same rules).
|
||||||
|
|
||||||
{{note|In addition, it will be shown in the discussion of tuples ([[10 Other Mechanisms|"Tuple types"]]), that <code> TUPLE [X] </code> conforms to <code> TUPLE </code>, <code> TUPLE [X, Y] </code> to <code> TUPLE [X] </code> and so on. }}
|
{{note|In addition, it will be shown in the discussion of tuples ([[10 Other Mechanisms#Tuple_types|"Tuple types"]]), that <code>TUPLE [X]</code> conforms to <code>TUPLE</code>, <code>TUPLE [X, Y]</code> to <code>TUPLE [X]</code> and so on. }}
|
||||||
|
|
||||||
So with the inheritance structure that we have seen, the declarations
|
So with the inheritance structure that we have seen, the declarations
|
||||||
<code>
|
<code>
|
||||||
@@ -100,7 +113,7 @@ For polymorphism to respect the reliability requirements of Eiffel, it must be c
|
|||||||
|
|
||||||
The second case listed in the rule is a call such as <code>target.routine(..., y, ...)</code> where the routine declaration is of the form <code>routine (..., x: SOME_TYPE)</code>. The relationship between <code>y</code>, the actual argument in the call, and the corresponding formal argument <code>x</code>, is exactly the same as in an assignment <code>x := y</code>: not just the type rule, as expressed by Type Conformance (the type of <code>y</code> must conform to <code>SOME_TYPE</code>), but also the actual run-time effect which, as for assignments, will be either a reference attachment or, for expanded types, a copy.
|
The second case listed in the rule is a call such as <code>target.routine(..., y, ...)</code> where the routine declaration is of the form <code>routine (..., x: SOME_TYPE)</code>. The relationship between <code>y</code>, the actual argument in the call, and the corresponding formal argument <code>x</code>, is exactly the same as in an assignment <code>x := y</code>: not just the type rule, as expressed by Type Conformance (the type of <code>y</code> must conform to <code>SOME_TYPE</code>), but also the actual run-time effect which, as for assignments, will be either a reference attachment or, for expanded types, a copy.
|
||||||
|
|
||||||
The ability to accept the assignment <code> x := Void </code> for <code> x </code> of any reference type ( [[6 The Dynamic Structure: Execution Model|"Basic operations", page 28]] ) is a consequence of the Type Conformance rule, since <code> Void </code> is of type <code> NONE </code> which by construction ( [[5 The Static Picture: System Organization|"The global inheritance structure", page 15]] ) conforms to all types.
|
The ability to accept the assignment <code>x := Void</code> for <code>x</code> of any reference type (see [[6 The Dynamic Structure: Execution Model#Basic_operations|"Basic operations"]] ) is a consequence of the Type Conformance rule, since <code>Void</code> is of type <code>NONE</code> which by construction ([[5 The Static Picture: System Organization#The_global_inheritance_structure|"The global inheritance structure"]] ) conforms to all types.
|
||||||
|
|
||||||
Polymorphism also yields a more precise definition of "instance". A '''direct instance''' of a type <code>A</code> is an object created from the exact pattern defined by the declaration of <code>A</code> 's base class, with one field for each of the class attributes; you will obtain it through a creation instruction of the form <code>create x</code> ..., for <code>x</code> of type <code>A</code>, or by cloning an existing direct instance. An '''instance''' of <code>A</code> is a direct instance of any type conforming to <code>A</code>: <code>A</code> itself, but also any type based on descendant classes. So an instance of <code>SAVINGS_ACCOUNT</code> is also an instance, although not a direct instance, of <code>ACCOUNT</code>.
|
Polymorphism also yields a more precise definition of "instance". A '''direct instance''' of a type <code>A</code> is an object created from the exact pattern defined by the declaration of <code>A</code> 's base class, with one field for each of the class attributes; you will obtain it through a creation instruction of the form <code>create x</code> ..., for <code>x</code> of type <code>A</code>, or by cloning an existing direct instance. An '''instance''' of <code>A</code> is a direct instance of any type conforming to <code>A</code>: <code>A</code> itself, but also any type based on descendant classes. So an instance of <code>SAVINGS_ACCOUNT</code> is also an instance, although not a direct instance, of <code>ACCOUNT</code>.
|
||||||
|
|
||||||
@@ -110,10 +123,12 @@ accounts: LIST [ACCOUNT]
|
|||||||
</code>
|
</code>
|
||||||
|
|
||||||
the procedure call <code>accounts.extend</code> ( <code>acc</code>), because it uses a procedure <code>extend</code>which in this case expects an argument of any type conforming to <code>ACCOUNT</code>, will be valid not only if <code>acc</code> is of type <code>ACCOUNT</code> but also if it is of a descendant type such as <code>SAVINGS_ACCOUNT</code>. Successive calls of this kind make it possible to construct a data structure that, at run-time, might contain objects of several types, all conforming to <code>ACCOUNT</code>:
|
the procedure call <code>accounts.extend</code> ( <code>acc</code>), because it uses a procedure <code>extend</code>which in this case expects an argument of any type conforming to <code>ACCOUNT</code>, will be valid not only if <code>acc</code> is of type <code>ACCOUNT</code> but also if it is of a descendant type such as <code>SAVINGS_ACCOUNT</code>. Successive calls of this kind make it possible to construct a data structure that, at run-time, might contain objects of several types, all conforming to <code>ACCOUNT</code>:
|
||||||
|
|
||||||
[[Image:tutorial-10]] [[Image:tutorial-11]]
|
[[Image:tutorial-10]] [[Image:tutorial-11]]
|
||||||
|
|
||||||
Such polymorphic data structures combine the flexibility and safety of genericity and inheritance. You can make them more or less general by choosing for the actual generic parameter, here <code>ACCOUNT</code>, a type higher or lower in the inheritance hierarchy. Static typing is again essential here, prohibiting for example a mistaken insertion of the form <code>accounts.extend (dep)</code> where <code>dep</code>is of type <code>DEPOSIT</code>, which does not conform to <code>ACCOUNT</code>.
|
Such polymorphic data structures combine the flexibility and safety of genericity and inheritance. You can make them more or less general by choosing for the actual generic parameter, here <code>ACCOUNT</code>, a type higher or lower in the inheritance hierarchy. Static typing is again essential here, prohibiting for example a mistaken insertion of the form <code>accounts.extend (dep)</code> where <code>dep</code>is of type <code>DEPOSIT</code>, which does not conform to <code>ACCOUNT</code>.
|
||||||
|
|
||||||
At the higher (most abstract) end of the spectrum, you can produce an unrestrictedly polymorphic data structure <code> general_list: LIST [ANY] </code> which makes the call <code> general_list.extend (x) </code> valid for any <code> x </code>. The price to pay is that retrieving an element from such a structure will yield an object on which the only known applicable operations are the most general ones, valid for all types: assignment, copy, clone, equality comparison and others from <code> ANY </code>. Assignment attempt, studied below, will make it possible to apply more specific operations after checking dynamically that a retrieved object is of the appropriate type.
|
At the higher (most abstract) end of the spectrum, you can produce an unrestrictedly polymorphic data structure <code>general_list: LIST [ANY]</code> which makes the call <code>general_list.extend (x)</code> valid for any <code>x</code>. The price to pay is that retrieving an element from such a structure will yield an object on which the only known applicable operations are the most general ones, valid for all types: assignment, copy, twin, equality comparison and others from <code>ANY</code>. Assignment attempt, studied below, will make it possible to apply more specific operations after checking dynamically that a retrieved object is of the appropriate type.
|
||||||
|
|
||||||
==Dynamic binding==
|
==Dynamic binding==
|
||||||
|
|
||||||
@@ -139,7 +154,18 @@ acc.deposit (...)
|
|||||||
|
|
||||||
to have the appropriate variant of the <code>deposit</code> operation triggered for each element.
|
to have the appropriate variant of the <code>deposit</code> operation triggered for each element.
|
||||||
|
|
||||||
The benefit of such techniques appears clearly if we compare them with the traditional way to address such needs: using multi-branch discriminating instructions of the form <code> if "Account is a savings account " then ... elseif "It is a money market account" then ... </code> and so on, or the corresponding <code> case ... of ..., switch </code> or <code> inspect </code> instructions. Apart from their heaviness and complexity, such solutions cause many components of a software system to rely on the knowledge of the exact set of variants available for a certain notion, such as bank account. Then any addition, change or removal of variants can cause a ripple of changes throughout the architecture. This is one of the majors obstacles to extendibility and reusability in traditional approaches. In contrast, using the combination of inheritance, redefinition, polymorphism and dynamic binding makes it possible to have a '''point of single choice''' -- a unique location in the system which knows the exhaustive list of variants. Every client then manipulates entities of the most general type, <code> ACCOUNT </code>, through dynamically bound calls of the form <code> acc.some_account_feature (...) </code>.
|
The benefit of such techniques appears clearly if we compare them with the traditional way to address such needs: using multi-branch discriminating instructions of the form
|
||||||
|
<code>
|
||||||
|
if "Account is a savings account " then
|
||||||
|
...
|
||||||
|
elseif "It is a money market account" then
|
||||||
|
...
|
||||||
|
elseif ...
|
||||||
|
</code>
|
||||||
|
and so on, or the corresponding <code>case ... of ..., switch</code> or <code>inspect</code> instructions. Apart from their heaviness and complexity, such solutions cause many components of a software system to rely on the knowledge of the exact set of variants available for a certain notion, such as bank account. Then any addition, change or removal of variants can cause a ripple of changes throughout the architecture. This is one of the majors obstacles to extendibility and reusability in traditional approaches. In contrast, using the combination of inheritance, redefinition, polymorphism and dynamic binding makes it possible to have a '''point of single choice''' -- a unique location in the system which knows the exhaustive list of variants. Every client then manipulates entities of the most general type, <code>ACCOUNT</code>, through dynamically bound calls of the form
|
||||||
|
<code>
|
||||||
|
acc.some_account_feature (...)
|
||||||
|
</code>
|
||||||
|
|
||||||
These observations make dynamic binding appear for what it is: not an implementation mechanism, but an '''architectural technique''' that plays a key role (along with information hiding, which it extends, and Design by Contract, to which it is linked through the assertion redefinition rules seen below) in providing the modular system architectures of Eiffel, the basis for the method's approach to reusability and extendibility. These properties apply as early as analysis and modeling, and continue to be useful throughout the subsequent steps.
|
These observations make dynamic binding appear for what it is: not an implementation mechanism, but an '''architectural technique''' that plays a key role (along with information hiding, which it extends, and Design by Contract, to which it is linked through the assertion redefinition rules seen below) in providing the modular system architectures of Eiffel, the basis for the method's approach to reusability and extendibility. These properties apply as early as analysis and modeling, and continue to be useful throughout the subsequent steps.
|
||||||
|
|
||||||
@@ -163,7 +189,7 @@ deferred class
|
|||||||
|
|
||||||
feature -- Access
|
feature -- Access
|
||||||
|
|
||||||
count: INTEGER is
|
count: INTEGER
|
||||||
-- Number of items in list
|
-- Number of items in list
|
||||||
do
|
do
|
||||||
... See below; this feature can be effective ...
|
... See below; this feature can be effective ...
|
||||||
@@ -171,7 +197,7 @@ feature -- Access
|
|||||||
|
|
||||||
feature -- Element change
|
feature -- Element change
|
||||||
|
|
||||||
extend (x: G) is
|
extend (x: G)
|
||||||
-- Add `x' at end of list.
|
-- Add `x' at end of list.
|
||||||
require
|
require
|
||||||
space_available: not full
|
space_available: not full
|
||||||
@@ -191,7 +217,7 @@ As the example of <code> extend </code> shows, a deferred feature, although it h
|
|||||||
|
|
||||||
Deferred classes do not have to be fully deferred. They may contain some effective features along with their deferred ones. Here, for example, we may express <code>count</code> as a function:
|
Deferred classes do not have to be fully deferred. They may contain some effective features along with their deferred ones. Here, for example, we may express <code>count</code> as a function:
|
||||||
<code>
|
<code>
|
||||||
count: INTEGER is
|
count: INTEGER
|
||||||
-- Number of items in list
|
-- Number of items in list
|
||||||
do
|
do
|
||||||
from
|
from
|
||||||
@@ -207,7 +233,7 @@ count: INTEGER is
|
|||||||
|
|
||||||
This implementation relies on the loop construct described below ( <code>from</code> introduces the loop initialization) and on a set of deferred features of the class which allow traversal of a list based on moving a fictitious cursor: <code>start</code> to bring the cursor to the first element if any, <code>after</code> to find out whether all relevant elements have been seen, and <code>forth</code> (with precondition <code>not</code> <code>after</code>) to advance the cursor to the next element. Procedure <code>forth</code> itself appears as
|
This implementation relies on the loop construct described below ( <code>from</code> introduces the loop initialization) and on a set of deferred features of the class which allow traversal of a list based on moving a fictitious cursor: <code>start</code> to bring the cursor to the first element if any, <code>after</code> to find out whether all relevant elements have been seen, and <code>forth</code> (with precondition <code>not</code> <code>after</code>) to advance the cursor to the next element. Procedure <code>forth</code> itself appears as
|
||||||
<code>
|
<code>
|
||||||
forth is
|
forth
|
||||||
-- Advance cursor by one position
|
-- Advance cursor by one position
|
||||||
require
|
require
|
||||||
not_after: not after
|
not_after: not after
|
||||||
@@ -246,7 +272,7 @@ Deferred classes cover abstract notions with many possible variants. They are wi
|
|||||||
|
|
||||||
These applications make deferred classes a central tool of the Eiffel method's support for seamlessness and reversibility. The last one in particular uses deferred classes and features to model objects from an application domain, without any commitment to implementation, design, or even software (and computers). Deferred classes are the ideal tool here: they express the properties of the domain's abstractions, without any temptation of implementation bias, yet with the precision afforded by type declarations, inheritance structures (to record classifications of the domain concepts), and contracts to express the abstract properties of the objects being described.
|
These applications make deferred classes a central tool of the Eiffel method's support for seamlessness and reversibility. The last one in particular uses deferred classes and features to model objects from an application domain, without any commitment to implementation, design, or even software (and computers). Deferred classes are the ideal tool here: they express the properties of the domain's abstractions, without any temptation of implementation bias, yet with the precision afforded by type declarations, inheritance structures (to record classifications of the domain concepts), and contracts to express the abstract properties of the objects being described.
|
||||||
|
|
||||||
Rather than using a separate method and notation for analysis and design, this apprroach integrates seamlessly with the subsequent phases (assuming the decision is indeed taken to develop a software system): it suffices to refine the deferred classes progressively by introducing effective elements, either by modifying the classes themselves, or by introducing design- and implementation-oriented descendants. In the resulting system, the classes that played an important role for analysis, and are the most meaningful for customers, will remain important; as we have seen ( [[3 The Software Process in Eiffel|"Seamlessness and reversibility", page 9]] ) this direct mapping property is a great help for extendibility.
|
Rather than using a separate method and notation for analysis and design, this approach integrates seamlessly with the subsequent phases (assuming the decision is indeed taken to develop a software system): it suffices to refine the deferred classes progressively by introducing effective elements, either by modifying the classes themselves, or by introducing design- and implementation-oriented descendants. In the resulting system, the classes that played an important role for analysis, and are the most meaningful for customers, will remain important; as we have seen ( [[3 The Software Process in Eiffel#Seamlessness_and_reversibility|"Seamlessness and reversibility"]] ) this direct mapping property is a great help for extendibility.
|
||||||
|
|
||||||
The following sketch (from the book [http://eiffel.com/doc/oosc/ Object-Oriented Software Construction] ) illustrates these ideas on the example of scheduling the programs of a TV station. This is pure modeling of an application domain; no computers or software are involved yet. The class describes the notion of program segment.
|
The following sketch (from the book [http://eiffel.com/doc/oosc/ Object-Oriented Software Construction] ) illustrates these ideas on the example of scheduling the programs of a TV station. This is pure modeling of an application domain; no computers or software are involved yet. The class describes the notion of program segment.
|
||||||
|
|
||||||
@@ -260,33 +286,33 @@ deferred class
|
|||||||
|
|
||||||
feature -- Access
|
feature -- Access
|
||||||
|
|
||||||
schedule: SCHEDULE is deferred end
|
schedule: SCHEDULE deferred end
|
||||||
-- Schedule to which segment belongs
|
-- Schedule to which segment belongs
|
||||||
|
|
||||||
index: INTEGER is deferred end
|
index: INTEGER deferred end
|
||||||
-- Position of segment in its schedule
|
-- Position of segment in its schedule
|
||||||
|
|
||||||
starting_time, ending_time: INTEGER is deferred end
|
starting_time, ending_time: INTEGER deferred end
|
||||||
-- Beginning and end of scheduled air time
|
-- Beginning and end of scheduled air time
|
||||||
|
|
||||||
next: SEGMENT is deferred end
|
next: SEGMENT deferred end
|
||||||
-- Segment to be played next, if any
|
-- Segment to be played next, if any
|
||||||
|
|
||||||
sponsor: COMPANY is deferred end
|
sponsor: COMPANY deferred end
|
||||||
-- Segment's principal sponsor
|
-- Segment's principal sponsor
|
||||||
|
|
||||||
rating: INTEGER is deferred end
|
rating: INTEGER deferred end
|
||||||
-- Segment's rating (for children's viewing etc.)
|
-- Segment's rating (for children's viewing etc.)
|
||||||
|
|
||||||
Minimum_duration: INTEGER is 30
|
Minimum_duration: INTEGER = 30
|
||||||
-- Minimum length of segments, in seconds
|
-- Minimum length of segments, in seconds
|
||||||
|
|
||||||
Maximum_interval: INTEGER is 2
|
Maximum_interval: INTEGER = 2
|
||||||
-- Maximum time (seconds) between successive segments
|
-- Maximum time (seconds) between successive segments
|
||||||
|
|
||||||
feature -- Element change
|
feature -- Element change
|
||||||
|
|
||||||
set_sponsor (s: SPONSOR) is
|
set_sponsor (s: SPONSOR)
|
||||||
require
|
require
|
||||||
not_void: s /= Void
|
not_void: s /= Void
|
||||||
deferred
|
deferred
|
||||||
@@ -319,7 +345,7 @@ Some deferred classes describe a structural property, useful to the description
|
|||||||
|
|
||||||
For such classes it is again essential to permit effective features in a deferred class, and to include assertions. For example class <code>COMPARABLE</code> declares <code>infix "<"</code> as deferred, and expresses <code>>, >=</code> and <code><</code> effectively in terms of it.
|
For such classes it is again essential to permit effective features in a deferred class, and to include assertions. For example class <code>COMPARABLE</code> declares <code>infix "<"</code> as deferred, and expresses <code>>, >=</code> and <code><</code> effectively in terms of it.
|
||||||
|
|
||||||
{{note|The type <code> like Current </code> will be explained in [[9 Inheritance|"Covariance and anchored declarations", page 79]] ; you may understand it, in the following class, as equivalent to <code> COMPARABLE </code>. }}
|
{{note|The type <code>like Current</code> will be explained in [[9 Inheritance#Covariance_and_anchored_declarations|"Covariance and anchored declarations"]] ; you may understand it, in the following class, as equivalent to <code>COMPARABLE</code>. }}
|
||||||
<code>
|
<code>
|
||||||
indexing
|
indexing
|
||||||
description: "Objects that can be compared according to a total preorder relation"
|
description: "Objects that can be compared according to a total preorder relation"
|
||||||
@@ -329,7 +355,7 @@ deferred class
|
|||||||
|
|
||||||
feature -- Comparison
|
feature -- Comparison
|
||||||
|
|
||||||
infix "<" (other: like Current): BOOLEAN is
|
infix "<" (other: like Current): BOOLEAN
|
||||||
-- Is current object less than `other'?
|
-- Is current object less than `other'?
|
||||||
require
|
require
|
||||||
other_exists: other /= Void
|
other_exists: other /= Void
|
||||||
@@ -338,7 +364,7 @@ feature -- Comparison
|
|||||||
asymmetric: Result implies not (other < Current)
|
asymmetric: Result implies not (other < Current)
|
||||||
end
|
end
|
||||||
|
|
||||||
infix "<=" (other: like Current): BOOLEAN is
|
infix "<=" (other: like Current): BOOLEAN
|
||||||
-- Is current object less than or equal to `other'?
|
-- Is current object less than or equal to `other'?
|
||||||
require
|
require
|
||||||
other_exists: other /= Void
|
other_exists: other /= Void
|
||||||
@@ -419,7 +445,9 @@ The invariant of a class is automatically considered to include -- in the sense
|
|||||||
Next we consider routine preconditions and postconditions. The rule here will follow from an examination of what contracts mean in the presence of polymorphism and dynamic binding.
|
Next we consider routine preconditions and postconditions. The rule here will follow from an examination of what contracts mean in the presence of polymorphism and dynamic binding.
|
||||||
|
|
||||||
Consider a parent <code>A</code> and a proper descendant <code>B</code> (a direct heir on the following figure), which redefines a routine <code>r</code> inherited from <code>A</code>.
|
Consider a parent <code>A</code> and a proper descendant <code>B</code> (a direct heir on the following figure), which redefines a routine <code>r</code> inherited from <code>A</code>.
|
||||||
|
|
||||||
[[Image:tutorial-12]]
|
[[Image:tutorial-12]]
|
||||||
|
|
||||||
As a result of dynamic binding, a call <code>a1</code> . <code>r</code> from a client <code>C</code> may be serviced not by <code>A</code> 's version of <code>r</code> but by <code>B</code> 's version if <code>a1</code>, although declared of type <code>A</code>, becomes at run time attached to an instance of <code>B</code>. This shows the combination of inheritance, redefinition, polymorphism and dynamic binding as providing a form of subcontracting; <code>A</code> subcontracts certain calls to <code>B</code>.
|
As a result of dynamic binding, a call <code>a1</code> . <code>r</code> from a client <code>C</code> may be serviced not by <code>A</code> 's version of <code>r</code> but by <code>B</code> 's version if <code>a1</code>, although declared of type <code>A</code>, becomes at run time attached to an instance of <code>B</code>. This shows the combination of inheritance, redefinition, polymorphism and dynamic binding as providing a form of subcontracting; <code>A</code> subcontracts certain calls to <code>B</code>.
|
||||||
|
|
||||||
The problem is to keep subcontractors honest. Assuming preconditions and postconditions as shown on the last figure, a call in <code>C</code> of the form
|
The problem is to keep subcontractors honest. Assuming preconditions and postconditions as shown on the last figure, a call in <code>C</code> of the form
|
||||||
@@ -437,11 +465,11 @@ Because it is impossible to check simply that an assertion is weaker or stronger
|
|||||||
|
|
||||||
The last case -- retaining the original -- is frequent but by no means universal.
|
The last case -- retaining the original -- is frequent but by no means universal.
|
||||||
|
|
||||||
The Assertion Redeclaration rule applies to '''redeclarations'''. This terms covers not just redefinition but also effecting (the implementation, by a class, of a feature that it inherits deferred). The rules -- not just for assertions but also, as reviewed below, for typing -- are indeed the same in both cases. Without the Assertion Redeclaration rule, assertions on deferred features, such as those on <code> extend </code>, <code> count </code> and <code> forth </code> in [[9 Inheritance|"Deferred features and classes", page 57]] , would be almost useless -- wishful thinking; the rule makes them binding on all effectings in descendants.
|
The Assertion Redeclaration rule applies to '''redeclarations'''. This terms covers not just redefinition but also effecting (the implementation, by a class, of a feature that it inherits deferred). The rules -- not just for assertions but also, as reviewed below, for typing -- are indeed the same in both cases. Without the Assertion Redeclaration rule, assertions on deferred features, such as those on <code>extend</code>, <code>count</code> and <code>forth</code> in [[9 Inheritance#Deferred_features_and_classes|"Deferred features and classes"]] , would be almost useless -- wishful thinking; the rule makes them binding on all effectings in descendants.
|
||||||
|
|
||||||
From the Assertion Redeclaration rule follows an interesting technique: '''abstract preconditions'''. What needs to be weakened for a precondition (or strengthened for a postcondition) is not the assertion's concrete semantics but its abstract specification as seen by the client. A descendant can change the implementation of that specification as it pleases, even to the effect of strengthening the concrete precondition, as long as the abstract form is kept or weakened. The precondition of procedure <code> extend </code> in the deferred class <code> LIST </code> provided an example. We wrote the routine (page [[9 Inheritance|58]] ) as
|
From the Assertion Redeclaration rule follows an interesting technique: '''abstract preconditions'''. What needs to be weakened for a precondition (or strengthened for a postcondition) is not the assertion's concrete semantics but its abstract specification as seen by the client. A descendant can change the implementation of that specification as it pleases, even to the effect of strengthening the concrete precondition, as long as the abstract form is kept or weakened. The precondition of procedure <code>extend</code> in the deferred class <code>LIST</code> provided an example. We wrote the routine (in [[9 Inheritance#Deferred_features_and_classes|"Deferred features and classes"]] ) as
|
||||||
<code>
|
<code>
|
||||||
extend (x: G) is
|
extend (x: G)
|
||||||
-- Add `x' at end of list.
|
-- Add `x' at end of list.
|
||||||
require
|
require
|
||||||
space_available: not full
|
space_available: not full
|
||||||
@@ -453,7 +481,7 @@ extend (x: G) is
|
|||||||
|
|
||||||
The precondition expresses that it is only possible to add an item to a list if the representation is not full. We may well consider -- in line with the Eiffel principle that whenever possible structures should be of unbounded capacity -- that <code>LIST</code> should by default make <code>full</code> always return false:
|
The precondition expresses that it is only possible to add an item to a list if the representation is not full. We may well consider -- in line with the Eiffel principle that whenever possible structures should be of unbounded capacity -- that <code>LIST</code> should by default make <code>full</code> always return false:
|
||||||
<code>
|
<code>
|
||||||
full: BOOLEAN is
|
full: BOOLEAN
|
||||||
-- Is representation full?
|
-- Is representation full?
|
||||||
-- (Default: no)
|
-- (Default: no)
|
||||||
do
|
do
|
||||||
@@ -463,7 +491,7 @@ full: BOOLEAN is
|
|||||||
|
|
||||||
Now a class <code>BOUNDED_LIST</code> that implements bounded-size lists (inheriting, like the earlier <code>ARRAYED_LIST</code>, from both <code>LIST</code> and <code>ARRAY</code>) may redefine <code>full</code>:
|
Now a class <code>BOUNDED_LIST</code> that implements bounded-size lists (inheriting, like the earlier <code>ARRAYED_LIST</code>, from both <code>LIST</code> and <code>ARRAY</code>) may redefine <code>full</code>:
|
||||||
<code>
|
<code>
|
||||||
full: BOOLEAN is
|
full: BOOLEAN
|
||||||
-- Is representation full?
|
-- Is representation full?
|
||||||
-- (Answer: if and only if number of items is capacity)
|
-- (Answer: if and only if number of items is capacity)
|
||||||
do
|
do
|
||||||
@@ -483,7 +511,7 @@ It is not an error to inherit two deferred features from different parents under
|
|||||||
|
|
||||||
More generally, it is permitted to have any number of deferred features and at most one effective feature that share the same name: the effective version, if present will effect all the others.
|
More generally, it is permitted to have any number of deferred features and at most one effective feature that share the same name: the effective version, if present will effect all the others.
|
||||||
|
|
||||||
All this is not a violation of the Final Name rule (page [[9 Inheritance|65]] ), since the name clashes prohibited by the rule involve two different features having the same final name; here the result is just one feature, resulting from the join of all the inherited versions.
|
All this is not a violation of the Final Name rule (defined in [[9 Inheritance#Multiple_inheritance_and_renaming|"Multiple inheritance and renaming"]] ), since the name clashes prohibited by the rule involve two different features having the same final name; here the result is just one feature, resulting from the join of all the inherited versions.
|
||||||
|
|
||||||
Sometimes we may want to join ''effective'' features inherited from different parents, assuming again the features have compatible signatures. One way is to redefine them all into a new version; then they again become one feature, with no name clash in the sense of the Final Name rule. But in other cases we may simply want one of the inherited implementations to take over the others. The solution is to revert to the preceding case by '''uneffecting''' the other features; uneffecting an inherited effective feature makes it deferred (this is the reverse of effecting, which turns an inherited deferred feature into an effective one). The syntax uses the <code>undefine</code> subclause:
|
Sometimes we may want to join ''effective'' features inherited from different parents, assuming again the features have compatible signatures. One way is to redefine them all into a new version; then they again become one feature, with no name clash in the sense of the Final Name rule. But in other cases we may simply want one of the inherited implementations to take over the others. The solution is to revert to the preceding case by '''uneffecting''' the other features; uneffecting an inherited effective feature makes it deferred (this is the reverse of effecting, which turns an inherited deferred feature into an effective one). The syntax uses the <code>undefine</code> subclause:
|
||||||
<code>
|
<code>
|
||||||
@@ -546,6 +574,8 @@ class ARRAYED_LIST [G] inherit
|
|||||||
{ANY} capacity
|
{ANY} capacity
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
...
|
...
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
@@ -559,14 +589,16 @@ This is part of the power of the object-oriented form of reuse, but can create a
|
|||||||
|
|
||||||
These observations suggest ways to produce, from a class text, a version that is equivalent feature-wise and assertion-wise, but has no inheritance dependency. This is called the '''Flat Form''' of the class. It is a class text that has no inheritance clause and includes all the features of the class, immediate (declared in the class itself) as well as inherited. For the inherited features, the flat form must of course take account of all the feature adaptation mechanisms: renaming (each feature must appear under its final name), redefinition, effecting, uneffecting and export status change. For redeclared features, <code>else require</code> clauses are or-ed with the precursors' preconditions, and <code>then ensure</code> clauses are and-ed with precursors' postconditions. For invariants, all the ancestors' clauses are concatenated. As a result, the flat form yields a view of the class, its features and its assertions that conforms exactly to the view offered to clients and (except for polymorphic uses) heirs.
|
These observations suggest ways to produce, from a class text, a version that is equivalent feature-wise and assertion-wise, but has no inheritance dependency. This is called the '''Flat Form''' of the class. It is a class text that has no inheritance clause and includes all the features of the class, immediate (declared in the class itself) as well as inherited. For the inherited features, the flat form must of course take account of all the feature adaptation mechanisms: renaming (each feature must appear under its final name), redefinition, effecting, uneffecting and export status change. For redeclared features, <code>else require</code> clauses are or-ed with the precursors' preconditions, and <code>then ensure</code> clauses are and-ed with precursors' postconditions. For invariants, all the ancestors' clauses are concatenated. As a result, the flat form yields a view of the class, its features and its assertions that conforms exactly to the view offered to clients and (except for polymorphic uses) heirs.
|
||||||
|
|
||||||
As with the Contract Form ( [[8 Design by Contract (tm), Assertions and Exceptions|"The contract form of a class", page 44]] ), producing the Flat Form is the responsibility of tools in the development environment. In EiffelStudio, you will just click the "Flat" icon.
|
As with the Contract Form ( [[8 Design by Contract (tm), Assertions and Exceptions#The_contract_form_of_a_class|"The contract form of a class"]] ), producing the Flat Form is the responsibility of tools in the development environment. In EiffelStudio, you will just click the "Flat" icon.
|
||||||
|
|
||||||
The Contract Form of the Flat Form of a class is known as its '''Flat-Contract Form'''. It gives the complete interface specification, documenting all exported features and assertions -- immediate or inherited -- and hiding implementation aspects. It is the appropriate documentation for a class.
|
The Contract Form of the Flat Form of a class is known as its '''Flat-Contract Form'''. It gives the complete interface specification, documenting all exported features and assertions -- immediate or inherited -- and hiding implementation aspects. It is the appropriate documentation for a class.
|
||||||
|
|
||||||
==Repeated inheritance and selection==
|
==Repeated inheritance and selection==
|
||||||
|
|
||||||
An inheritance mechanism, following from multiple inheritance, remains to be seen. Through multiple inheritance, a class can be a proper descendant of another through more than one path. This is called repeated inheritance and can be indirect, as in the following figure, or even direct, when a class <code>D</code> lists a class <code>A</code> twice in its <code>inherit</code> clause.
|
An inheritance mechanism, following from multiple inheritance, remains to be seen. Through multiple inheritance, a class can be a proper descendant of another through more than one path. This is called repeated inheritance and can be indirect, as in the following figure, or even direct, when a class <code>D</code> lists a class <code>A</code> twice in its <code>inherit</code> clause.
|
||||||
|
|
||||||
[[Image:tutorial-13]]
|
[[Image:tutorial-13]]
|
||||||
|
|
||||||
The figure's particular example is in fact often used by introductory presentations of multiple inheritance, which is a pedagogical mistake: simple multiple inheritance examples (such as <code>INTEGER</code> inheriting from <code>NUMERIC</code> and <code>COMPARABLE</code>, or <code>COMPANY_PLANE</code> from <code>ASSET</code> and <code>PLANE</code>) should involve the combination of '''separate abstractions'''. Repeated inheritance is an advanced technique; although invaluable, it does not arise in elementary uses and requires a little more care.
|
The figure's particular example is in fact often used by introductory presentations of multiple inheritance, which is a pedagogical mistake: simple multiple inheritance examples (such as <code>INTEGER</code> inheriting from <code>NUMERIC</code> and <code>COMPARABLE</code>, or <code>COMPANY_PLANE</code> from <code>ASSET</code> and <code>PLANE</code>) should involve the combination of '''separate abstractions'''. Repeated inheritance is an advanced technique; although invaluable, it does not arise in elementary uses and requires a little more care.
|
||||||
|
|
||||||
In fact there is only one non-trivial issue in repeated inheritance: what does a feature of the repeated ancestor, such as <code>change_address</code> and <code>computer_account</code>, mean for the repeated descendant, here <code>TEACHING_ASSISTANT</code>? (The example features chosen involve a routine and an attribute; the basic rules will be the same.)
|
In fact there is only one non-trivial issue in repeated inheritance: what does a feature of the repeated ancestor, such as <code>change_address</code> and <code>computer_account</code>, mean for the repeated descendant, here <code>TEACHING_ASSISTANT</code>? (The example features chosen involve a routine and an attribute; the basic rules will be the same.)
|
||||||
@@ -617,16 +649,14 @@ ta: TEACHING_ASSISTANT
|
|||||||
since the valid calls are of the form <code>ta.faculty_account</code> and <code>ta.student_account</code>, neither of them ambiguous; the call <code>ta.computer_account</code> would be invalid, since after the renamings class <code>TEACHING_ASSISTANT</code> has no feature of that name. The <code>select</code> only applies to a call
|
since the valid calls are of the form <code>ta.faculty_account</code> and <code>ta.student_account</code>, neither of them ambiguous; the call <code>ta.computer_account</code> would be invalid, since after the renamings class <code>TEACHING_ASSISTANT</code> has no feature of that name. The <code>select</code> only applies to a call
|
||||||
<code>
|
<code>
|
||||||
up.computer_account
|
up.computer_account
|
||||||
|
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
with <code>up</code> of type <code>UNIVERSITY_PERSON</code>, dynamically attached to an instance of <code>TEACHING_ASSISTANT</code>; then the <code>select</code> resolves the ambiguity by causing the call to use the version from <code>TEACHER</code>.
|
with <code>up</code> of type <code>UNIVERSITY_PERSON</code>, dynamically attached to an instance of <code>TEACHING_ASSISTANT</code>; then the <code>select</code> resolves the ambiguity by causing the call to use the version from <code>TEACHER</code>.
|
||||||
|
|
||||||
So if you traverse a list <code>computer_users: LIST [UNIVERSITY_PERSON]</code> to print some information about the computer account of each list element, the account used for a teaching assistant is the faculty account, not the student account.
|
So if you traverse a list <code>computer_users: LIST [UNIVERSITY_PERSON]</code> to print some information about the computer account of each list element, the account used for a teaching assistant is the faculty account, not the student account.
|
||||||
|
|
||||||
You may, if desired, redefine <code>faculty_account</code> in class <code>TEACHING_ASSISTANT</code>, using <code>student_account</code> if necessary, to take into consideration the existence of another account. But in all cases we need a precise disambiguation of what <code>computer_account</code> means for a <code>TEACHING_ASSISTANT</code> object known only through a <code>UNIVERSITY_PERSON</code> entity.
|
You may, if desired, redefine <code>faculty_account</code> in class <code>TEACHING_ASSISTANT</code>, using <code>student_account</code> if necessary, to take into consideration the existence of another account. But in all cases we need a precise disambiguation of what <code>computer_account</code> means for a <code>TEACHING_ASSISTANT</code> object known only through a <code>UNIVERSITY_PERSON</code> entity.
|
||||||
|
|
||||||
The <code> select </code> is only needed in case of replication. If the Repeated Inheritance rule would imply sharing, as with change_address, and one or both of the shared versions has been redeclared, the Final Name rule makes the class invalid, since it now has '''two different features''' with the same name. (This is only a problem if both versions are effective; if one or both are deferred there is no conflict but a mere case of feature joining as explained in [[9 Inheritance|"Join and uneffecting", page 70]] .) The two possible solutions follow from the previous discussions:
|
The <code>select</code> is only needed in case of replication. If the Repeated Inheritance rule would imply sharing, as with change_address, and one or both of the shared versions has been redeclared, the Final Name rule makes the class invalid, since it now has '''two different features''' with the same name. (This is only a problem if both versions are effective; if one or both are deferred there is no conflict but a mere case of feature joining as explained in [[9 Inheritance#Join_and_uneffecting|"Join and uneffecting"]] .) The two possible solutions follow from the previous discussions:
|
||||||
|
|
||||||
If you do want sharing, one of the two versions must take precedence over the other. It suffices to '''undefine''' the other, and everything gets back to order. Alternatively, you can redefine both into a new version, which takes precedence over both.
|
If you do want sharing, one of the two versions must take precedence over the other. It suffices to '''undefine''' the other, and everything gets back to order. Alternatively, you can redefine both into a new version, which takes precedence over both.
|
||||||
|
|
||||||
@@ -634,18 +664,16 @@ If you want to keep both versions, switch from sharing to replication: rename on
|
|||||||
|
|
||||||
==Constrained genericity==
|
==Constrained genericity==
|
||||||
|
|
||||||
Eiffel's inheritance mechanism has an important application to extending the flexibility of the '''genericity''' mechanism. In a class <code> SOME_CONTAINER [G] </code>, as noted (section [[7 Genericity and Arrays|7]] ), the only operations available on entities of type <code> G </code>, the formal generic parameter, are those applicable to entities of all types. A generic class may, however, need to assume more about the generic parameter, as with a class <code> SORTABLE_ARRAY [G ...] </code> which will have a procedure <code> sort </code> that needs, at some stage, to perform tests of the form
|
Eiffel's inheritance mechanism has an important application to extending the flexibility of the '''genericity''' mechanism. In a class <code>SOME_CONTAINER [G]</code>, as noted in [[7 Genericity and Arrays|"Genericity and Arrays"]] ), the only operations available on entities of type <code>G</code>, the formal generic parameter, are those applicable to entities of all types. A generic class may, however, need to assume more about the generic parameter, as with a class <code>SORTABLE_ARRAY [G ...]</code> which will have a procedure <code>sort</code> that needs, at some stage, to perform tests of the form
|
||||||
<code>
|
<code>
|
||||||
if item (i) < item (j) then ...
|
if item (i) < item (j) then ...
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
where <code>item (i)</code> and <code>item (j)</code> are of type <code>G</code>. But this requires the availability of a feature <code>infix "<"</code> in all types that may serve as actual generic parameters corresponding to <code>G</code>. Using the type <code>SORTABLE_ARRAY [INTEGER]</code> should be permitted, because <code>INTEGER</code> has such a feature; but not <code>SORTABLE_ARRAY [COMPLEX]</code> if there is no total order relation on <code>COMPLEX</code>.
|
where <code>item (i)</code> and <code>item (j)</code> are of type <code>G</code>. But this requires the availability of a feature <code>infix "<"</code> in all types that may serve as actual generic parameters corresponding to <code>G</code>. Using the type <code>SORTABLE_ARRAY [INTEGER]</code> should be permitted, because <code>INTEGER</code> has such a feature; but not <code>SORTABLE_ARRAY [COMPLEX]</code> if there is no total order relation on <code>COMPLEX</code>.
|
||||||
|
|
||||||
To cover such cases, declare the class as
|
To cover such cases, declare the class as
|
||||||
<code>
|
<code>
|
||||||
class SORTABLE_ARRAY [G -> COMPARABLE]
|
class SORTABLE_ARRAY [G -> COMPARABLE]
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
making it '''constrained generic'''. The symbol <code>-></code> recalls the arrow of inheritance diagrams; what follows it is a type, known as the generic constraint. Such a declaration means that:
|
making it '''constrained generic'''. The symbol <code>-></code> recalls the arrow of inheritance diagrams; what follows it is a type, known as the generic constraint. Such a declaration means that:
|
||||||
|
|
||||||
Within the class, you may apply the features of the generic constraint -- here the features of <code>COMPARABLE</code>: <code>infix "<"</code>, <code>infix "<"</code> etc. -- to expressions of type <code>G</code>.
|
Within the class, you may apply the features of the generic constraint -- here the features of <code>COMPARABLE</code>: <code>infix "<"</code>, <code>infix "<"</code> etc. -- to expressions of type <code>G</code>.
|
||||||
@@ -658,9 +686,9 @@ Unconstrained genericity, as in <code> C [G] </code>, is defined as equivalent t
|
|||||||
|
|
||||||
==Assignment attempt==
|
==Assignment attempt==
|
||||||
|
|
||||||
The Type Conformance rule ( [[9 Inheritance|"Polymorphism", page 53]] ) ensures type safety by requiring all assignments to be from a more specific source to a more general target.
|
The Type Conformance rule ( [[9 Inheritance#Polymorphism|"Polymorphism"]] ) ensures type safety by requiring all assignments to be from a more specific source to a more general target.
|
||||||
|
|
||||||
Sometimes you can't be sure of the source object's type. This happens for example when the object comes from the outside -- a file, a database, a network. The persistence storage mechanism( [[6 The Dynamic Structure: Execution Model|"Deep operations and persistence", page 30]] ) includes, along with the procedure <code> store </code> seen there, the reverse operation, a function <code> retrieved </code> which yields an object structure retrieved from a file or network, to which it was sent using <code> store </code>. But <code> retrieved </code> as declared in the corresponding class <code> STORABLE </code> of EiffelBase can only return the most general type, <code> ANY </code>; it is not possible to know its exact type until execution time, since the corresponding objects are not under the control of the retrieving system, and might even have been corrupted by some external agent.
|
Sometimes you can't be sure of the source object's type. This happens for example when the object comes from the outside -- a file, a database, a network. The persistence storage mechanism( [[6 The Dynamic Structure: Execution Model#Deep_operations_and_persistence|"Deep operations and persistence"]] ) includes, along with the procedure <code>store</code> seen there, the reverse operation, a function <code>retrieved</code> which yields an object structure retrieved from a file or network, to which it was sent using <code>store</code>. But <code>retrieved</code> as declared in the corresponding class <code>STORABLE</code> of EiffelBase can only return the most general type, <code>ANY</code>; it is not possible to know its exact type until execution time, since the corresponding objects are not under the control of the retrieving system, and might even have been corrupted by some external agent.
|
||||||
|
|
||||||
In such cases you cannot trust the declared type but must check it against the type of an actual run-time object. Eiffel introduces for this purpose the '''assignment attempt''' operation, written
|
In such cases you cannot trust the declared type but must check it against the type of an actual run-time object. Eiffel introduces for this purpose the '''assignment attempt''' operation, written
|
||||||
<code>
|
<code>
|
||||||
@@ -716,7 +744,7 @@ In many cases the signature of a redeclared feature remains the same as the orig
|
|||||||
<code>
|
<code>
|
||||||
owner: HOLDER
|
owner: HOLDER
|
||||||
|
|
||||||
set_owner (h: HOLDER) is
|
set_owner (h: HOLDER)
|
||||||
-- Make `h' the account owner.
|
-- Make `h' the account owner.
|
||||||
require
|
require
|
||||||
not_void: h /= Void
|
not_void: h /= Void
|
||||||
@@ -726,7 +754,9 @@ set_owner (h: HOLDER) is
|
|||||||
</code>
|
</code>
|
||||||
|
|
||||||
We introduce an heir <code>BUSINESS_ACCOUNT</code> of <code>ACCOUNT</code> to represent special business accounts, corresponding to class <code>BUSINESS</code> inheriting from <code>HOLDER</code>:
|
We introduce an heir <code>BUSINESS_ACCOUNT</code> of <code>ACCOUNT</code> to represent special business accounts, corresponding to class <code>BUSINESS</code> inheriting from <code>HOLDER</code>:
|
||||||
|
|
||||||
[[Image:tutorial-14]]
|
[[Image:tutorial-14]]
|
||||||
|
|
||||||
Clearly, we must redefine <code>owner</code> in class <code>BUSINESS_ACCOUNT</code> to yield a result of type <code>BUSINESS</code>; the same signature redefinition must be applied to the argument of <code>set_owner</code>. This case is typical of the general scheme of signature redefinition: in a descendant, you may need to redefine both results and arguments to types conforming to the originals. This is reflected by a language rule:
|
Clearly, we must redefine <code>owner</code> in class <code>BUSINESS_ACCOUNT</code> to yield a result of type <code>BUSINESS</code>; the same signature redefinition must be applied to the argument of <code>set_owner</code>. This case is typical of the general scheme of signature redefinition: in a descendant, you may need to redefine both results and arguments to types conforming to the originals. This is reflected by a language rule:
|
||||||
|
|
||||||
{{rule|name=Covariance|text=In a feature redeclaration, both the result type if the feature is a query (attribute or function) and the type of any argument if it is a routine (procedure or function) must conform to the original type as declared in the precursor version. }}
|
{{rule|name=Covariance|text=In a feature redeclaration, both the result type if the feature is a query (attribute or function) and the type of any argument if it is a routine (procedure or function) must conform to the original type as declared in the precursor version. }}
|
||||||
@@ -735,7 +765,7 @@ The term "covariance" reflects the property that all types -- those of arguments
|
|||||||
|
|
||||||
If a feature such as <code>set_owner</code> has to be redefined for more than its signature -- to update its implementation or assertions -- the signature redefinition will be explicit. For example <code>set_owner</code> could do more for business owners than it does for ordinary owners. Then the redefinition will be of the form
|
If a feature such as <code>set_owner</code> has to be redefined for more than its signature -- to update its implementation or assertions -- the signature redefinition will be explicit. For example <code>set_owner</code> could do more for business owners than it does for ordinary owners. Then the redefinition will be of the form
|
||||||
<code>
|
<code>
|
||||||
set_owner (b: BUSINESS) is
|
set_owner (b: BUSINESS)
|
||||||
-- Make b the account owner.
|
-- Make b the account owner.
|
||||||
do
|
do
|
||||||
... New routine body ...
|
... New routine body ...
|
||||||
@@ -744,7 +774,7 @@ set_owner (b: BUSINESS) is
|
|||||||
|
|
||||||
In other cases, however, the body will be exactly the same as in the precursor. Then explicit redefinition would be tedious, implying much text duplication. The mechanism of '''anchored redeclaration''' solves this problem. The original declaration of <code>set_owner</code> in <code>ACCOUNT</code> should be of the form
|
In other cases, however, the body will be exactly the same as in the precursor. Then explicit redefinition would be tedious, implying much text duplication. The mechanism of '''anchored redeclaration''' solves this problem. The original declaration of <code>set_owner</code> in <code>ACCOUNT</code> should be of the form
|
||||||
<code>
|
<code>
|
||||||
set_owner (h: like owner) is
|
set_owner (h: like owner)
|
||||||
-- Make h the account owner.
|
-- Make h the account owner.
|
||||||
-- The rest as before:
|
-- The rest as before:
|
||||||
require
|
require
|
||||||
@@ -764,21 +794,21 @@ This means that anchored declaration are a form of of implicit covariant redecla
|
|||||||
|
|
||||||
In the example, class <code>BUSINESS_ACCOUNT</code> only needs to redefine the type of <code>owner</code> (to <code>BUSINESS</code>). It doesn't have to redefine <code>set_owner</code> except if it needs to change its implementation or assertions.
|
In the example, class <code>BUSINESS_ACCOUNT</code> only needs to redefine the type of <code>owner</code> (to <code>BUSINESS</code>). It doesn't have to redefine <code>set_owner</code> except if it needs to change its implementation or assertions.
|
||||||
|
|
||||||
It is possible to use <code> Current </code> as anchor; the declaration <code> like Current </code> denotes a type based on the current class (with the same generic parameters if any). This is in fact a common case; we saw in [[9 Inheritance|"Structural property classes", page 62]] , that it applies in class <code> COMPARABLE </code> to features such as
|
It is possible to use <code>Current</code> as anchor; the declaration <code>like Current</code> denotes a type based on the current class (with the same generic parameters if any). This is in fact a common case; we saw in [[9 Inheritance#Structural_property_classes|"Structural property classes"]] , that it applies in class <code>COMPARABLE</code> to features such as
|
||||||
<code>
|
<code>
|
||||||
infix "<" (other: like Current): BOOLEAN is ...
|
infix "<" (other: like Current): BOOLEAN ...
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
since we only want to compare two comparable elements of compatible types -- but not, for example, integer and strings, even if both types conform to <code>COMPARABLE</code>. (A "balancing rule" makes it possible, however, to mix the various arithmetic types, consistently with mathematical traditions, in arithmetic expressions such as <code>3 + 45.82</code> or boolean expressions such as <code>3 < 45.82</code>.)
|
since we only want to compare two comparable elements of compatible types -- but not, for example, integer and strings, even if both types conform to <code>COMPARABLE</code>. (A "balancing rule" makes it possible, however, to mix the various arithmetic types, consistently with mathematical traditions, in arithmetic expressions such as <code>3 + 45.82</code> or boolean expressions such as <code>3 < 45.82</code>.)
|
||||||
|
|
||||||
Similarly, class <code>ANY</code> declares procedure <code>copy</code> as
|
Similarly, class <code>ANY</code> declares procedure <code>copy</code> as
|
||||||
<code>
|
<code>
|
||||||
copy (other: like Current) is ...
|
copy (other: like Current) ...
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
with the argument anchored to the current object. Function <code> clone </code>, for its part, has signature <code> clone (other: ANY): like other </code>, with both argument and result anchored to the argument, so that for any <code> x </code> the type of <code> clone (x) </code> is the same as the type of <code> x </code>.
|
with the argument anchored to the current object.
|
||||||
|
|
||||||
A final, more application-oriented example of anchoring to <code> Current </code> is the feature <code> merge </code> posited in an earlier example (page [[6 The Dynamic Structure: Execution Model|13]] ) with the signature <code> merge (other: ACCOUNT) </code>. By using instead <code> merge (other: like Current) </code> we can ensure that in any descendant class -- <code> BUSINESS_ACCOUNT </code>, <code> SAVINGS_ACCOUNT </code>, <code> MINOR_ACCOUNT </code> ... -- an account will only be mergeable with another of a compatible type.
|
A final, more application-oriented example of anchoring to <code>Current</code> is the feature <code>merge</code> posited in an earlier example (in [[6 The Dynamic Structure: Execution Model|"The Dynamic Structure: Execution Model"]] ) with the signature <code>merge (other: ACCOUNT)</code>. By using instead <code>merge (other: like Current)</code> we can ensure that in any descendant class -- <code>BUSINESS_ACCOUNT</code>, <code>SAVINGS_ACCOUNT</code>, <code>MINOR_ACCOUNT</code> ... -- an account will only be mergeable with another of a compatible type.
|
||||||
|
|
||||||
Covariance makes static type checking more delicate; mechanisms of "system validity" and "catcalls" address the problem, discussed in detail in the book [http://eiffel.com/doc/oosc/ Object-Oriented Software Construction] (see the bibliography).
|
Covariance makes static type checking more delicate; mechanisms of "system validity" and "catcalls" address the problem, discussed in detail in the book [http://eiffel.com/doc/oosc/ Object-Oriented Software Construction] (see the bibliography).
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user