mirror of
https://github.com/EiffelSoftware/eiffel-org.git
synced 2025-12-07 07:12:25 +01:00
git-svn-id: https://svn.eiffel.com/eiffel-org/trunk@2484 abb3cda0-5349-4a8f-a601-0c33ac3a8c38
224 lines
12 KiB
Plaintext
224 lines
12 KiB
Plaintext
[[Property:title|Eiffel Classes]]
|
|
[[Property:weight|1]]
|
|
[[Property:uuid|f905426e-3109-b60b-adf0-c74b83c81b55]]
|
|
The unit of software reuse in Eiffel is the class.
|
|
|
|
The unit of modularity in Eiffel is the class.
|
|
|
|
The unit of type modeling in Eiffel is the class.
|
|
|
|
All Eiffel code must exist within the context of a class.
|
|
|
|
In Eiffel, application systems, or simply systems, are created by assembling a set of related classes. The classes in a system will be related only by one or both of the two allowable relationships in object-oriented design.
|
|
|
|
Having read the above, you should be convinced that the concept of class is important and far-reaching. The fact that we have precise rules about classes simplifies life a lot. The only kind of module in Eiffel is a class. Each class exists in one source file (which contains only that class), and contains the code necessary to provide a static definition of a data type. Every runtime entity, i.e. every object, must be an instance of a class. Because we can depend upon these things in Eiffel, we have consistency and predictabililty in the inherently complex world of software development.
|
|
|
|
Let's take a look at how classes are structured.
|
|
|
|
The code that makes up an Eiffel class is divided into the following parts:
|
|
|
|
==Structure of a Class==
|
|
|
|
All of the above, except Class header, are optional. So the simplest Eiffel class you could build would look like this:
|
|
<code>
|
|
class
|
|
SIMPLE
|
|
end
|
|
</code>
|
|
|
|
Okay, so class SIMPLE is only interesting in its simplicity. Let's look at an example that is more illustrative:
|
|
<code>
|
|
note
|
|
description: Objects that model lists
|
|
revision: $Revision: 1.4 $
|
|
|
|
class
|
|
OLD_FASHIONED_LIST [G]
|
|
|
|
obsolete "This class is obsolete, use LINKED_LIST [G] instead"
|
|
|
|
inherit
|
|
DYNAMIC_LIST [G]
|
|
|
|
create
|
|
make
|
|
|
|
feature -- Initialization
|
|
|
|
make
|
|
-- Create an empty list.
|
|
do
|
|
before := True
|
|
ensure
|
|
is_before: before
|
|
end
|
|
|
|
feature -- Access
|
|
|
|
item: G
|
|
-- Current item
|
|
do
|
|
Result := active.item
|
|
end
|
|
|
|
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)
|
|
</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.
|
|
===Note===
|
|
<code>
|
|
note
|
|
description: Objects that model lists
|
|
revision: $Revision: 1.4 $
|
|
</code>
|
|
|
|
The <code>note</code> 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 <code>note</code> does not change the semantics of the class.
|
|
|
|
The <code>note</code> part in the class above is typical. It is introduced with the language keyword <code>note</code>, and contains two note clauses, each of which is comprised of an index and a single index value. You can code note 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]
|
|
</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>frozen</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>".
|
|
|
|
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]
|
|
</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:
|
|
<code>
|
|
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.
|
|
|
|
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.
|
|
|
|
You will learn more about generic classes in the section titled [[Genericity|Genericity]] .
|
|
|
|
===Obsolete===
|
|
<code>
|
|
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 <code>note</code>, <code>obsolete</code> 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]
|
|
</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.
|
|
|
|
===Creators===
|
|
<code>
|
|
create
|
|
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]
|
|
</code>
|
|
|
|
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
|
|
</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 [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>
|
|
** The appropriate form of zero for number 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>
|
|
|
|
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
|
|
-- Create an empty list.
|
|
do
|
|
before := True
|
|
ensure
|
|
is_before: before
|
|
end
|
|
|
|
feature -- Access
|
|
|
|
item: G
|
|
-- Current item
|
|
do
|
|
Result := active.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.
|
|
|
|
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.
|
|
|
|
{{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)
|
|
</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.
|
|
|
|
We will learn more about class invariants in the section titled [[Design by Contract and Assertions|Design by Contract and Assertions]] .
|
|
|
|
|
|
|
|
|