| Online Eiffel Documentation

EiffelStudio

# Adding Class Features

The features of a class make it useful. They are the things that objects which are instances of the class have and can do.

It is during the process of adding class features that we relate a class we are producing to other classes via the client/supplier relationship.

It is when we add features to a class that we can build the executable code that makes things happen.

## Categorizing Features

In Eiffel, we have several ways of thinking abstractly about features and categorizing them. As you saw in Eiffel Classes the feature 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:

    indexing
    	description: Objects that model lists
    	revision: $Revision: 1.5 $

    class
    	OLD_FASHIONED_LIST [G]

    obsolete "This class is obsolete, use LINKED_LIST [G] instead"

    inherit
    	DYNAMIC_LIST [G]

    create
    	make

    feature -- Initialization

    	make is
    			-- Create an empty list.
    		do
    			before := True
    			ensure is_before: before
    		end

    feature -- Access

    	item: G is
    			-- Current item
    		do
    			Result := active.item
    		end

    	first: like item is
    			-- 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)
    end

The example shows three of the features ( make , item , and first ) 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 OLD_FASHIONED_LIST . In fact we can just about guarantee that there are other features of this class. Remember the inheritance part of the class:

    	inherit
    		DYNAMIC_LIST [G]

This means that every feature in DYNAMIC_LIST will be a feature of OLD_FASHIONED_LIST . 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.

We will see more about this in Inheritance.

### Implementation Type

Whenever we are building a class in Eiffel, we are potential reuse producers. As such, we can categorize the features of a class based on the three types of feature implementation:

- Attribute
- Function
- Procedure

Attributes are those features which occupy storage in an instance. When you code an attribute in a class that you are producing, that class becomes a client to the class of the attribute. This is the most common way to establish a client/supplier relationship between the classses. Client/supplier is one of the two relationships that can exist between classes.

Functions and procedures are the computation features, that is they are features that involve executable code. Functions and procedures together are themselves categorized as routines.

Also, from the producer standpoint we may view features as whether they work from memory or through computation:

- Memory
  - Attribute
- Computation
  - Function
  - Procedure

This view can be valuable when a producer is considering performance issues. For example, if there is some class function called count , a producer might investigate whether it is better to leave count as a function, computing it each time the feature is applied, or to change it to an attribute and compute it less often.

### Usage

A times we find it appropriate to categorize features by how we use them. Specifically, as:

- Query
  - Attribute
  - Function
- Command
  - Procedure

Queries are features that, when applied to an instance, provide a value in response. Commands instruct an intance to take some action, but do not return a value. Seeing features as either queries or commands is of primary interest to reuse consumers. But as producers there are important reasons for ensuring that when we implement a feature, we implement it as a query or as a command, but not both. We will see more about this in Design by Contract and Assertions.

## General Syntax of Features

### More Sample Features

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.

    class
    	SOME_CLASS

    create
    	make,
    	make_with_arguments

    feature -- Initialization

    	make is
    			-- 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

    feature -- Access

    	an_attribute: INTEGER
    			-- An attribute of type INTEGER

    	another_attribute: INTEGER is 46
    			-- A constant attribute

    	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

    feature -- Basic Operations

    	a_procedure is
    			-- 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

    end -- Class SOME_CLASS

### Feature Declaration

When you write a feature in a class, you typically will include some of the following:

- The feature's name

  Note: In Eiffel every feature of a class must have a name that is unique within that class.

- The feature's type (in the case of an attribute or a function)
- The feature's formal argument list (in the case of a function or procedure that has arguments)
- The actual value of the feature (in the case of a constant attribute)
- The implementation code (in the case of a function or procedure)

Let's dissect one feature and identify its parts:

    	another_function (an_int: INTEGER): INTEGER is
    			-- A function with arguments
    		do
    			Result := an_attribute + an_int
    		end

In this feature:

- "another_function" is the feature's name.
- "(an_int: INTEGER)" is the argument list.
  - "an_int" is the name of the first argument
  - "INTEGER" is the type "an_int"
- "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

## General Structure of Routines

As you can imagine, the possibilities for coding routines are more complex than those for coding attributes. Routines always contain the keyword "is" after the first part of the feature declaration. The part after the "is" is called the routine part.

The routine part is made up of the following sections:

- Header Comment
- Obsolete
- Precondition
- Local Declarations
- Routine Body
- Postcondition
- Rescue

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.

    	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

Using this example, let's identify and discuss the different sections.

### Header Comment

    			-- Insert a text item to the header control
    			--     after the `insert_item_item_no' item.

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 insert_text_header_item is not obsolete ... but if it were it would include an obsolete clause. This works much like the obsolete part for classes that we saw in Eiffel Classes: the keyword " obsolete " followed by a manifest string which bears a message to potential reuse consumers:

### Precondition

    		require
    			exists: exists
    			label_not_void: a_label /= Void
    			insert_after_item_no_positive: insert_after_item_no >= 0

The precondition part of a feature is introduced by the keyword " require ". 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.

### Local Declarations

    		local
    			hd_item: WEL_HD_ITEM

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, hd_item is available as type WEL_HD_ITEM during the computation of feature insert_text_header_item .

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

    		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)

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.

There are other forms that a routine body can take. Here are some examples of some others:

#### External Routines

    	cwin_tooltips_class: POINTER is
    		external
    			"C [macro ] : EIF_POINTER"
    		alias
    			"TOOLTIPS_CLASS"
    		end

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.

Tip: Because of the high degree of language interaction provided by Microsoft.NET, it is not necessary in Eiffel for.NET to use externals to use software components from.NET assemblies. Instead, these components are presented to the Eiffel programmer as if they were Eiffel classes. Read more about this in Conventions.

#### Once Routines

    	once
    		Result := some_computation

This is the routine body of a "once" routine, specifically a "once function". A once routine is introduced by the keyword " once " rather than do . 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.

#### Deferred Routines

    	deferred

The body for a deferred routine is simply the keyword " deferred ". Below is the deferred routine body in the context of an entire feature.

    	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

As we learned in Eiffel Classes a deferred routine has specification, but no implementation. We will investigate deferred classes and features further in Inheritance.

### Postcondition

    	ensure
    		item_count_increased: item_count = old item_count + 1

The postcondition part of a routine is introduced by the keyword " ensure ". 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.

### Rescue

Our example feature does not have an explicitly coded rescue part. The rescue, introduced by the keyword " rescue ", 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 . Until then, you can see what a rescue part looks like in the feature below.

    	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

## Making Features Available to Clients

Remember that when we build classes in Eiffel, we keep in mind the possibility that these classes may eventually become valued, reusable software components. As a result we have a responsibility to make sure that our classes allow clients to use them in a safe and productive fashion. We make available those features that it is appropriate for clients to use, and we hide the rest. In Eiffel we say that a feature that is available to clients is "exported".

Control of the export of features inherited from ancestors is done by using the "export" keyword. Export of inherited features will be discussed in Inheritance.

Control of the export of immediate features (those features introduced in the text of a class) is done with the "feature" clause. We have encountered the feature clause already:

    feature -- Access

    	an_attribute: INTEGER
    			-- An attribute of type INTEGER

    	another_attribute: INTEGER is 46
    			-- A constant attribute

    	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

    feature -- Basic Operations

    	a_procedure is
    			-- 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

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

    	feature -- Basic Operations

is equivalent to

    	feature {ANY} -- Basic Operations

which means that the following features are available to clients which conform to class ANY . And in Eiffel, ANY is the class from which all other classes inherit. As a consequence all classes conform to ANY .

Inside the braces is a list of classes which are eligible as clients.

    	feature {STRING_HANDLER} -- Implementation

Features following this example from class STRING will be available to client class STRING_HANDLER and all its proper descendants.

As stated above, you can put a list of class names in the braces:

    	feature {CLASS_A, CLASS_B, CLASS_C} -- Semi-private features

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 NONE , the class from which no other class can inherit:

    	feature {NONE} -- Implementation

## Eiffel Instructions and Control Structures

When you begin to write routines in Eiffel, you will need to understand how to write instructions. Fortunately, the set of instruction types you can code is fairly small. Here we will look the most common of these. You will see some more in other topics.

### Creation

    	create hd_item.make

We discussed creation procedures and the process of bringing new objects into being in Eiffel Classes. A creation instruction starts with the keyword " create ". It creates a new object, initialize its fields, may apply a creation procedure, and attaches the object to an entity.

### Procedure Call

    	hd_item.set_text (a_label)

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 is_open_read is an attribute declared as type BOOLEAN :

    	is_open_read := False

In this instruction, an attribute of type BOOLEAN named is_open_read is being assigned the value of the manifest boolean " False ". The attribute is_open_read is based on an expanded class BOOLEAN , so that means that the value for False is held in the field for is_open_read . This is in contrast to what happens with reference types.

    	my_string := some_other_string

In this assignment, we will assume that both entities are type STRING . Because STRING is a reference type, the field for my_string will hold either a reference to an instance of STRING , or it will be Void . When the assignment above executes, then whatever was in the field for my_string is replaced with a reference to the same object that some_other_string 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 " x receives y " or " x gets y ".

    	x := y

then the rule says that this assignment is valid only if the type of y conforms to the type of x .

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 x and y .

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 x and y 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 y conforms to that x 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.

### Conditional

    	if l_c.is_digit then
    		l_state := 2
    	elseif l_c = '-' or l_c = '+' then
    		l_state := 1
    	else
    		l_state := 3
    	end

Conditionals in Eiffel use at a minimum the keywords " if ", " then ", and end ".Optionally, they may use the keywords " elseif " and " else ".

### Loop

    	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

There is only one structure for loops in Eiffel. In its typical form it uses the four keywords " from ", " until ", " loop ", and " end ". The instructions following the from keyword do any initialization necessary for the loop. After until is a boolean expression which is the exit condition. The loop body after loop 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.