Author:halw

Date:2008-12-15T21:23:45.000000Z


git-svn-id: https://svn.eiffel.com/eiffel-org/trunk@139 abb3cda0-5349-4a8f-a601-0c33ac3a8c38
This commit is contained in:
halw
2008-12-15 21:23:45 +00:00
parent df0e7e8976
commit b8588abd09
8 changed files with 580 additions and 584 deletions

View File

@@ -7,13 +7,13 @@ This section deals with what happens when objects, that is runtime instances of
Eiffel creation procedures are features of a class which can be used to initialize instances. Classes can have more than one creation procedure available. However, each creation procedure must ensure that the class invariant holds when the procedure completes execution. In other words, the creation procedure is there to initialize a newly created instance, and the class invariant guarantees that a newly initialized instance is actually valid.
There is nothing special about creation procedures themselves, they are just ordinary procedures (although by convention their names usually begin with the word " <code> make </code>"). What makes them creation procedures is the fact that their names are listed as creation procedures in the class text.
There is nothing special about creation procedures themselves, they are just ordinary procedures (although by convention their names usually begin with the word "<code>make</code>"). What makes them creation procedures is the fact that their names are listed as creation procedures in the class text.
In Eiffel, a creation procedure can be applied to an instance at any time (not just at object creation). This is done sometimes to reinitialize existing instances.
==Constructors in .NET==
Like creation procedures in Eiffel, .NET constructors are used to initialize new instances of types. Constructors manifest themselves differently depending upon which .NET language you use. In C#, constructors always appear as a method having the same name as the class on which they are implemented. In Visual Basic .NET, they always appear as a Sub with the name <code> New </code>. Once compiled into an assembly, the metadata labels constructors as <code> . ctor </code>.
Like creation procedures in Eiffel, .NET constructors are used to initialize new instances of types. Constructors manifest themselves differently depending upon which .NET language you use. In C#, constructors always appear as a method having the same name as the class on which they are implemented. In Visual Basic .NET, they always appear as a Sub with the name <code>New</code>. Once compiled into an assembly, the metadata labels constructors as <code>. ctor</code>.
Constructors can have multiple versions by overloading. That is, each version would have a different set of argument types.
@@ -21,17 +21,17 @@ Constructors can only be applied when a new instance is created.
===Constructors as Eiffel Creation Procedures===
When types from .NET assemblies are made available to Eiffel systems, the constructors are presented as creation procedures. Just as constructors show up in the C# and VB.NET environments with names appropriate to those languages, so it is with Eiffel for .NET. Always, constructors will have feature names which begin with the word " <code> make </code>", the convention for creation procedure naming in Eiffel.
When types from .NET assemblies are made available to Eiffel systems, the constructors are presented as creation procedures. Just as constructors show up in the C# and VB.NET environments with names appropriate to those languages, so it is with Eiffel for .NET. Always, constructors will have feature names which begin with the word "<code>make</code>", the convention for creation procedure naming in Eiffel.
If there is only one version of the constructor, that version will be mapped to a single feature named <code> make </code>. However, if there are overloaded versions of the constructor, then these versions given names starting with " <code> make_from_ </code>" and then followed with the argument names from the assembly metadata separated with the conjunction " <code> _and_ </code>". Let's look at an example.
If there is only one version of the constructor, that version will be mapped to a single feature named <code>make</code>. However, if there are overloaded versions of the constructor, then these versions given names starting with "<code>make_from_</code>" and then followed with the argument names from the assembly metadata separated with the conjunction "<code>_and_</code>". Let's look at an example.
The .NET type <code> System.Drawing.Size </code> has an overloaded constructor with two versions. In the <code> System.Drawing </code> assembly metadata, these two constructor versions look like this:
The .NET type <code>System.Drawing.Size</code>has an overloaded constructor with two versions. In the <code>System.Drawing</code> assembly metadata, these two constructor versions look like this:
<code>
void .ctor(int32 width, int32 height)
void .ctor(System.Drawing.Point pt)
</code>
So the argument names for the first version are <code> width </code> and <code> height </code>. For the second version there is only one argument named <code> pt </code>. The constructor versions as presented to Eiffel programmers as creation procedures look like this:
So the argument names for the first version are <code>width</code> and <code>height</code>. For the second version there is only one argument named <code>pt</code>. The constructor versions as presented to Eiffel programmers as creation procedures look like this:
<code>
make_from_width_and_height (width: INTEGER; height: INTEGER)
make_from_pt (pt: DRAWING_POINT)
@@ -41,7 +41,7 @@ Presenting the names in this format handles the conflicts of overloading and pro
===Eiffel Creation Procedures as Constructors?===
Eiffel creation procedures do not map to constructors when Eiffel classes are compiled into assemblies. Rather, they are actually manifested as functions on a factory class in the namespace <code> Create </code> in the assembly. These functions return an initialized instance. In the section Type Organization there is more information about the organization of the types in assemblies built with Eiffel for .NET, along with an example of using types from such an assembly.
Eiffel creation procedures do not map to constructors when Eiffel classes are compiled into assemblies. Rather, they are actually manifested as functions on a factory class in the namespace <code lang=text>Create</code> in the assembly. These functions return an initialized instance. In the section Type Organization there is more information about the organization of the types in assemblies built with Eiffel for .NET, along with an example of using types from such an assembly.

View File

@@ -153,15 +153,15 @@ Properties in .NET provide:
* the opportunity to '''strengthen encapsulation,''' because values cannot be receive assignment without executing the property's "set" code
* '''uniform access ''' queries because properties are queries, but unlike previous C-style OO languages in which properties did not exist, if a property is used in programming a client class, the programmer does not need to know whether the data provided by the property was done so from memory or through computation. This leaves the producer of the class with the property the latitude to change the implementation without breaking existing clients.
In Eiffel, the same goals are fulfilled, but a little differently. Simple attributes are well-encapsulated, because the Eiffel language does not allow direct assignment to them from outside the control of their class. So any assignment of the form <code>x</code>. <code>f</code> := <code>y</code> is not valid in Eiffel. To allow client to set values of the attribute <code>f</code>, the producer of the class of which <code>x</code> is an instance would have built a command (a " <code>set_</code>" procedure) to do so. Then the code in a client to set <code>f</code> would look like this: <code>x</code>. <code>set_f</code> ( <code>y</code>).
In Eiffel, the same goals are fulfilled, but a little differently. Simple attributes are well-encapsulated, because the Eiffel language does not allow direct assignment to them from outside the control of their class. So any assignment of the form <code>x</code>. <code>f</code> := <code>y</code> is not valid in Eiffel. To allow client to set values of the attribute <code>f</code>, the producer of the class of which <code>x</code> is an instance would have built a command (a "<code>set_</code>" procedure) to do so. Then the code in a client to set <code>f</code> would look like this: <code>x</code>.<code>set_f</code> (<code>y</code>).
Uniform access is achieved in Eiffel through the way in which clientssee features which are queries. The code " <code>print</code> ( <code>x</code>. <code>count</code>)" applies the query <code>count</code> to the object attached to <code>x</code> and prints the result. You cannot tell by looking at this code whether <code>count</code> is an attribute or a function, that is, whether the <code>count</code> is returned from memory or through computation. In fact, it could be either, but that is a matter for its producer to deal with. As reuse consumers the implementation of <code>count</code>is not important to us ...but the fact that the producer can change the implementation without causing our code to need modification is very important to us.
Uniform access is achieved in Eiffel through the way in which clientssee features which are queries. The code " <code>print</code> ( <code>x</code>.<code>count</code>)" applies the query <code>count</code> to the object attached to <code>x</code> and prints the result. You cannot tell by looking at this code whether <code>count</code> is an attribute or a function, that is, whether the <code>count</code> is returned from memory or through computation. In fact, it could be either, but that is a matter for its producer to deal with. As reuse consumers the implementation of <code>count</code>is not important to us ...but the fact that the producer can change the implementation without causing our code to need modification is very important to us.
Because Eiffel does not support properties directly, the propertiesof typeswhich Eiffel for .NET programmers usefrom .NET assemblies have to be mapped to Eiffel features.
In order to ask for the property's current value (technically, receiving the result of the property's <code>get</code> routine), a query feature is generated for the property. The query will be namedthe Eiffel name of the property.
As noted above, setting the value of a property cannot be done in Eiffel as it is done in C# and VB.NET because Eiffel disallows assignments of the form <code>x</code>. <code>f</code> := <code>y</code>. So, for each writable property, an Eiffel command feature is available to set the value of the property. The name for this command will be <code>set_</code> followed by the Eiffel name for the property.
As noted above, setting the value of a property cannot be done in Eiffel as it is done in C# and VB.NET because Eiffel disallows assignments of the form <code>x</code>.<code>f</code> := <code>y</code>. So, for each writable property, an Eiffel command feature is available to set the value of the property. The name for this command will be "<code>set_</code>" followed by the Eiffel name for the property.
As a result, the code for using a .NET property looks very much like the code to use an Eiffel attribute. In the following code fragment, an instance of the type <code>System.Windows.Forms.Form</code> which is available in Eiffel for .NET as <code>WINFORMS_FORM</code> is used by an Eiffel client. <code>System.Windows.Forms.Form</code> has a property <code>Text</code> which is of type <code>System.String</code>. Here the <code>Text</code> property is being set using the <code>set_text</code> feature, and then being recalled by using the query <code>text</code>.
<code>

View File

@@ -19,17 +19,17 @@ When we build software that has access to both the rich Eiffel libraries and the
The example of these similar types which will almost certainly get in your face is the string types. You may remember that we looked briefly at the case of the string types in Naming Conventions and Name Handling.
The Eiffel Base Library contains class <code> STRING </code>; the .NET assembly contains type <code> System.String </code>, which Eiffel for .NETusers see as <code> SYSTEM_STRING </code>. At an abstract level both of these model sequences of characters. But they are not the same type. In fact, they are different in some important ways. For example, instances of <code> System.String </code> are immutable. So you cannot append to an instance of <code> System.String </code>. If you want to build a string by appending, you should use an instance of <code> System.Text.StringBuilder </code> to do the appending, then extract the instance of <code> System.String </code> from it. With <code> STRING </code> it is permissible to append, so you don't need a helper like the <code> System.Text.StringBuilder </code> type.
The Eiffel Base Library contains class <code>STRING</code>; the .NET assembly contains type <code>System.String</code>, which Eiffel for .NETusers see as <code>SYSTEM_STRING</code>. At an abstract level both of these model sequences of characters. But they are not the same type. In fact, they are different in some important ways. For example, instances of <code>System.String</code> are immutable. So you cannot append to an instance of <code>System.String</code>. If you want to build a string by appending, you should use an instance of <code>System.Text.StringBuilder</code> to do the appending, then extract the instance of <code>System.String</code> from it. With <code>STRING</code> it is permissible to append, so you don't need a helper like the <code>System.Text.StringBuilder</code> type.
So the two types are similar at an abstract level, but different semantically. There are reasonable arguments for the design of each.
Many types in the delivered assemblies have properties which are strings or methods which return or take strings as arguments. In all these cases, the strings in question are instances of <code> System.String </code>.
Many types in the delivered assemblies have properties which are strings or methods which return or take strings as arguments. In all these cases, the strings in question are instances of <code>System.String</code>.
Many classes in the delivered Eiffel libraries have features involving strings, that is attributes which are strings or routines which return or take strings as arguments. In all these cases, the strings are instances of <code> STRING </code> (except for those designed for .NET compliance).
Many classes in the delivered Eiffel libraries have features involving strings, that is attributes which are strings or routines which return or take strings as arguments. In all these cases, the strings are instances of <code>STRING</code> (except for those designed for .NET compliance).
In C# and VB.NET, if you specify a quoted string like <code> "Hello World!" </code> in your code, that string will conform to type <code> System.String </code>. If you do the same in Eiffel, then <code> "Hello World!" </code> will be an instance of <code> STRING </code>. In Eiffel terminology, <code> "Hello World!" </code> appearing in source code is a manifest string.
In C# and VB.NET, if you specify a quoted string like <code>"Hello World!"</code> in your code, that string will conform to type <code>System.String</code>. If you do the same in Eiffel, then <code>"Hello World!"</code> will be an instance of <code>STRING</code>. In Eiffel terminology, <code>"Hello World!"</code> appearing in source code is a manifest string.
What all this means to you is that you cannot use an instance of <code> System.String </code> when an instance of <code> STRING </code> is called for, and vice versa. Three out of four of the executable lines in the following code sample are invalid:
What all this means to you is that you cannot use an instance of <code>System.String</code> when an instance of <code>STRING</code> is called for, and vice versa. Three out of four of the executable lines in the following code sample are invalid:
<code>
local
my_string: STRING
@@ -44,13 +44,13 @@ What all this means to you is that you cannot use an instance of <code> System.S
end
</code>
To handle this issue, the Eiffel for .NET class <code> STRING </code> has two features which can be used when a string of the other type is needed.
To handle this issue, the Eiffel for .NET class <code>STRING</code> has two features which can be used when a string of the other type is needed.
The first of these features is a query <code> to_cil </code> which returns an object of type <code> System.String </code> which has a sequence of characters equivalent to that of the <code> STRING </code> to which <code> to_cil </code> is applied. The <code> to_cil </code> can be applied to manifest strings by enclosing the manifest string in parentheses.
The first of these features is a query <code>to_cil</code> which returns an object of type <code>System.String</code> which has a sequence of characters equivalent to that of the <code>STRING</code> to which <code>to_cil</code> is applied. The <code>to_cil</code> can be applied to manifest strings by enclosing the manifest string in parentheses.
The other feature is a creation procedure named <code> make_from_cil </code> which takes as an argument an instance of <code> System.String </code> and initializes its target <code> STRING </code> with a sequence of characters equivalent to that of the argument.
The other feature is a creation procedure named <code>make_from_cil</code> which takes as an argument an instance of <code>System.String</code> and initializes its target <code>STRING</code> with a sequence of characters equivalent to that of the argument.
In the following sample, we use these features of <code> STRING </code> to make all the lines from the previous sample valid.
In the following sample, we use these features of <code>STRING</code> to make all the lines from the previous sample valid.
<code>
local
my_string: STRING
@@ -65,8 +65,8 @@ In the following sample, we use these features of <code> STRING </code> to make
end
</code>
{{note|As shown in the above example, it is necessary to apply <code> to_cil </code> to a manifest string if you are assigning it to a <code> System.String </code> or passing it as an argument where a <code> System.String </code> is called for. <br/>
This is expected to change in a future release. It shall become unnecesary to apply <code> to_cil </code> to manifest strings. Instead, whether a <code> STRING </code> or <code> System.String </code> is needed will be determined by the context in which the manifest string is being used, and the proper type of object will be generated. }}
{{note|As shown in the above example, it is necessary to apply <code>to_cil</code> to a manifest string if you are assigning it to a <code>System.String</code> or passing it as an argument where a <code>System.String</code> is called for. <br/>
This is expected to change in a future release. It shall become unnecesary to apply <code>to_cil</code> to manifest strings. Instead, whether a <code>STRING</code> or <code>System.String</code> is needed will be determined by the context in which the manifest string is being used, and the proper type of object will be generated. }}
===Other Similar Types===

View File

@@ -5,16 +5,16 @@ In any comprehensive object-oriented system, the act of programming results in c
==Eiffel Clusters==
Eiffel classes are grouped in clusters. These clusters of classes ordinarily share some commonality of functionality or purpose. In other words,the classes in a particular cluster may provide the same sorts of capabilities or relate to a single software model. In the Eiffel Base Library there is a cluster called <code> list </code> which contains classes which implement different types of lists, for example, <code> ARRAYED_LIST </code>, <code> LINKED_LIST </code>, <code> SORTED_TWO_WAY_LIST </code>, <code> TWO_WAY_CIRCULAR </code>. At an abstract level all these classes are related to the software model of the notion of "list".
Eiffel classes are grouped in clusters. These clusters of classes ordinarily share some commonality of functionality or purpose. In other words,the classes in a particular cluster may provide the same sorts of capabilities or relate to a single software model. In the Eiffel Base Library there is a cluster called <code>list</code> which contains classes which implement different types of lists, for example, <code>ARRAYED_LIST</code>, <code>LINKED_LIST</code>, <code>SORTED_TWO_WAY_LIST</code>, <code>TWO_WAY_CIRCULAR</code>. At an abstract level all these classes are related to the software model of the notion of "list".
The cluster <code> list </code> is actually a subcluster of the cluster <code> structures </code> which contains clusters other than <code> list </code> related to data structures other than lists. Eiffel convention dictates that a cluster should either contain classes or subclusters, but not both.
The cluster <code>list</code> is actually a subcluster of the cluster <code>structures</code> which contains clusters other than <code>list</code> related to data structures other than lists. Eiffel convention dictates that a cluster should either contain classes or subclusters, but not both.
So clusters serve both to categorize and locate classes. So, class <code> LINKED_LIST </code> can be described as a basic library class implementing a data structure, more particularly a list. As such, it can be found in the Base Library, in cluster <code> structures </code> in subcluster <code> list </code>.
So clusters serve both to categorize and locate classes. So, class <code>LINKED_LIST</code> can be described as a basic library class implementing a data structure, more particularly a list. As such, it can be found in the Base Library, in cluster <code>structures</code> in subcluster <code>list</code>.
==.NET Namespaces and Assemblies==
In .NET, types (the language independent, compiled form of classes) are stored in "assemblies". So, we locate a type we want to use by referencing the assembly in which it resides.
As .NET programmers, we think of types as being categorized by namespace. For example, we view the type <code> System.Windows.Forms.TextBox </code> as the <code> TextBox </code> type in the context of the windows forms namespace ( <code> System.Windows.Forms </code>). Particularly, this is in contrast to type <code> System.Web.UI.TextBox </code> which is a <code> TextBox </code> for web forms. As it relates to making .NET types usable by Eiffel, the important thing to understand is that the real .NET type name is the fully qualified type name, including the namespace. Namespaces are simply a bit of "syntactic sugar" that keeps us from having to repeat the entire type name every time we use it in source code.
As .NET programmers, we think of types as being categorized by namespace. For example, we view the type <code>System.Windows.Forms.TextBox</code> as the <code>TextBox</code> type in the context of the windows forms namespace ( <code>System.Windows.Forms</code>). Particularly, this is in contrast to type <code>System.Web.UI.TextBox</code> which is a <code>TextBox</code> for web forms. As it relates to making .NET types usable by Eiffel, the important thing to understand is that the real .NET type name is the fully qualified type name, including the namespace. Namespaces are simply a bit of "syntactic sugar" that keeps us from having to repeat the entire type name every time we use it in source code.
===.NET Assemblies Available to Eiffel===
When types from .NET assemblies are made available to Eiffel programmers, each assembly is mapped to a cluster. So all the types in an assembly appear as if they were Eiffel classes in a cluster which corresponds to the .NET assembly.
To summarize, as you learned in Naming Conventions and Name Handling, unambiguous Eiffel-style names are made available for the.NET types in an assembly. The assembly is represented to Eiffel for .NET programmers as a cluster of classes. The process of name derivation is based upon a portion of the .NET type name, possibly augmented with a prefix to ensure uniqueness.
@@ -25,11 +25,11 @@ The object model for which Eiffel was designed differs in some ways from the .NE
When you compile Eiffel for .NET, the result is a .NET assembly; either an executable system, or a library of potentially reusable data types. Because the object model for Eiffel is different from that of .NET, the assembly resulting from a compile is different in some ways.
First an assembly built using Eiffel for .NET will likely contain lots of types and interfaces. This is because as you use a class from the Eiffel libraries, say class <code> STRING </code>, all the classes upon which <code> STRING </code> depends ( <code> STRING </code>'s suppliers and ancestors) must also be included in the assembly. That's because the Eiffel libraries, unlike the Microsoft .NET libraries like <code> mscorlib </code> and <code> System.Data </code>, are not distributed as shared type libraries.
First an assembly built using Eiffel for .NET will likely contain lots of types and interfaces. This is because as you use a class from the Eiffel libraries, say class <code>STRING</code>, all the classes upon which <code>STRING</code> depends ( <code>STRING</code>'s suppliers and ancestors) must also be included in the assembly. That's because the Eiffel libraries, unlike the Microsoft .NET libraries like <code>mscorlib</code> and <code>System.Data</code>, are not distributed as shared type libraries.
Another thing you may notice is that each Eiffel class you produce is represented by three entities in the assembly ... two classes and an interface. So, if you produce a class called <code> GUARD_DOG </code>, then in the assembly you'd see an interface called <code> GuardDog </code>, a class called <code> Impl.GuardDog </code>, and a class called <code> Create.GuardDog </code>. Again, this is done for reasons that concern the differences in the object models between Eiffel and .NET.
Another thing you may notice is that each Eiffel class you produce is represented by three entities in the assembly ... two classes and an interface. So, if you produce a class called <code>GUARD_DOG</code>, then in the assembly you'd see an interface called <code>GuardDog</code>, a class called <code>Impl.GuardDog</code>, and a class called <code>Create.GuardDog</code>. Again, this is done for reasons that concern the differences in the object models between Eiffel and .NET.
The <code> GuardDog </code> interface is what you use when you declare an entity or variable of that type. The objects attached to that entity at runtime will be of the type <code> Impl.GuardDog </code>. You create an instance of <code> Impl.GuardDog </code> and attach it to an entity of type <code> GuardDog </code> by calling a routine in the factory class <code> Create.GuardDog </code>. The factory routines will almost always have names that begin with the word " <code> Make </code>", and represent the creation routines of Eiffel the classes. So in the case of using an instance of <code> GuardDog </code> from a C# class, the code would like this:
The <code>GuardDog</code> interface is what you use when you declare an entity or variable of that type. The objects attached to that entity at runtime will be of the type <code>Impl.GuardDog</code>. You create an instance of <code>Impl.GuardDog</code> and attach it to an entity of type <code>GuardDog</code> by calling a routine in the factory class <code>Create.GuardDog</code>. The factory routines will almost always have names that begin with the word " <code>Make</code>", and represent the creation routines of Eiffel the classes. So in the case of using an instance of <code>GuardDog</code> from a C# class, the code would like this:
<code>
{
GuardDog aGuardDog = Create.GuardDog.Make(); //Create an instance
@@ -39,7 +39,7 @@ The <code> GuardDog </code> interface is what you use when you declare an entity
This object creation model accounts for some of the differences between constructors in .NET and creation procedures in Eiffel. These differences will be discussed in more detail in Constructors and Creation Procedures.
Another advantage is that it provides a syntax that is similar to that used to create objects in Eiffel. An Eiffel for .NET client to the class <code> GUARD_DOG </code> might use the following code to create and use an instance.
Another advantage is that it provides a syntax that is similar to that used to create objects in Eiffel. An Eiffel for .NET client to the class <code>GUARD_DOG</code> might use the following code to create and use an instance.
<code>
local
a_guard_dog: GUARD_DOG -- Declare an entity of the type
@@ -49,7 +49,7 @@ Another advantage is that it provides a syntax that is similar to that used to c
end
</code>
You may have noticed in these examples that even though the type <code> GuardDog </code> was compiled from an Eiffel class, when the C# client uses <code> GuardDog </code>, it uses what would be considered the .NET naming convention for the type <code> GuardDog </code> (vs. <code> GUARD_DOG </code>) and the method name <code> RollOver </code> (vs <code> roll_over </code>). What happens here is that when assemblies are produced from Eiffel classes, by default .NET naming standards are used in the assembly.
You may have noticed in these examples that even though the type <code>GuardDog</code> was compiled from an Eiffel class, when the C# client uses <code>GuardDog</code>, it uses what would be considered the .NET naming convention for the type <code>GuardDog</code> (vs. <code>GUARD_DOG</code>) and the method name <code>RollOver</code> (vs <code>roll_over</code>). What happens here is that when assemblies are produced from Eiffel classes, by default .NET naming standards are used in the assembly.

View File

@@ -9,65 +9,67 @@ It is when we add features to a class that we can build the executable code that
==Categorizing Features==
In Eiffel, we have several ways of thinking abstractly about features and categorizing them. As you saw in [[Eiffel Classes|Eiffel Classes]] the <code> feature </code> clause gives us a way to group features in the software text. We have ways to group features more generally, too. Here are some.
In Eiffel, we have several ways of thinking abstractly about features and categorizing them. As you saw in [[Eiffel Classes|Eiffel Classes]] the <code>feature</code> clause gives us a way to group features in the software text. We have ways to group features more generally, too. Here are some.
===Source===
You remember the example class from [[Eiffel Classes|Eiffel Classes]] :
<code>
indexing
description: Objects that model lists
revision: $Revision: 1.5 $
description: Objects that model lists
revision: $Revision: 1.5 $
class
OLD_FASHIONED_LIST [G]
OLD_FASHIONED_LIST [G]
obsolete "This class is obsolete, use LINKED_LIST [G] instead"
inherit
DYNAMIC_LIST [G]
DYNAMIC_LIST [G]
create
make
make
feature -- Initialization
make is
make
-- Create an empty list.
do
before := True
ensure is_before: before
end
do
before := True
ensure
is_before: before
end
feature -- Access
item: G is
-- Current item
do
Result := active.item
end
item: G
-- Current item
do
Result := active.item
end
first: like item is
-- Item at first position
do
Result := first_element.item
end
(other features omitted)
first: like item
-- Item at first position
do
Result := first_element.item
end
... other features omitted ...
invariant
before_constraint: before implies (active = first_element)
after_constraint: after implies (active = last_element)
before_constraint: before implies (active = first_element)
after_constraint: after implies (active = last_element)
end
</code>
The example shows three of the features ( <code> make </code>, <code> item </code>, and <code> first </code>) coded directly in the class. These features (and the ones omitted from the example in the interest of brevity) may not be the only features of the class <code> OLD_FASHIONED_LIST </code>. In fact we can just about guarantee that there are other features of this class. Remember the inheritance part of the class:
The example shows three of the features ( <code>make</code>, <code>item</code>, and <code>first</code>) coded directly in the class. These features (and the ones omitted from the example in the interest of brevity) may not be the only features of the class <code>OLD_FASHIONED_LIST</code>. In fact we can just about guarantee that there are other features of this class. Remember the inheritance part of the class:
<code>
inherit
DYNAMIC_LIST [G]
inherit
DYNAMIC_LIST [G]
</code>
This means that every feature in <code> DYNAMIC_LIST </code> will be a feature of <code> OLD_FASHIONED_LIST </code>. So one way we can think of features is by their source.
This means that every feature in <code>DYNAMIC_LIST</code> will be a feature of <code>OLD_FASHIONED_LIST</code>. So one way we can think of features is by their source.
* Immediate features are those that are introduced by a class itself.
* Inherited features are those that come to the class from its proper ancestors.
@@ -93,7 +95,7 @@ Also, from the producer standpoint we may view features as whether they work fro
** Procedure
This view can be valuable when a producer is considering performance issues. For example, if there is some class function called <code> count </code>, a producer might investigate whether it is better to leave <code> count </code> as a function, computing it each time the feature is applied, or to change it to an attribute and compute it less often.
This view can be valuable when a producer is considering performance issues. For example, if there is some class function called <code>count</code>, a producer might investigate whether it is better to leave <code>count</code> as a function, computing it each time the feature is applied, or to change it to an attribute and compute it less often.
===Usage===
@@ -115,59 +117,59 @@ Queries are features that, when applied to an instance, provide a value in respo
Here is another example class. Again this class contrived, so it does not do anyhing worthwhile except show you what different types of features look like. This class has an attribute and a constant attribute, and functions and procedures both with and without arguments.
<code>
class
SOME_CLASS
SOME_CLASS
create
make,
make_with_arguments
make,
make_with_arguments
feature -- Initialization
make is
-- Creation procedure
do
an_attribute := 5
end
make
-- Creation procedure
do
an_attribute := 5
end
make_with_arguments (hour: INTEGER; minute: INTEGER; second: INTEGER) is
-- Another creation procedure
do
an_attribute := second + (minute * 60) + (hour * 3600)
end
make_with_arguments (hour: INTEGER; minute: INTEGER; second: INTEGER)
-- Another creation procedure
do
an_attribute := second + (minute * 60) + (hour * 3600)
end
feature -- Access
an_attribute: INTEGER
-- An attribute of type INTEGER
an_attribute: INTEGER
-- An attribute of type INTEGER
another_attribute: INTEGER is 46
-- A constant attribute
another_attribute: INTEGER = 46
-- A constant attribute
a_function: STRING is
-- A function without arguments
do
Result := an_attribute.out
end
a_function: STRING is
-- A function without arguments
do
Result := an_attribute.out
end
another_function (an_int: INTEGER): INTEGER is
-- A function with arguments
do
Result := an_attribute + an_int
end
another_function (an_int: INTEGER): INTEGER
-- A function with arguments
do
Result := an_attribute + an_int
end
feature -- Basic Operations
a_procedure is
-- A procedure with no arguments
do
an_attribute := an_attribute + 5
end
a_procedure
-- A procedure with no arguments
do
an_attribute := an_attribute + 5
end
another_procedure (an_int: INTEGER; another_int: INTEGER) is
-- A procedure with arguments
do
an_attribute := an_attribute + an_int + another_int
end
another_procedure (an_int: INTEGER; another_int: INTEGER)
-- A procedure with arguments
do
an_attribute := an_attribute + an_int + another_int
end
end -- Class SOME_CLASS
</code>
@@ -185,11 +187,11 @@ When you write a feature in a class, you typically will include some of the foll
Let's dissect one feature and identify its parts:
<code>
another_function (an_int: INTEGER): INTEGER is
-- A function with arguments
do
Result := an_attribute + an_int
end
another_function (an_int: INTEGER): INTEGER
-- A function with arguments
do
Result := an_attribute + an_int
end
</code>
In this feature:
@@ -201,9 +203,10 @@ In this feature:
* "INTEGER" (after the argument list) is the feature's type.
* "do " introduces the implementation code.
* "Result := an_attribute + an_int" is an instruction.
{{note|This feature is a function. As a consequence the computation uses the keyword "Result" as an entity name for the value to be returned by the function }}
* "end" ends the feature declaration
{{note|This feature is a function. As a consequence the computation uses the keyword "<eiffel>Result</eiffel>' as an entity name for the value to be returned by the function }}
* "<eiffel>end</eiffel>" ends the feature declaration
==General Structure of Routines==
@@ -222,69 +225,69 @@ All of the sections except the Routine Body are optional.
Here is another feature, a routine, which has all of these except obsolete and rescue.
<code>
insert_text_header_item (a_label: STRING;
a_width, a_format: INTEGER; insert_after_item_no: INTEGER) is
-- Insert a text item to the header control
-- after the `insert_item_item_no' item.
require
exists: exists
label_not_void: a_label /= Void
insert_after_item_no_positive: insert_after_item_no >= 0
local
hd_item: WEL_HD_ITEM
do
create hd_item.make
hd_item.set_text (a_label)
hd_item.set_width (a_width)
hd_item.set_format (a_format)
insert_header_item (hd_item, insert_after_item_no)
ensure
item_count_increased: item_count = old item_count + 1
end
insert_text_header_item (a_label: STRING;
a_width, a_format: INTEGER; insert_after_item_no: INTEGER)
-- Insert a text item to the header control
-- after the `insert_item_item_no' item.
require
exists: exists
label_not_void: a_label /= Void
insert_after_item_no_positive: insert_after_item_no >= 0
local
hd_item: WEL_HD_ITEM
do
create hd_item.make
hd_item.set_text (a_label)
hd_item.set_width (a_width)
hd_item.set_format (a_format)
insert_header_item (hd_item, insert_after_item_no)
ensure
item_count_increased: item_count = old item_count + 1
end
</code>
Using this example, let's identify and discuss the different sections.
===Header Comment===
<code>
-- Insert a text item to the header control
-- after the `insert_item_item_no' item.
-- Insert a text item to the header control
-- after the `insert_item_item_no' item.
</code>
Although the feature's header comment is optional, most Eiffel programmers and their technical managers feel that it should never be omitted. Header comments are included by some language processing tools in specialized views of classes. Header comments are almost always short, usually no more than a few lines. Header comments serve to provide a natural language description of what the feature will do (in the case of features that are commands) or what information it provides (in the case of queries).
===Obsolete===
The feature <code> insert_text_header_item </code> is not obsolete ... but if it were it would include an <code> obsolete </code> clause. This works much like the obsolete part for classes that we saw in [[Eiffel Classes|Eiffel Classes]] : the keyword " <code> obsolete </code>" followed by a manifest string which bears a message to potential reuse consumers:
The feature <code>insert_text_header_item</code> is not obsolete ... but if it were it would include an <code>obsolete</code> clause. This works much like the obsolete part for classes that we saw in [[Eiffel Classes|Eiffel Classes]] : the keyword "<code>obsolete</code>" followed by a manifest string which bears a message to potential reuse consumers:
===Precondition===
<code>
require
exists: exists
label_not_void: a_label /= Void
insert_after_item_no_positive: insert_after_item_no >= 0
require
exists: exists
label_not_void: a_label /= Void
insert_after_item_no_positive: insert_after_item_no >= 0
</code>
The precondition part of a feature is introduced by the keyword " <code> require </code>". It contains a set of assertions which define the state necessary for the correct execution of a routine. We will see more about assertions in [[Design by Contract and Assertions|Design by Contract and Assertions]] .
The precondition part of a feature is introduced by the keyword "<code>require</code>". It contains a set of assertions which define the state necessary for the correct execution of a routine. We will see more about assertions in [[Design by Contract and Assertions|Design by Contract and Assertions]] .
===Local Declarations===
<code>
local
hd_item: WEL_HD_ITEM
local
hd_item: WEL_HD_ITEM
</code>
This part contains the declarations for any "local entities" used by the feature. Sometimes the computation accomplished in a feature requires the use of entities which are only temporary. It would not be appropriate to make these attributes of the class. So, instead we can use local entities, which have scope only within the feature in which they are declared. In the example, <code> hd_item </code> is available as type <code> WEL_HD_ITEM </code> during the computation of feature <code> insert_text_header_item </code>.
This part contains the declarations for any "local entities" used by the feature. Sometimes the computation accomplished in a feature requires the use of entities which are only temporary. It would not be appropriate to make these attributes of the class. So, instead we can use local entities, which have scope only within the feature in which they are declared. In the example, <code>hd_item</code> is available as type <code>WEL_HD_ITEM</code> during the computation of feature <code>insert_text_header_item</code>.
{{note|A local entity name must not be the same as any feature of the class in which its feature occurs or the same as any argument name of the feature in which it occurs. }}
===Routine Body===
<code>
do
create hd_item.make
hd_item.set_text (a_label)
hd_item.set_width (a_width)
hd_item.set_format (a_format)
insert_header_item (hd_item, insert_after_item_no)
do
create hd_item.make
hd_item.set_text (a_label)
hd_item.set_width (a_width)
hd_item.set_format (a_format)
insert_header_item (hd_item, insert_after_item_no)
</code>
This is the routine body for a fairly typical effective, internal routine. It contains the instructions that get the job done for the feature. You may notice that experienced Eiffel programmers rarely write routine bodies that are more than a few lines long. The reason for this is more that just intuitive. This phenomenon will be explained in the section [[Design by Contract and Assertions|Design by Contract and Assertions]] .
@@ -293,12 +296,12 @@ There are other forms that a routine body can take. Here are some examples of so
====External Routines====
<code>
cwin_tooltips_class: POINTER is
external
"C [macro ] : EIF_POINTER"
alias
"TOOLTIPS_CLASS"
end
cwin_tooltips_class: POINTER
external
"C [macro ] : EIF_POINTER"
alias
"TOOLTIPS_CLASS"
end
</code>
The routine body above is for an "external" routine. External routines are used to represent within Eiffel classes, routines that are written in other languages.
@@ -307,70 +310,70 @@ The routine body above is for an "external" routine. External routines are used
====Once Routines====
<code>
once
Result := some_computation
once
Result := some_computation
</code>
This is the routine body of a "once" routine, specifically a "once function". A once routine is introduced by the keyword " <code> once </code>" rather than <code> do </code>. It contains an computational body that executes only the first time it is called. Upon subsequent calls it has no effect.
This is the routine body of a "once" routine, specifically a "once function". A once routine is introduced by the keyword "<code>once</code>" rather than <code>do</code>. It contains an computational body that executes only the first time it is called. Upon subsequent calls it has no effect.
Once procedures are useful for doing things that for some reason should not be done multiple times.
If the once routine is a function (as the example above), it computes the resulting object on the first call, then on subsequent calls, it returns the object it originally created without executing the computation. Once functions facilitate shared objects which helps us in avoiding globals. A class containing a once function can be inherited by many other classes. The first object to apply the once function causes the resulting object to be created and initialized. Subsequent applications by any other object accesses the originally created instance.
If the once routine is a function (as the example above), it computes the resulting object on the first call, then on subsequent calls, it returns the object it originally created without executing the computation. Once functions facilitate shared objects which helps us in avoiding global entities. A class containing a once function can be inherited by many other classes. The first object to apply the once function causes the resulting object to be created and initialized. Subsequent applications by any other object accesses the originally created instance.
====Deferred Routines====
<code>
deferred
deferred
</code>
The body for a deferred routine is simply the keyword " <code> deferred </code>". Below is the deferred routine body in the context of an entire feature.
The body for a deferred routine is simply the keyword "<code>deferred</code>". Below is the deferred routine body in the context of an entire feature.
<code>
set_position (new_position: INTEGER) is
-- Set `position' with `new_position'
require
exists: exists
valid_minimum: new_position >= minimum
valid_maximum: new_position <= maximum
deferred
ensure
position_set: position = new_position
end
set_position (new_position: INTEGER)
-- Set `position' with `new_position'
require
exists: exists
valid_minimum: new_position >= minimum
valid_maximum: new_position <= maximum
deferred
ensure
position_set: position = new_position
end
</code>
As we learned in [[Eiffel Classes|Eiffel Classes]] a deferred routine has specification, but no implementation. We will investigate deferred classes and features further in [[Inheritance|Inheritance]] .
===Postcondition===
<code>
ensure
item_count_increased: item_count = old item_count + 1
ensure
item_count_increased: item_count = old item_count + 1
</code>
The postcondition part of a routine is introduced by the keyword " <code> ensure </code>". The postcondition is a group of assertions which describe the state that must be satisfied upon the successful completion of the routine. We will see more about assertions in [[Design by Contract and Assertions|Design by Contract and Assertions]] .
The postcondition part of a routine is introduced by the keyword "<code>ensure</code>". The postcondition is a group of assertions which describe the state that must be satisfied upon the successful completion of the routine. We will see more about assertions in [[Design by Contract and Assertions|Design by Contract and Assertions]] .
===Rescue===
Our example feature does not have an explicitly coded rescue part. The rescue, introduced by the keyword " <code> rescue </code>", provides a routine with a chance to do additional processing in the case that it incurs an exeption during normal processing. We will learn about the rescue clause in the section [[Exception Mechanism|Exception Mechanism]] . Until then, you can see what a rescue part looks like in the feature below.
Our example feature does not have an explicitly coded rescue part. The rescue, introduced by the keyword "<code>rescue</code>", provides a routine with a chance to do additional processing in the case that it incurs an exeption during normal processing. We will learn about the rescue clause in the section [[Exception Mechanism|Exception Mechanism]] . Until then, you can see what a rescue part looks like in the feature below.
<code>
bind is
-- Bind socket to local address in `address'.
require
socket_exists: exists
valid_local_address: address /= Void
local
ext: ANY
retried: BOOLEAN
do
if not retried then
ext := address.socket_address
c_bind (descriptor, $ext, address.count)
is_open_read := True
end
rescue
if not assertion_violation then
is_open_read := False
retried := True
retry
end
end
bind
-- Bind socket to local address in `address'.
require
socket_exists: exists
valid_local_address: address /= Void
local
ext: ANY
retried: BOOLEAN
do
if not retried then
ext := address.socket_address
c_bind (descriptor, $ext, address.count)
is_open_read := True
end
rescue
if not assertion_violation then
is_open_read := False
retried := True
retry
end
end
</code>
==Making Features Available to Clients==
@@ -383,66 +386,66 @@ Control of the export of immediate features (those features introduced in the te
<code>
feature -- Access
an_attribute: INTEGER
-- An attribute of type INTEGER
an_attribute: INTEGER
-- An attribute of type INTEGER
another_attribute: INTEGER is 46
-- A constant attribute
another_attribute: INTEGER = 46
-- A constant attribute
a_function: STRING is
-- A function without arguments
do
Result := an_attribute.out
end
a_function: STRING
-- A function without arguments
do
Result := an_attribute.out
end
another_function (an_int: INTEGER): INTEGER is
-- A function with arguments
do
Result := an_attribute + an_int
end
another_function (an_int: INTEGER): INTEGER
-- A function with arguments
do
Result := an_attribute + an_int
end
feature -- Basic Operations
a_procedure is
-- A procedure with no arguments
do
an_attribute := an_attribute + 5
end
a_procedure
-- A procedure with no arguments
do
an_attribute := an_attribute + 5
end
another_procedure (an_int: INTEGER; another_int: INTEGER) is
-- A procedure with arguments
do
an_attribute := an_attribute + an_int + another_int
end
another_procedure (an_int: INTEGER; another_int: INTEGER)
-- A procedure with arguments
do
an_attribute := an_attribute + an_int + another_int
end
</code>
A feature clause like the ones in the example above means that all the features that follow, until the next feature clause are exported to clients based on any class. Techically, this
A feature clause like the ones in the example above means that all the features that follow, until the next feature clause are exported to clients based on any class. Technically, this
<code>
feature -- Basic Operations
feature -- Basic Operations
</code>
is equivalent to
<code>
feature {ANY} -- Basic Operations
feature {ANY} -- Basic Operations
</code>
which means that the following features are available to clients which conform to class <code> ANY </code>. And in Eiffel, <code> ANY </code> is the class from which all other classes inherit. As a consequence all classes conform to <code> ANY </code>.
which means that the following features are available to clients which conform to class <code>ANY</code>. And in Eiffel, <code>ANY</code> is the class from which all other classes inherit. As a consequence all classes conform to <code>ANY</code>.
Inside the braces is a list of classes which are eligible as clients.
<code>
feature {STRING_HANDLER} -- Implementation
feature {STRING_HANDLER} -- Implementation
</code>
Features following this example from class <code> STRING </code> will be available to client class <code> STRING_HANDLER </code> and all its proper descendants.
Features following this example from class <code>STRING</code> will be available to client class <code>STRING_HANDLER</code> and all its proper descendants.
As stated above, you can put a list of class names in the braces:
<code>
feature {CLASS_A, CLASS_B, CLASS_C} -- Semi-private features
feature {CLASS_A, CLASS_B, CLASS_C} -- Semi-private features
</code>
Often features which are solely for implementation should not be seen or used by clients of any type. You can ensure this by exporting to class <code> NONE </code>, the class from which no other class can inherit:
Often features which are solely for implementation should not be seen or used by clients of any type. You can ensure this by exporting to class <code>NONE</code>, the class from which no other class can inherit:
<code>
feature {NONE} -- Implementation
feature {NONE} -- Implementation
</code>
==Eiffel Instructions and Control Structures==
@@ -451,81 +454,81 @@ When you begin to write routines in Eiffel, you will need to understand how to w
===Creation===
<code>
create hd_item.make
create hd_item.make
</code>
We discussed creation procedures and the process of bringing new objects into being in [[Eiffel Classes|Eiffel Classes]] . A creation instruction starts with the keyword " <code> create </code>". It creates a new object, initialize its fields, may apply a creation procedure, and attaches the object to an entity.
We discussed creation procedures and the process of bringing new objects into being in [[Eiffel Classes|Eiffel Classes]] . A creation instruction starts with the keyword "<code>create</code>". It creates a new object, initialize its fields, may apply a creation procedure, and attaches the object to an entity.
===Procedure Call===
<code>
hd_item.set_text (a_label)
hd_item.set_text (a_label)
</code>
The application of a feature to an object constitutes an instruction if the feature is a procedure.
===Assignment===
In Eiffel, assignment syntax is simple. But depending upon the types involved, what actually happens may need some explanation. Assume here that <code> is_open_read </code> is an attribute declared as type <code> BOOLEAN </code>:
In Eiffel, assignment syntax is simple. But depending upon the types involved, what actually happens may need some explanation. Assume here that <code>is_open_read</code> is an attribute declared as type <code>BOOLEAN</code>:
<code>
is_open_read := False
is_open_read := False
</code>
In this instruction, an attribute of type <code> BOOLEAN </code> named <code> is_open_read </code> is being assigned the value of the manifest boolean " <code> False </code>". The attribute <code> is_open_read </code> is based on an expanded class <code> BOOLEAN </code>, so that means that the value for <code> False </code> is held in the field for <code> is_open_read </code>. This is in contrast to what happens with reference types.
In this instruction, an attribute of type <code>BOOLEAN</code> named <code>is_open_read</code> is being assigned the value of the manifest boolean "<code>False</code>". The attribute <code>is_open_read</code> is based on an expanded class <code>BOOLEAN</code>, so that means that the value for <code>False</code> is held in the field for <code>is_open_read</code>. This is in contrast to what happens with reference types.
<code>
my_string := some_other_string
my_string := some_other_string
</code>
In this assignment, we will assume that both entities are type <code> STRING </code>. Because <code> STRING </code> is a reference type, the field for <code> my_string </code> will hold either a reference to an instance of <code> STRING </code>, or it will be <code> Void </code>. When the assignment above executes, then whatever was in the field for <code> my_string </code> is replaced with a reference to the same object that <code> some_other_string </code> refers to.
In this assignment, we will assume that both entities are type <code>STRING</code>. Because <code>STRING</code> is a reference type, the field for <code>my_string</code> will hold either a reference to an instance of <code>STRING</code>, or it will be <code>Void</code>. When the assignment above executes, then whatever was in the field for <code>my_string</code> is replaced with a reference to the same object that <code>some_other_string</code> refers to.
In summary, for objects based on expanded classes, assignment means assignment of value. For objects based on reference classes, assignment means assignment of reference.
====Conformance====
There is an important rule about assignment in Eiffel. The rule exists to ensure type safety. Consider the following assignment which we would read as " <code> x </code>receives <code> y </code>" or " <code> x </code>gets <code> y </code>".
There is an important rule about assignment in Eiffel. The rule exists to ensure type safety. Consider the following assignment which we would read as "<code>x</code> receives <code>y</code>" or "<code>x</code> gets <code>y</code>".
<code>
x := y
x := y
</code>
then the rule says that this assignment is valid only if the type of <code> y </code> conforms to the type of <code> x </code>.
then the rule says that this assignment is valid only if the type of <code>y</code> conforms to the type of <code>x</code>.
The rule is clear enough ... if you know what "conform" means. In the presence of mechanisms like expanded types, inheritance, and genericity, a precise definition of conformance is lengthy.
The rule is clear enough ... if you know what "conform" means. In the presence of mechanisms like expanded types, inheritance, and genericity, a precise definition of conformance is lengthy.
But we can get by for a while with much less. For now let us be satisfied with this:
Consider the classes in the declarations for <code> x </code> and <code> y </code>.
Consider the classes in the declarations for <code>x</code> and <code>y</code>.
You might be tempted to say that to achieve "conformance", the classes would have to be the same. And for the case in which the declarations for <code> x </code> and <code> y </code> are expanded classes, you'd be correct. They conform only if they are the same class.
You might be tempted to say that to achieve "conformance", the classes would have to be the same. And for the case in which the declarations for <code>x</code> and <code>y</code> are expanded classes, you'd be correct. They conform only if they are the same class.
But for reference types, that constraint is unnecessarily restrictive. So, for reference types, the class of <code> y </code> conforms to that <code> x </code> if it is the same class or a proper descendant of that class. This facilitates a powerful idea known as polymorphic attachment that we will see more closely in the section on [[Inheritance|Inheritance]] .
But for reference types, that constraint is unnecessarily restrictive. So, for reference types, the class of <code>y</code> conforms to that <code>x</code> if it is the same class or a proper descendant of that class. This facilitates a powerful idea known as polymorphic attachment that we will see more closely in the section on [[Inheritance|Inheritance]] .
===Conditional===
<code>
if l_c.is_digit then
l_state := 2
elseif l_c = '-' or l_c = '+' then
l_state := 1
else
l_state := 3
end
if l_c.is_digit then
l_state := 2
elseif l_c = '-' or l_c = '+' then
l_state := 1
else
l_state := 3
end
</code>
Conditionals in Eiffel use at a minimum the keywords " <code> if </code>", " <code> then </code>", and <code> end </code>".Optionally, they may use the keywords " <code> elseif </code>" and " <code> else </code>".
Conditionals in Eiffel use at a minimum the keywords "<code>if</code>", "<code>then</code>", and "<code>end</code>". Optionally, they may use the keywords "<code>elseif</code>" and "<code>else</code>".
===Loop===
<code>
from
i := lower
until
i > upper
loop
if item (i) /= Void and then v.is_equal (item (i)) then
Result := Result + 1
end
i := i + 1
end
from
i := lower
until
i > upper
loop
if item (i) /= Void and then v.is_equal (item (i)) then
Result := Result + 1
end
i := i + 1
end
</code>
There is only one structure for loops in Eiffel. In its typical form it uses the four keywords " <code> from </code>", " <code> until </code>", " <code> loop </code>", and " <code> end </code>". The instructions following the <code> from </code> keyword do any initialization necessary for the loop. After <code> until </code> is a boolean expression which is the exit condition. The loop body after <code> loop </code> is a list of instructions that are executed for every iteration. As you can imagine, something in the loop body should most likely cause the exit condition eventually to become true.
There is only one structure for loops in Eiffel. In its typical form it uses the four keywords "<code>from</code>", "<code>until</code>", "<code>loop</code>", and "<code>end</code>". The instructions following the <code>from</code> keyword do any initialization necessary for the loop. After <code>until</code> is a boolean expression which is the exit condition. The loop body after <code>loop</code> is a list of instructions that are executed for every iteration. As you can imagine, something in the loop body should most likely cause the exit condition eventually to become true.

View File

@@ -22,94 +22,95 @@ The code that makes up an Eiffel class is divided into the following parts:
All of the above, except Class header, are optional. So the simplest Eiffel class you could build would look like this:
<code>
class
SIMPLE
SIMPLE
end
</code>
Okay, so class SIMPLE is only interesting in its simplicity. Let's look at an example that is more illustrative:
<code>
indexing
description: Objects that model lists
revision: $Revision: 1.4 $
description: Objects that model lists
revision: $Revision: 1.4 $
class
OLD_FASHIONED_LIST [G]
OLD_FASHIONED_LIST [G]
obsolete "This class is obsolete, use LINKED_LIST [G] instead"
inherit
DYNAMIC_LIST [G]
DYNAMIC_LIST [G]
create
make
make
feature -- Initialization
make is
-- Create an empty list.
do
before := True
ensure
is_before: before
end
make
-- Create an empty list.
do
before := True
ensure
is_before: before
end
feature -- Access
item: G is
-- Current item
do
Result := active.item
end
item: G
-- Current item
do
Result := active.item
end
first: like item is
-- Item at first position
do
Result := first_element.item
end
(other features omitted)
first: like item
-- Item at first position
do
Result := first_element.item
end
... other features omitted ...
invariant
before_constraint: before implies (active = first_element)
after_constraint: after implies (active = last_element)
before_constraint: before implies (active = first_element)
after_constraint: after implies (active = last_element)
</code>
Here is a class that, although completely contrived, utilizes all of the required and optional parts of the class. Let's look at each part individually.
===Indexing===
<code>
indexing
description: Objects that model lists
revision: $Revision: 1.4 $
description: Objects that model lists
revision: $Revision: 1.4 $
</code>
The indexing part of a class is there to allow you as a producer to record information of your choice which will help you or other reuse consumers at some later time to locate understand the class. This important in Eiffel because we try to treat every class as if someday it will become reusable.
Information in indexing does not change the semantics of the class.
The indexing in the class above is typical. It is introduced with the language keyword <code> indexing </code>, and contains two index clauses, each of which is comprised of an index and a single index value. You can code index clauses with indexes that you devise yourself, so there is nothing inherently special about " <code> description </code>" and " <code> revision </code>" as used above. But, these indexes could be special to tools which analyze libraries of classes use them. Although these clauses have only one index value each, it is permissible to put more, separated by commas.
The indexing in the class above is typical. It is introduced with the language keyword <code>indexing</code>, and contains two index clauses, each of which is comprised of an index and a single index value. You can code index clauses with indexes that you devise yourself, so there is nothing inherently special about "<code>description</code>" and "<code>revision</code>" as used above. But, these indexes could be special to tools which analyze libraries of classes use them. Although these clauses have only one index value each, it is permissible to put more, separated by commas.
===Class Header===
<code>
class
OLD_FASHIONED_LIST [G]
OLD_FASHIONED_LIST [G]
</code>
The class header is introduced by the keyword "class", which in turn can be preceded by one of three keywords which mark the class as <code> deferred </code>, <code> expanded </code>, or <code> external </code>. In our example, the class has none of these markings, so it is an effective class whose instances are access by reference.
The class header is introduced by the keyword "class", which in turn can be preceded by one of three keywords which mark the class as <code>deferred</code>, <code>expanded</code>, or <code>external</code>. In our example, the class has none of these markings, so it is an effective class whose instances are access by reference.
The keyword class is followed by the class name, in this case " <code> OLD_FASHIONED_LIST </code>".
The keyword class is followed by the class name, in this case "<code>OLD_FASHIONED_LIST</code>".
Of the three keywords for header marks, the one which you will encounter most often is <code> deferred </code>. A class is deferred if it contains one or more features that are deferred, that is, features which have been specified in the class but for which no implementation has been provided. Proper descendants of a deferred class will provide implementations for its deferred features.
Of the three keywords for header marks, the one which you will encounter most often is <code>deferred</code>. A class is deferred if it contains one or more features that are deferred, that is, features which have been specified in the class but for which no implementation has been provided. Proper descendants of a deferred class will provide implementations for its deferred features.
===Formal Generics===
<code>
class
OLD_FASHIONED_LIST [G]
OLD_FASHIONED_LIST [G]
</code>
In this example the class name is followed by the specification of one formal generic parameter " <code> G </code>". The presence of one or more formal generic parameters will designate a class as a generic class. The formal generic parameter is a place holder for a class name which will be provided by reuse consumers. For example if we wrote a class which was a client to <code> OLD_FASHIONED_LIST </code> we would substitute the class name for the type of objects that we would want to build an <code> OLD_FASHIONED_LIST </code> of. We might make this declaration:
In this example the class name is followed by the specification of one formal generic parameter "<code>G</code>". The presence of one or more formal generic parameters will designate a class as a generic class. The formal generic parameter is a place holder for a class name which will be provided by reuse consumers. For example if we wrote a class which was a client to <code>OLD_FASHIONED_LIST</code> we would substitute the class name for the type of objects that we would want to build an <code>OLD_FASHIONED_LIST</code> of. We might make this declaration:
<code>
my_list_of_cats: OLD_FASHION_LIST [CAT]
my_list_of_cats: OLD_FASHION_LIST [CAT]
</code>
The entity <code> my_list_of_cats </code> could then be attached at runtime to an <code> OLD_FASHIONED_LIST </code> of objects of type <code> CAT </code>. So the class <code> CAT </code> becomes an actual generic parameter and substitutes for <code> G </code> in the declaration.
The entity <code>my_list_of_cats</code> could then be attached at runtime to an <code>OLD_FASHIONED_LIST</code> of objects of type <code>CAT</code>. So the class <code>CAT</code> becomes an actual generic parameter and substitutes for <code>G</code> in the declaration.
Of course formal generic parameters cannot be the same name as a class name in the same universe. If multiple formal generic parameters are used, they are separated by commas.
@@ -120,98 +121,100 @@ You will learn more about generic classes in the section titled [[Genericity|Gen
obsolete "This class is obsolete, use LINKED_LIST [G] instead"
</code>
<code> OLD_FASHION_LIST </code>s are obsolete ... and the class is marked as such by include the line above. The manifest string contains an explanation, instructions, and/or recommended alternatives. Compilers and other language tools can deliver this message to potential reuse consumers. As with indexing, obsolete has no effect on the semantics of the class.
<code>OLD_FASHION_LIST</code>s are obsolete ... and the class is marked as such by include the line above. The manifest string contains an explanation, instructions, and/or recommended alternatives. Compilers and other language tools can deliver this message to potential reuse consumers. As with indexing, obsolete has no effect on the semantics of the class.
Obsolete is rarely used because of the nature of certain elements of the Eiffel methodology. For example, if implementations are well-hidden behind implementation-independent specifications, then those implementations may be changed to adapt the class to changing execution environments in such a way that clients are unaffected.
===Inheritance===
<code>
inherit
DYNAMIC_LIST [G]
DYNAMIC_LIST [G]
</code>
One of the two possible relationships between classes, inheritance is also a powerful software reuse mechanism. In this example class <code> OLD_FASHIONED_LIST </code> declares itself to be a proper descendant of class <code> DYNAMIC_LIST </code>.
One of the two possible relationships between classes, inheritance is also a powerful software reuse mechanism. In this example class <code>OLD_FASHIONED_LIST</code> declares itself to be a proper descendant of class <code>DYNAMIC_LIST</code>.
There will be more in the section called . For now though, be aware of two important implications of this declaration:
* Every feature of <code> DYNAMIC_LIST </code> is available to <code> OLD_FASHIONED_LIST </code> and potentially available to its clients.
* Whenever an instance of <code> DYNAMIC_LIST </code> is called for, then an instance of <code> OLD_FASHIONED_LIST </code> will suffice.
* Every feature of <code>DYNAMIC_LIST</code> is available to <code>OLD_FASHIONED_LIST</code> and potentially available to its clients.
* Whenever an instance of <code>DYNAMIC_LIST</code> is called for, then an instance of <code>OLD_FASHIONED_LIST</code> will suffice.
===Creators===
<code>
create
make
make
</code>
The creators part of a class declares a procedure as being a creation procedure. In this case the procedure in question is the one named <code> make </code>. By convention, creation procedure names begin with the word " <code> make </code>".
The creators part of a class declares a procedure as being a creation procedure. In this case the procedure in question is the one named <code>make</code>. By convention, creation procedure names begin with the word " <code>make</code>".
Let's take a quick look at object creation. Consider this declaration:
<code>
my_list_of_cats: OLD_FASHION_LIST [CAT]
my_list_of_cats: OLD_FASHION_LIST [CAT]
</code>
Here the entity <code> my_list_of_cats </code> can be attached to an object of type <code> OLD_FASHION_LIST </code> [ <code> CAT </code>] at runtime. The process of converting <code> my_list_of_cats </code> from holding a void reference to holding a reference to a object modeling a list of cats, starts when a creation instruction is executed. The creation instruction creates the instance and may apply a creation procedure to initialize the instance. A creation instruction for the declaration above would look like this:
Here the entity <code>my_list_of_cats</code> can be attached to an object of type <code>OLD_FASHION_LIST [CAT]</code> at runtime. The process of converting <code>my_list_of_cats</code> from holding a void reference to holding a reference to a object modeling a list of cats, starts when a creation instruction is executed. The creation instruction creates the instance and may apply a creation procedure to initialize the instance. A creation instruction for the declaration above would look like this:
<code>
create my_list_of_cats.make
create my_list_of_cats.make
</code>
The <code> create </code> keyword is used to introduce a creation instruction. This instruction causes the following four things to happen:
* A shell of a new instance of <code> OLD_FASHION_LIST </code> [ <code> CAT </code>] is created in memory with a memory field for every attribute
The <code>create</code> keyword is used to introduce a creation instruction. This instruction causes the following four things to happen:
* A shell of a new instance of <code>OLD_FASHION_LIST [CAT]</code> is created in memory with a memory field for every attribute
* Each field is initialized with standard default values
** False for type <code> BOOLEAN </code>
** Null character for type <code> CHARACTER </code>
** False for type <code>BOOLEAN</code>
** Null character for type <code>CHARACTER</code>
** The appropriate form of zero for number types
** <code> Void </code> for reference types
** <code>Void</code> for reference types
* Attach the new instance to the entity <code> my_list_of_cats </code>
* Apply the creation procedure <code> make </code>
* Attach the new instance to the entity <code>my_list_of_cats</code>
* Apply the creation procedure <code>make</code>
Once these steps complete successfully, <code> my_list_of_cats </code> will be attached to a valid instance (i.e., an instance in which the class invariant is true) of <code> OLD_FASHIONED_LIST </code> [ <code> CAT </code>].
Once these steps complete successfully, <code>my_list_of_cats</code> will be attached to a valid instance (i.e., an instance in which the class invariant is true) of <code>OLD_FASHIONED_LIST [CAT]</code>.
===Features===
<code>
feature -- Initialization
make is
-- Create an empty list.
do
before := True
ensure is_before: before
end
make
-- Create an empty list.
do
before := True
ensure
is_before: before
end
feature -- Access
item: G is
-- Current item
do
Result := active.item
end
item: G
-- Current item
do
Result := active.item
end
first: like item is
-- Item at first position
do
Result := first_element.item
end
first: like item
-- Item at first position
do
Result := first_element.item
end
</code>
The features part of a class is the area in which we feel that most of the "programming" is done. It is here that we define those things that instances of a class have and can do. We will learn more about features in the next section [[Adding Class Features|Adding Class Features]] .
Until then let's just take a quick look at how features fit into a class. Notice that in our example the features part is introduced by the keyword " <code> feature </code>". In fact there are two occurrences of <code> feature </code> in this example, each followed by a comment.
Until then let's just take a quick look at how features fit into a class. Notice that in our example the features part is introduced by the keyword "<code>feature</code>". In fact there are two occurrences of <code>feature</code> in this example, each followed by a comment.
You may declare multiple <code> feature </code> statements. This helps you group features in a manner that makes sense. Here we see the first group contains those features which are listed as creation procedures in the creators part of the class. The second group of features labeled " <code> Access </code>" contains a set of queries available to clients of the class.
You may declare multiple <code>feature</code> statements. This helps you group features in a manner that makes sense. Here we see the first group contains those features which are listed as creation procedures in the creators part of the class. The second group of features labeled "<code>Access</code>" contains a set of queries available to clients of the class.
Although the words " <code> Initialization </code>" and " <code> Access </code>" are actually in comments after the <code> feature </code> keyword, some language processing tools apply some significance to these, for example, ordering the groups in "pretty-printed" views of a class. Also, some tools allow you to build templates for creating new classes which have <code> feature </code> clauses already in place for predetermined groups.
Although the words "<code>Initialization</code>" and "<code>Access</code>" are actually in comments after the <code>feature</code> keyword, some language processing tools apply some significance to these, for example, ordering the groups in "pretty-printed" views of a class. Also, some tools allow you to build templates for creating new classes which have <code>feature</code> clauses already in place for predetermined groups.
{{tip|There is not a technical requirement governing the grouping or ordering of features in a class. It is the option of the producer of a class to group and order the features in a fashion that holds some meaning. Many years of Eiffel development experience are reflected in the classes in the Eiffel Base Library. This is a good place to look for examples of well constructed classes. }}
{{tip|There is not a technical requirement governing the grouping or ordering of features in a class. It is the option of the producer of a class to group and order the features in a fashion that holds some meaning. Many years of Eiffel development experience are reflected in the classes in the EiffelBase Library. This is a good place to look for examples of well constructed classes. }}
===Invariant===
<code>
invariant
before_constraint: before implies (active = first_element)
after_constraint: after implies (active = last_element)
before_constraint: before implies (active = first_element)
after_constraint: after implies (active = last_element)
</code>
Here's the last word in a class definition ... both literally and figuratively. The invariant part, introduced not surprisingly by the keyword " <code> invariant </code>", is that portion of the class in which we can state what it means for an object to be a valid instance of this class.
Here's the last word in a class definition ... both literally and figuratively. The invariant part, introduced not surprisingly by the keyword "<code>invariant</code>", is that portion of the class in which we can state what it means for an object to be a valid instance of this class.
We will learn more about class invariants in the section titled [[Design by Contract and Assertions|Design by Contract and Assertions]] .

View File

@@ -3,81 +3,76 @@
[[Property:uuid|3a0bfe10-78e7-00eb-d9a0-b977d1fa352a]]
We got a very short introduction to generic classes when we were looking at the formal generic part of class structure in [[Eiffel Classes|Eiffel Classes]] . That discussion left to the imagination the motivation and benefits for creating generic classes.
You will see that most of the generic classes model containers for multiple items and at least one of their formal generic parameters represents the type of items that are to be stored in the container. Some generic classes, like <code> LINKABLE </code>, care for only one instance of the type represented by their formal generic parameter.
You will see that most of the generic classes model containers for multiple items and at least one of their formal generic parameters represents the type of items that are to be stored in the container. Some generic classes, like <code>LINKABLE</code>, care for only one instance of the type represented by their formal generic parameter.
==Motivation==
Imagine that a software producer is planning to build a class which would represent a list of things. Some one might ask "What kinds of things?" To which the producer would reply, "Just things. I want my list to be usable for all kinds of things."
Imagine that a software producer is planning to build a class which would represent a list of things. Someone might ask "What kinds of things?" To which the producer would reply, "Just things. I want my list to be usable for all kinds of things."
Using the idea of polymorphic attachment that we learned in [[Inheritance|Inheritance]] , the producer could build such a class. It might have a query <code> item </code> which would return the thing from the list to which a cursor currently points. It might have a command <code> put </code> which would enter some new thing into the list.
Using the idea of polymorphic attachment that we learned in [[Inheritance|Inheritance]] , the producer could build such a class. It might have a query <code>item</code> which would return the thing from the list to which a cursor currently points. It might have a command <code>put</code> which would enter some new thing into the list.
What would be the type of <code> item </code>? And what would be the type of the argument to <code> put </code>?
What would be the type of <code>item</code>? And what would be the type of the argument to <code>put</code>?
If the producer wants the class to handle all kinds of things, then the answer must be class <code> ANY </code>, the class from which all others inherit.
If the producer wants the class to handle all kinds of things, then the answer must be class <code>ANY</code>, the class from which all others inherit.
<code>
class
LIST_OF_THINGS
.
.
LIST_OF_THINGS
...
feature -- Access
item: ANY
-- The thing currently pointed to by cursor
item: ANY
-- The thing currently pointed to by cursor
.
...
feature -- Element change
put (new_item: ANY)
-- Add `new_item' at the end of the list
.
.
put (new_item: ANY)
-- Add `new_item' at the end of the list
...
</code>
This will work, but has some definite disadvantages. Suppose you choose to use this class to maintain a list of cats in one of your classes. You might make this declaration:
<code>
my_cats: LIST_OF_THINGS
-- A list of my cats
my_cats: LIST_OF_THINGS
-- A list of my cats
</code>
Then you could add individual instances to the list:
<code>
fluffy, twinkie: CAT
.
.
my_cats.put (fluffy)
my_cats.put (twinkie)
fluffy, twinkie: CAT
...
my_cats.put (fluffy)
my_cats.put (twinkie)
</code>
One problem with this type of list is that the type system will not help you keep from doing something pathological like:
<code>
fluffy, twinkie: CAT
thor: PSYCHOTIC_HYDROPHOBIC_CAT_HATING_DOG
.
.
my_cats.put (fluffy)
my_cats.put (twinkie)
my_cats.put (thor)
fluffy, twinkie: CAT
thor: PSYCHOTIC_HYDROPHOBIC_CAT_HATING_DOG
-- A very nasty dog
...
my_cats.put (fluffy)
my_cats.put (twinkie)
my_cats.put (thor)
</code>
Another problem is that to do any <code>CAT</code> things with an item in the list, you must reattach it to a <code>CAT</code> entity. The following is invalid.
<code>
my_cats.item.purr -- Is invalid
my_cats.item.purr -- Is invalid
</code>
This is because "item" is type <code>ANY</code> and although it may be currently attached to an instance of <code>CAT</code>, the static typing system cannot guarantee that. So you must use assignment attempt as we saw in the polymorphism example in [[Inheritance|Inheritance]] .
<code>
some_cat: CAT
.
.
some_cat ?= my_cats.item
if some_cat /= Void then
some_cat.purr
end
some_cat: CAT
...
some_cat ?= my_cats.item
if some_cat /= Void then
some_cat.purr
end
</code>
You can see that this type of list has its drawbacks. Of course you could build a <code> LIST_OF_CATS </code> class in which <code> item </code> and the argument for <code> put </code> would be of type <code> CAT </code>. This would let you <code> purr </code> a cat without pulling it out of the list, and it would also prevent you from accidently letting old Thor in with the cats. But, every time you needed a list to hold a different type of object, you have to write a new class.
You can see that this type of list has its drawbacks. Of course you could build a <code>LIST_OF_CATS</code> class in which <code>item</code> and the argument for <code>put</code> would be of type <code>CAT</code>. This would let you <code>purr</code> a cat without pulling it out of the list, and it would also prevent you from accidently letting old Thor in with the cats. But, every time you needed a list to hold a different type of object, you have to write a new class.
Indeed, this is how things are done in environements without facilities genericity.
@@ -85,74 +80,68 @@ What we would like to have is a way to produce the text of the list class once.
==Basic Genericity==
In Eiffel this is accomplished through generic classes. Generic classes are written relative not to a specific class but to a kind of phony class name called a formal generic parameter. With genericity, the <code> LIST_OF_THINGS </code> class might become a class called <code> LIST </code> which is a list of items of type <code> G </code>. In class <code> LIST </code> we would declare <code> item </code> as type G, as well as the argument to <code> put </code>.
In Eiffel this is accomplished through generic classes. Generic classes are written relative not to a specific class but to a kind of phony class name called a formal generic parameter. With genericity, the <code>LIST_OF_THINGS</code> class might become a class called <code>LIST</code> which is a list of items of type <code>G</code>. In class <code>LIST</code> we would declare <code>item</code> as type G, as well as the argument to <code>put</code>.
<code>
class
LIST [G]
.
.
LIST [G]
...
feature -- Access
item: G
-- The item currently pointed to by cursor
.
item: G
-- The item currently pointed to by cursor
...
feature -- Element change
put (new_item: G)
-- Add `new_item' at the end of the list
.
.
put (new_item: G)
-- Add `new_item' at the end of the list
...
</code>
We could declare feature <code> my_cats </code> as a <code> LIST </code> of items of type <code> CAT </code>. By doing so we are providing <code> CAT </code> as an "actual generic parameter" in the declaration. Then we are free to treat the features of <code> LIST </code> as if the class name <code> CAT </code> had been substituted for every occurrence of the formal generic parameter <code> G </code>.
We could declare feature <code>my_cats</code> as a <code>LIST</code> of items of type <code>CAT</code>. By doing so we are providing <code>CAT</code> as an "actual generic parameter" in the declaration. Then we are free to treat the features of <code>LIST</code> as if the class name <code>CAT</code> had been substituted for every occurrence of the formal generic parameter <code>G</code>.
<code>
my_cats: LIST [CAT]
-- A list of my cats
fluffy, twinkie: CAT
.
.
my_cats.put (fluffy)
my_cats.put (twinkie)
.
my_cats.item.purr -- Valid now
my_cats: LIST [CAT]
-- A list of my cats
fluffy, twinkie: CAT
...
my_cats.put (fluffy)
my_cats.put (twinkie)
...
my_cats.item.purr -- Valid now
</code>
The following would no longer be valid:
<code>
my_cats: LIST [CAT]
-- A list of my cats
my_cats: LIST [CAT]
-- A list of my cats
thor: PSYCHOTIC_HYDROPHOBIC_CAT_HATING_DOG
.
.
my_cats.put (thor) -- Is invalid
thor: PSYCHOTIC_HYDROPHOBIC_CAT_HATING_DOG
...
my_cats.put (thor) -- Is invalid
</code>
==Constrained Genericity==
The generic class <code> LIST </code> illustrated above is perfectly useful for making typed lists of any type of object. The features of the <code> LIST </code> will not attempt to use the objects in the list in any way. Sometimes though, it is important for a class to be guaranteed more about the nature of the types that can be substituted for its formal generic parameter.
The generic class <code>LIST</code> illustrated above is perfectly useful for making typed lists of any type of object. The features of the <code>LIST</code> will not attempt to use the objects in the list in any way. Sometimes though, it is important for a class to be guaranteed more about the nature of the types that can be substituted for its formal generic parameter.
Take for example the case of a class called <code> SORTED_LIST </code>. A <code> SORTED_LIST </code> is a list, of course, but it is special in that it acts upon the elements that it holds to keep them in order.
Take for example the case of a class called <code>SORTED_LIST</code>. A <code>SORTED_LIST</code> is a list, of course, but it is special in that it acts upon the elements that it holds to keep them in order.
A <code> SORTED_LIST </code> needs to be able to order its elements. So, it must be able to apply queries to those elements to determine which should sort high than which. The elements themselves must respond to ordering operations.
A <code>SORTED_LIST</code> needs to be able to order its elements. So, it must be able to apply queries to those elements to determine which should sort high than which. The elements themselves must respond to ordering operations.
If <code> SORTED_LIST </code> were defined like we did <code> LIST </code>
If <code>SORTED_LIST</code> were defined like we did <code>LIST</code>
<code>
class
SORTED_LIST [G]
SORTED_LIST [G]
</code>
there would be no guarantee that ordering operations, like "<" and ">" could be applied to all types that could be listed. An Eiffel facility called "constrained genericity" will solve this problemfor us. In the case of <code> SORTED_LIST </code>, we would add to the formal generic part as follows.
there would be no guarantee that ordering operations, like "<" and ">" could be applied to all types that could be listed. An Eiffel facility called "constrained genericity" will solve this problemfor us. In the case of <code>SORTED_LIST</code>, we would add to the formal generic part as follows.
<code>
class
SORTED_LIST [G -> COMPARABLE]
SORTED_LIST [G -> COMPARABLE]
</code>
You may remember from [[Inheritance|Inheritance]] that if we make instances of a class comparable with each other, then we make the class inherit from <code> COMPARABLE </code> and effect the feature "<".
You may remember from [[Inheritance|Inheritance]] that if we make instances of a class comparable with each other, then we make the class inherit from <code>COMPARABLE</code> and effect the feature "<".
Here, constrained genericity does two things for us.
* First, it states that any candidate for substitution for <code> G </code> must conform to class <code> COMPARABLE </code>. Typically this means it must inherit from <code> COMPARABLE </code>.
* Second, it allows, within the features of <code> SORTED_LIST </code>, the features of <code> COMPARABLE </code> to be applied to any item which has a type of <code> G </code>.
* First, it states that any candidate for substitution for <code>G</code> must conform to class <code>COMPARABLE</code>. Typically this means it must inherit from <code>COMPARABLE</code>.
* Second, it allows, within the features of <code>SORTED_LIST</code>, the features of <code>COMPARABLE</code> to be applied to any item which has a type of <code>G</code>.

View File

@@ -11,9 +11,9 @@ Inheritance allows us to make extensions and adaptations to existing software, w
==The Eiffel Inheritance Model==
If class <code> B </code> inherits from class <code> A </code>, then:
* Every feature of <code> A </code> is also a feature of <code> B </code>
* In any case in which an instance of <code> A </code> is called for, then an instance of <code> B </code> will suffice.
If class <code>B</code> inherits from class <code>A</code>, then:
* Every feature of <code>A</code> is also a feature of <code>B</code>
* In any case in which an instance of <code>A</code> is called for, then an instance of <code>B</code> will suffice.
Flexibility and adaptability are key qualities of the Eiffel inheritance model. On an informal level, this means that, except as prevented by certain constraints, a class can inherit from a set of classes containing just about any other classes.
@@ -23,51 +23,51 @@ If a class is deferred, then it is not completely implemented. A class is deferr
What this means to us as software producers, is that in any development effort, we have available a great number of classes which can serve as potential starting points. That is, classes that we could make parents to the classes we produce. And, those classes do not have to chosen from a strict dichotomy of classes which are either completely abstract or completely implemented. Inheritance from classes that are deferred but have some implemented features is both possible and encouraged. It reuses existing software and it reduces the opportunity for error.
Consider the deferred class <code> COMPARABLE </code> from the Eiffel Base Library. A portion of <code> COMPARABLE </code> is shown below:
Consider the deferred class <code>COMPARABLE</code> from the Eiffel Base Library. A portion of <code>COMPARABLE</code> is shown below:
<code>
deferred class
COMPARABLE
COMPARABLE
feature -- Comparison
infix "<" (other: like Current): BOOLEAN is
-- Is current object less than `other'?
deferred
end
infix "<" (other: like Current): BOOLEAN
-- Is current object less than `other'?
deferred
end
infix "<=" (other: like Current): BOOLEAN is
-- Is current object less than or equal to `other'?
do
Result := not (other < Current)
end
infix "<=" (other: like Current): BOOLEAN
-- Is current object less than or equal to `other'?
do
Result := not (other < Current)
end
infix ">" (other: like Current): BOOLEAN is
-- Is current object greater than `other'?
do
Result := other < Current
end
infix ">" (other: like Current): BOOLEAN
-- Is current object greater than `other'?
do
Result := other < Current
end
infix ">=" (other: like Current): BOOLEAN is
-- Is current object greater than or equal to `other'?
do
Result := not (Current < other)
end
infix ">=" (other: like Current): BOOLEAN
-- Is current object greater than or equal to `other'?
do
Result := not (Current < other)
end
is_equal (other: like Current): BOOLEAN is
-- Is `other' attached to an object of the same type
-- as current object and identical to it?
do
Result := (not (Current < other) and not (other < Current))
end
is_equal (other: like Current): BOOLEAN
-- Is `other' attached to an object of the same type
-- as current object and identical to it?
do
Result := (not (Current < other) and not (other < Current))
end
</code>
If you are producing a class that you wish to support basic comparison operators, like "<" and ">", you can have that class inherit from <code> COMPARABLE </code>, which has features which correspond to those operators. The text for <code> COMPARABLE </code> contains eight features. Seven of these are effective and one is deferred.
If you are producing a class that you wish to support basic comparison operators, like "<" and ">", you can have that class inherit from <code>COMPARABLE</code>, which has features which correspond to those operators. The text for <code>COMPARABLE</code> contains eight features. Seven of these are effective and one is deferred.
So through inheritance from <code> COMPARABLE </code>, your class, let's call it <code> WHATZIT </code>, would now have these features available. But how would the features of <code> COMPARABLE </code> know what it means to compare <code> WHATZIT </code>s?
So through inheritance from <code>COMPARABLE</code>, your class, let's call it <code>WHATZIT</code>, would now have these features available. But how would the features of <code>COMPARABLE</code> know what it means to compare <code>WHATZIT</code>s?
Of course, it would have no way of knowing, so you must show it. And you do that by writing the implementation for "<", the one deferred feature that <code> WHATZIT </code> inherits from the <code> COMPARABLE </code> class.
Of course, it would have no way of knowing, so you must show it. And you do that by writing the implementation for "<", the one deferred feature that <code>WHATZIT</code> inherits from the <code>COMPARABLE</code> class.
When you look closely at the effective features of <code> COMPARABLE </code>, you see that their implementations are ultimately based on "<". If we were not able to inherit from multiple partially implemented classes, then we would be forced to implement many more features, a process which invites error, or, in the case of comparison, to move to a less appealing model.
When you look closely at the effective features of <code>COMPARABLE</code>, you see that their implementations are ultimately based on "<". If we were not able to inherit from multiple partially implemented classes, then we would be forced to implement many more features, a process which invites error, or, in the case of comparison, to move to a less appealing model.
==The Inheritance Part of Classes in Eiffel==
@@ -77,101 +77,101 @@ Feature adaptation is an enabling capability, but it is also one that takes some
We will look at the types of feature adaptation that will serve most useful to you as you begin to produce Eiffel software.
In [[Eiffel Classes|Eiffel Classes]] you saw where the inheritance part fits into the class structure. Shown below is a portion of class <code> LINKED_QUEUE </code> from the Eiffel libraries. <code> LINKED_QUEUE </code> is an effective class which implements the abstract notion of a <code> QUEUE </code> (a deferred class) with an implementation based on the services provided by <code> LINKED_LIST </code> (an effective class).
In [[Eiffel Classes|Eiffel Classes]] you saw where the inheritance part fits into the class structure. Shown below is a portion of class <code>LINKED_QUEUE</code> from the Eiffel libraries. <code>LINKED_QUEUE</code> is an effective class which implements the abstract notion of a <code>QUEUE</code> (a deferred class) with an implementation based on the services provided by <code>LINKED_LIST</code> (an effective class).
<code>
class
LINKED_QUEUE [G]
LINKED_QUEUE [G]
inherit
QUEUE [G]
undefine
is_empty,
copy,
is_equal
redefine
linear_representation,
prune_all,
extend
select
item,
put
end
LINKED_LIST [G]
rename
item as ll_item,
remove as ll_remove,
make as ll_make,
remove_left as remove,
put as ll_put
export
{NONE}
all
{ANY}
writable,
extendible,
wipe_out,
readable
undefine
fill,
append,
prune,
readable,
writable,
prune_all,
extend,
force,
is_inserted
redefine
duplicate,
linear_representation
select
remove
end
QUEUE [G]
undefine
is_empty,
copy,
is_equal
redefine
linear_representation,
prune_all,
extend
select
item,
put
end
LINKED_LIST [G]
rename
item as ll_item,
remove as ll_remove,
make as ll_make,
remove_left as remove,
put as ll_put
export
{NONE}
all
{ANY}
writable,
extendible,
wipe_out,
readable
undefine
fill,
append,
prune,
readable,
writable,
prune_all,
extend,
force,
is_inserted
redefine
duplicate,
linear_representation
select
remove
end
</code>
Okay ... now calm down ... please. This is an example from a very highly-evolved and sophisticated library which is replete with software reuse. <code> LINKED_QUEUE </code> has two parents and uses considerable feature adaptation. In fact, it uses every feature adaptation option available. The benefit is obvious, though. <code> LINKED_QUEUE </code> class has only seven features actually coded. In total there are only 26 lines of instructions!
Okay ... now calm down ... please. This is an example from a very highly-evolved and sophisticated library which is replete with software reuse. <code>LINKED_QUEUE</code> has two parents and uses considerable feature adaptation. In fact, it uses every feature adaptation option available. The benefit is obvious, though. <code>LINKED_QUEUE</code> class has only seven features actually coded. In total there are only 26 lines of instructions!
In practice you can use inheritance, even multiple inheritance, to do some quite productive programming in Eiffel without having to write anything that looks like the inheritance part of <code> LINKED_QUEUE </code> above.
In practice you can use inheritance, even multiple inheritance, to do some quite productive programming in Eiffel without having to write anything that looks like the inheritance part of <code>LINKED_QUEUE</code> above.
Regardless, let's break <code> LINKED_QUEUE </code>'s inheritance part into chunks and take a look at some of them.
Regardless, let's break <code>LINKED_QUEUE</code>'s inheritance part into chunks and take a look at some of them.
===Rename===
<code>
rename
item as ll_item,
remove as ll_remove,
make as ll_make,
remove_left as remove,
put as ll_put
rename
item as ll_item,
remove as ll_remove,
make as ll_make,
remove_left as remove,
put as ll_put
</code>
As you might have already guessed, the rename part, introduced oddly enough by the keyword " <code> rename </code>", is used to rename features.
As you might have already guessed, the rename part, introduced oddly enough by the keyword "<code>rename</code>", is used to rename features.
Specifically, it is used when an heir wants to use a feature from a parent, but wants to use it under a different name than that by which the parent knows it. So in the example, the feature known as <code> item </code> in <code> LINKED_LIST </code> is perfectly usable in <code> LINKED_QUEUE </code>, but must be applied as <code> ll_item </code>.
Specifically, it is used when an heir wants to use a feature from a parent, but wants to use it under a different name than that by which the parent knows it. So in the example, the feature known as <code>item</code> in <code>LINKED_LIST</code> is perfectly usable in <code>LINKED_QUEUE</code>, but must be applied as <code>ll_item</code>.
This is common when your class inherits two different features with the same name from two different parents and you want to be able to use them both. Because you can only have one feature with a given name, then rename one of the features.
===New Exports===
<code>
export
{NONE}
all
{ANY}
writable,
extendible,
wipe_out,
readable
export
{NONE}
all
{ANY}
writable,
extendible,
wipe_out,
readable
</code>
The new exports part is introduced by the keyword " <code> export </code>". This section allows you to change the export status of inherited features. Remember from [[Adding Class Features|Adding Class Features]] that features become available (or not) to clients by their export status. Export status of immediate features is controlled in the feature clause. But here we are dealing with inherited features, so we control their status in the export part of the class's inheritance section. Any feature not mentioned will have the same export status as it did in the parent class.
The new exports part is introduced by the keyword "<code>export</code>". This section allows you to change the export status of inherited features. Remember from [[Adding Class Features|Adding Class Features]] that features become available (or not) to clients by their export status. Export status of immediate features is controlled in the feature clause. But here we are dealing with inherited features, so we control their status in the export part of the class's inheritance section. Any feature not mentioned will have the same export status as it did in the parent class.
In this example, the keyword " <code> all </code>" is used first to say that all features inherited form <code> LINKED_LIST </code> are unavailable to any clients (export to class <code> NONE </code>). This is typical for a class like <code> LINKED_QUEUE </code> in which the features important to the client come from the deferred parent, in this case <code> QUEUE </code>, and the class <code> LINKED_LIST </code> is used only for implementation. But, it seems that also in this case, the producer felt differently about the features <code> writable </code>, <code> extendible </code>, <code> wipe_out </code>, and <code> readable </code>, and decided the allow clients of <code> ANY </code> type to utilize these features inherited from <code> LINKED_LIST </code>.
In this example, the keyword "<code>all</code>" is used first to say that all features inherited form <code>LINKED_LIST</code> are unavailable to any clients (export to class <code>NONE</code>). This is typical for a class like <code>LINKED_QUEUE</code> in which the features important to the client come from the deferred parent, in this case <code>QUEUE</code>, and the class <code>LINKED_LIST</code> is used only for implementation. But, it seems that also in this case, the producer felt differently about the features <code>writable</code>, <code>extendible</code>, <code>wipe_out</code>, and <code>readable</code>, and decided the allow clients of <code>ANY</code> type to utilize these features inherited from <code>LINKED_LIST</code>.
===Undefine===
<code>
undefine
is_empty,
copy,
is_equal
undefine
is_empty,
copy,
is_equal
</code>
Next, undefine ... it's probably not what you think. You might assume that undefine is a way to banish forever any inherited features that you just do not want to deal with. But what happens to features whose names are listed in an undefine clause is that they become deferred features in the heir.
@@ -182,25 +182,25 @@ Another way you might use undefine is in the case in which you actually want a f
===Redefine===
<code>
redefine
linear_representation,
prune_all,
extend
redefine
linear_representation,
prune_all,
extend
</code>
The redefine part lists the names of effective features for which the producer of the heir class would like to provide implementations that replace the inherited implementations.
So, in this example the implementation for <code> linear_representation </code>, for example, that <code> LINKED_QUEUE </code> would have inherited from <code> QUEUE </code> will not be used. Instead <code> LINKED_QUEUE </code> implements its own version of <code> linear_representation </code>.
So, in this example the implementation for <code>linear_representation</code>, for example, that <code>LINKED_QUEUE</code> would have inherited from <code>QUEUE</code> will not be used. Instead <code>LINKED_QUEUE</code> implements its own version of <code>linear_representation</code>.
{{note|When a class implements a version of an inherited feature which was deferred in its parent, this is known as "effecting" the feature. Because features being effected are getting their first implementation, it is not necessary to list their names in the redefine part, or anywhere else in the inheritance part of the heir. }}
===Select===
<code>
select
remove
select
remove
</code>
The select part is used only under special circumstances. The case in which select is required involves a situation called "repeated" inheritance. Repeated inheritance occurs when an heir inherits more than once from the same ancestor. Usually this means it has two or more parents who have a common proper ancestor (but it can occur directly). The features from the common ancestor are inherited by each of the parents and passed on to the heir. The rules and effects of repeated inheritance occupy an entire chapter in the official Eiffel language reference and will not be reproduced here. Justunderstand at this point that it is sometimes necessary to use <code> select </code> to provide the dynamic binding system with an unambiguous choice of features in the presence of polymorphic attachment.
The select part is used only under special circumstances. The case in which select is required involves a situation called "repeated" inheritance. Repeated inheritance occurs when an heir inherits more than once from the same ancestor. Usually this means it has two or more parents who have a common proper ancestor (but it can occur directly). The features from the common ancestor are inherited by each of the parents and passed on to the heir. The rules and effects of repeated inheritance occupy an entire chapter in the official Eiffel language reference and will not be reproduced here. Justunderstand at this point that it is sometimes necessary to use <code>select</code> to provide the dynamic binding system with an unambiguous choice of features in the presence of polymorphic attachment.
You should note also that repeated inheritance can and does occur often without causing any problem at all. In fact it happens in every case of multiple inheritance, due to the fact that all classes inherit from class ANY and receive its features as a result. The reason it is not a problem is that in the case that any feature makes it from the original common ancestor along multiple paths to the heir with its name and implementation still intact, it will arrive as only one feature heir. This is called sharing and nothing special needs to be done to make it happen.
@@ -208,144 +208,145 @@ You should note also that repeated inheritance can and does occur often without
It is time now to see another way in which inheritance helps build more extendible software.
Assume that we have to build classes that model different types of polygons. We would do this by building a class for polygon which would model a garden-variety polygon, a multi-sided closed figure. But when we consider that there are specialized types of polygons, like triangles and rectangles, we realize that to support these specializations, we need classes for them as well. And this is an obvious opportunity for inheritance. All triangles and rectangles are polygons. So, we start with class <code> POLYGON </code> and its proper descendants <code> TRIANGLE </code> and <code> RECTANGLE </code>.
Assume that we have to build classes that model different types of polygons. We would do this by building a class for polygon which would model a garden-variety polygon, a multi-sided closed figure. But when we consider that there are specialized types of polygons, like triangles and rectangles, we realize that to support these specializations, we need classes for them as well. And this is an obvious opportunity for inheritance. All triangles and rectangles are polygons. So, we start with class <code>POLYGON</code> and its proper descendants <code>TRIANGLE</code> and <code>RECTANGLE</code>.
So we can make declarations like:
<code>
my_polygon: POLYGON
your_polygon: POLYGON
my_triangle: TRIANGLE
my_rectangle: RECTANGLE
another_rectangle: RECTANGLE
my_polygon: POLYGON
your_polygon: POLYGON
my_triangle: TRIANGLE
my_rectangle: RECTANGLE
another_rectangle: RECTANGLE
</code>
Assume these declarations are in force for all the examples this section on polymorphism.
We saw in [[Adding Class Features|Adding Class Features]] that we can say that one class conforms to another if it is the same class or one of its proper descendants. Therefore POLYGON conforms to <code> POLYGON </code>. Also, <code> TRIANGLE </code> and <code> RECTANGLE </code> conform to <code> POLYGON </code>. But, importantly, <code> POLYGON </code> does not conform to <code> TRIANGLE </code> or <code> RECTANGLE </code>. This makes sense intuitively, because we know all rectangles and triangles are polygons ... and we also know that not all polygons are rectangles.
We saw in [[Adding Class Features|Adding Class Features]] that we can say that one class conforms to another if it is the same class or one of its proper descendants. Therefore POLYGON conforms to <code>POLYGON</code>. Also, <code>TRIANGLE</code> and <code>RECTANGLE</code> conform to <code>POLYGON</code>. But, importantly, <code>POLYGON</code> does not conform to <code>TRIANGLE</code> or <code>RECTANGLE</code>. This makes sense intuitively, because we know all rectangles and triangles are polygons ... and we also know that not all polygons are rectangles.
===Polymorphic Attachment===
These facts affect how assignments can work. Using the declarations above:
<code>
my_polygon := your_polygon -- Is valid
your_polygon :=my_polygon -- Is valid
my_polygon :=my_rectangle -- Is valid
my_polygon := my_triangle -- Is valid
my_polygon := your_polygon -- Is valid
your_polygon :=my_polygon -- Is valid
my_polygon :=my_rectangle -- Is valid
my_polygon := my_triangle -- Is valid
</code>
but
<code>
my_rectangle := my_polygon -- Is not valid
my_triangle := my_polygon -- Is not valid
my_rectangle := my_polygon -- Is not valid
my_triangle := my_polygon -- Is not valid
</code>
and of course
<code>
my_rectangle := my_triangle -- Is not valid
my_rectangle := my_triangle -- Is not valid
</code>
Consider now the assignment below which is valid.
<code>
my_polygon := my_rectangle
my_polygon := my_rectangle
</code>
After an assignment like this executes the entity <code> my_polygon </code> will be holding at runtime a reference to an instance of a type which is not a direct instance of its declared type <code> POLYGON </code>. But conformance ensures us that, although it may not be a direct instance, it will indeed by an instance. (all rectangles are polygons).
After an assignment like this executes the entity <code>my_polygon</code> will be holding at runtime a reference to an instance of a type which is not a direct instance of its declared type <code>POLYGON</code>. But conformance ensures us that, although it may not be a direct instance, it will indeed by an instance. (all rectangles are polygons).
Depending upon how many different types of polygons get modeled in classes, the entity " <code> my_polygon </code>" could be attached objects of may different types ... it could take on many forms. This in fact is the basis for the term "polymorphism"; having many forms. So we speak of "polymorphic attachment" as the process by which at runtime entities can hold references to objects which are not of the entity's declared type ... but they are of conforming types.
Depending upon how many different types of polygons get modeled in classes, the entity "<code>my_polygon</code>" could be attached objects of may different types ... it could take on many forms. This in fact is the basis for the term "polymorphism"; having many forms. So we speak of "polymorphic attachment" as the process by which at runtime entities can hold references to objects which are not of the entity's declared type ... but they are of conforming types.
Now let's see how we get some value from this.
===Dynamic Binding===
Suppose that one of the features of <code> POLYGON </code> is a query <code> perimeter </code> which returns an instance's perimeter. The producer of <code> POLYGON </code> may have implemented <code> perimeter </code> as a function that computes the perimeter by adding up the lengths of all the sides. This approach is guaranteed to work for all polygons, and we can apply the <code> perimeter </code> feature to any polygon. Let's print some perimeters:
Suppose that one of the features of <code>POLYGON</code> is a query <code>perimeter</code> which returns an instance's perimeter. The producer of <code>POLYGON</code> may have implemented <code>perimeter</code> as a function that computes the perimeter by adding up the lengths of all the sides. This approach is guaranteed to work for all polygons, and we can apply the <code>perimeter</code> feature to any polygon. Let's print some perimeters:
<code>
print (my_polygon.perimeter)
print (my_triangle.perimeter)
print (my_rectangle.perimeter)
print (my_polygon.perimeter)
print (my_triangle.perimeter)
print (my_rectangle.perimeter)
</code>
<code> TRIANGLE </code> and <code> RECTANGLE </code> might have properties, expressed as queries, which as a part of their specialization, distinguish them from run-of-the-mill polygons. Two features of rectangles are <code> width </code> and <code> height </code> the lengths of the sides.
<code>TRIANGLE</code> and <code>RECTANGLE</code> might have properties, expressed as queries, which as a part of their specialization, distinguish them from run-of-the-mill polygons. Two features of rectangles are <code>width</code> and <code>height</code> the lengths of the sides.
Armed with these <code> RECTANGLE </code>-specific features, the producer of <code> RECTANGLE </code> may say,"Now I no longer have to depend upon that crude implementation of <code> perimeter </code> that is inherited from <code> POLYGON </code>. I can build an efficient <code> RECTANGLE </code>-specific implementation of <code> perimeter </code>, based on the knowledge that for all <code> RECTANGLE </code>s perimeter = 2*(width+height)"
Armed with these <code>RECTANGLE</code>-specific features, the producer of <code>RECTANGLE</code> may say, "Now I no longer have to depend upon that crude implementation of <code>perimeter</code> that is inherited from <code>POLYGON</code>. I can build an efficient <code>RECTANGLE</code>-specific implementation of <code>perimeter</code>, based on the knowledge that for all <code>RECTANGLE</code>s perimeter = 2*(width+height)"
To implement this specialized version of <code> perimeter </code>, the producer of <code> RECTANGLE </code> must add the feature to the class, but also must list its name in the " <code> redefine </code>" part of the <code> RECTANGLE </code>'s inheritance clause.
To implement this specialized version of <code>perimeter</code>, the producer of <code>RECTANGLE</code> must add the feature to the class, but also must list its name in the "<code>redefine</code>" part of the <code>RECTANGLE</code>'s inheritance clause.
<code>
class RECTANGLE
class
RECTANGLE
inherit
POLYGON
redefine
perimeter
end
POLYGON
redefine
perimeter
end
.
.
feature
perimeter: REAL is
-- Sum of lengths of all sides
do
Result := 2 * (width + height)
end
perimeter: REAL
-- Sum of lengths of all sides
do
Result := 2 * (width + height)
end
</code>
You would expect then, that this version of perimeter would be executed in the following context:
<code>
print (my_rectangle.perimeter)
print (my_rectangle.perimeter)
</code>
But what makes this interesting is that even in the context below
<code>
my_polygon := my_rectangle
print (my_polygon.perimeter)
my_polygon := my_rectangle
print (my_polygon.perimeter)
</code>
in which <code> perimeter </code> is being applied to a entity declared as <code> POLYGON </code>, the specialized version of <code> perimeter </code> from <code> RECTANGLE </code> is being used. It would be impossible to ensure at compile time which version of <code> perimeter </code> is most appropriate. So it must be done at runtime. This ability to choose the best version of a feature to apply, just at the moment it needs to be applied, is called "dynamic binding".
in which <code>perimeter</code> is being applied to a entity declared as <code>POLYGON</code>, the specialized version of <code>perimeter</code> from <code>RECTANGLE</code> is being used. It would be impossible to ensure at compile time which version of <code>perimeter</code> is most appropriate. So it must be done at runtime. This ability to choose the best version of a feature to apply, just at the moment it needs to be applied, is called "dynamic binding".
Static typing tells us at compile time that it is safe to apply <code> perimeter </code> to <code> my_polygon </code> No matter which of the types of polygons is attached to <code> my_polygon </code>, there will be a <code> perimeter </code> feature that will work.
Static typing tells us at compile time that it is safe to apply <code>perimeter</code> to <code>my_polygon</code> No matter which of the types of polygons is attached to <code>my_polygon</code>, there will be a <code>perimeter</code> feature that will work.
Dynamic binding tells us that when we apply <code> perimeter </code>, we know that the most appropriate version of the feature will get applied at runtime.
Dynamic binding tells us that when we apply <code>perimeter</code>, we know that the most appropriate version of the feature will get applied at runtime.
===Assignment Attempt===
Now let's add another situation. Consider the code below:
<code>
my_polygon := my_rectangle
print (my_polygon.perimeter)
print (my_polygon.width) -- Is invalid
my_polygon := my_rectangle
print (my_polygon.perimeter)
print (my_polygon.width) -- Is invalid
</code>
We could apply <code> perimeter </code> to <code> my_polygon </code> and everything is fine ... we even get <code> RECTANGLE </code>'s specialized version of the feature. But it is invalid for us to try to apply <code> width </code> to <code> my_polygon </code> even though we feel (with rather strong conviction) that at this point in execution, <code> my_polygon </code> will be attached to an object of type <code> RECTANGLE </code>, and we know that <code> width </code> is a valid query on <code> RECTANGLE </code>s.
We could apply <code>perimeter</code> to <code>my_polygon</code> and everything is fine ... we even get <code>RECTANGLE</code>'s specialized version of the feature. But it is invalid for us to try to apply <code>width</code> to <code>my_polygon</code> even though we feel (with rather strong conviction) that at this point in execution, <code>my_polygon</code> will be attached to an object of type <code>RECTANGLE</code>, and we know that <code>width</code> is a valid query on <code>RECTANGLE</code>s.
The reason follows. When we declared <code> my_polygon </code> as type <code> POLYGON </code>, we made a deal that says that the only features that can be applied to <code> my_polygon </code> are the features of <code> POLYGON </code>. Remember that static typing guarantees us at compile time that at runtime there will be at least one version of the feature available that can be applied.
The reason follows. When we declared <code>my_polygon</code> as type <code>POLYGON</code>, we made a deal that says that the only features that can be applied to <code>my_polygon</code> are the features of <code>POLYGON</code>. Remember that static typing guarantees us at compile time that at runtime there will be at least one version of the feature available that can be applied.
<code>
print (my_polygon.width) -- Is invalid
print (my_polygon.width) -- Is invalid
</code>
But in the case above, the guarantee cannot be made. <code> my_polygon </code> is declared with class <code> POLYGON </code> which has no <code> width </code> feature, despite the fact that some of its proper descendants might.
But in the case above, the guarantee cannot be made. <code>my_polygon</code> is declared with class <code>POLYGON</code> which has no <code>width</code> feature, despite the fact that some of its proper descendants might.
Does this mean that we can never do <code> RECTANGLE </code> things with this instance again, once we have attached it to <code> my_polygon </code>?
Does this mean that we can never do <code>RECTANGLE</code> things with this instance again, once we have attached it to <code>my_polygon</code>?
No. There is a language facility called the "assignment attempt" which will come to our rescue. The assignment attempt will allow us safely to attach our instance back to an entity typed as <code> RECTANGLE </code>. After doing so, we are free use <code> RECTANGLE </code> features.
No. There is a language facility called the "assignment attempt" which will come to our rescue. The assignment attempt will allow us safely to attach our instance back to an entity typed as <code>RECTANGLE</code>. After doing so, we are free use <code>RECTANGLE</code> features.
<code>
my_polygon := my_rectangle
print (my_polygon.perimeter)
another_rectangle ?= my_polygon
print (another_rectangle.width)
my_polygon := my_rectangle
print (my_polygon.perimeter)
another_rectangle ?= my_polygon
print (another_rectangle.width)
</code>
The assignment attempt uses the syntax "?=", versus the ":=" of assignment.
This is significant ... as is the name assignment attempt. The reason is that it is possible in some context that <code> my_polygon </code> will be attached to something other than a rectangle when we do this:
This is significant ... as is the name assignment attempt. The reason is that it is possible in some context that <code>my_polygon</code> will be attached to something other than a rectangle when we do this:
<code>
another_rectangle ?= my_polygon
another_rectangle ?= my_polygon
</code>
If it were true that <code> my_polygon </code> were attached to an instance of say <code> TRIANGLE </code> when the assignment attempt above was executed, then <code> another_rectangle </code> would be left as a <code> Void </code> reference. As a consequence, it is prudent to check to see if the attachment has actually been made before trying to apply features. Applying a feature to a <code> Void </code> reference will cause an exception. Almost always, assignment attempt will be used in the following manner:
If it were true that <code>my_polygon</code> were attached to an instance of say <code>TRIANGLE</code> when the assignment attempt above was executed, then <code>another_rectangle</code> would be left as a <code>Void</code> reference. As a consequence, it is prudent to check to see if the attachment has actually been made before trying to apply features. Applying a feature to a <code>Void</code> reference will cause an exception. Almost always, assignment attempt will be used in the following manner:
<code>
my_polygon := my_rectangle
print (my_polygon.perimeter)
another_rectangle ?= my_polygon
if another_rectangle /= Void then -- Make sure assignment happened
print (another_rectangle.width)
end
my_polygon := my_rectangle
print (my_polygon.perimeter)
another_rectangle ?= my_polygon
if another_rectangle /= Void then -- Make sure assignment happened
print (another_rectangle.width)
end
</code>