more about classes ranga rodrigo. information hiding. copying objects
Post on 02-Jan-2016
215 Views
Preview:
TRANSCRIPT
Unexpected Phenomena Due to Reference Types
read_name is local first_name, last_name : STRING do io.read_string first_name := io.last_string io.read_string last_name := io.last_string io.put_string(first_name + " " +
last_name + "%N") end
Issues like this arise particularly when data values are tested for equality or copied.
All languages provide built-in facilities for assignment and testing equality, in Eiffel using the symbols "=", "/=" and ":=".
These operations work on values.
References In languages like Java and Eiffel, where
class variables hold references, the operations work on the reference values. In Eiffel: Assignment of class variables b := c copies a
reference value, so b and c refer to the same object.
Equality and inequality tests on class variables b = c and b /= c compare reference values, and report on whether b and c refer to the same object.
In Eiffel
Assignment of class variables b := c copies a reference value, so b and c refer to the same object.
Equality and inequality tests on class variables b = c and b /= c compare reference values, and report on whether b and c refer to the same object.
Providing Value-Semantics
Often, however, we want "value" semantics even with reference variables.
To provide this, features can be defined in the root class of a language's class hierarchy which can be overidden in user-defined classes to provide the required functionality.
In Java, this root class is called "Object" and in Eiffel "ANY".
It is assumed that every other class defined in the language automatically inherits from the root class, even if this is not stated in the class definition.
Equality
Two objects can be tested for equality using the following two features, defined in ANY:
b.is_equal(c)equal(b, c)
These features compare objects on a field by field basis, and return true if all attributes are the same in both objects, in the sense of b.field = c.field. equal(b, c) will work even if b = Void, so is often less cumbersome to use than b.is_equal(c), where you should check for this case, or else risk a run-time exception.
Equality
Philosophically speaking, this is an odd sense of equality.
Normally we assume that if two objects are equal, they can be substituted for each other in any context without problem.
This is not guaranteed by equal, however. Consider a POINT class, with two coordinate
attributes, and a LINE class, with two POINT attributes. Then look at the following code:
origin, p1, p2 : POINTl1, l2 : LINE
create origin.make(0, 0)create p1.make(1, 1)create p2.make(1, 1)
create l1.make(origin, p1)create l2.make(origin, p2)
equal(p1, p2) -- is True, butequal(l1, l2) -- is False
Equality
This seems to be a translation into Eiffel of the assertion "two distinct lines can be drawn from the origin to a given point", which is probably not true of the domain being modelled by the POINT and LINE classes.
The problem here is that equal only tests for equality of the first-level attributes within two objects.
Deep Equality
If these attributes hold references, we might want to check whether their attributes are in turn equal, rather than just seeing if they are the same object.
Eiffel has a notion of deep equality which does this. Using the definitions above:
deep_equal(l1, l2) -- is True
Deep Equality
For some reason, there is no corresponding feature b.is_deep_equal(c).
Rather than using the notion of deep equality, a simpler approach is to explicitly define a suitable equality test for each class.
This is done by redefining the feature is_equal. (The syntax for inheritance shown here will be explained in the next lecture.)
class LINEinherit ANY redefine
is_equal endfeature p1, p2 : POINT
is_equal( l : LINE ) : BOOLEAN isdo
Result := equal(p1, l.p1) andequal(p2, l.p2)
endend
Object Copying
Objects can be copied using a feature copy, overwrites the field values in one object by those of another, or clone which returns a new object which is a copy of another.
Shallow copying can lead to multiple references to shared objects, so deep equivalents of these features also exist.
The syntax is:
c.copy(b) c := clone(b)c.deep_copy(b) c := deep_clone(b)
Object Copying
Notice that, if no redefinitions are in effect, two objects will be equal after a copy, and deep_equal after a deep_copy.
A class-specific copy function can be obtained by redefining the copy feature inherited from ANY.
Information Hiding
Information hiding is a technique to limit the access that client code has to the internal details of a class.
Benefits of this include: protection against unintended corruption of
class data stored in attributes; ability to modify the internal details of a
class without affecting client code (provided that the class interface is kept constant).
Information Hiding in Java and C++
Java and C++ provide mechanisms for defining client access to class features: public, private etc.
A class will typically support information hiding by making its attributes private, and providing a public interface.
Information Hiding in Eiffel
In Eiffel, the Uniform Access Principle and the fact the clients have read-only access to class attributes make this approach unnecessary in simple cases. In general, however, an information hiding mechanism is needed.
E.g., the RESULTS class defined in the last lecture: Eiffel does not prevent client code from changing the values in the array that records the points gained for the each game:
r : RESULTSr.points[2] := 3
class RESULTScreate makefeature points : ARRAY[INTEGER] played : INTEGER total : INTEGER make( games : INTEGER ) is
do create points.make(1, games)
end add_result( pts : INTEGER ) is
do played := played + 1 points.put( pts, played ) total := total + pts
endend
Information Hiding in Eiffel
The mechanism for supporting information hiding in Eiffel is to specify which classes have access to a given feature or features. The points array can be made private as follows:
feature {} -- empty list of clients, so private points : ARRAY[INTEGER]
Information Hiding in Eiffel
class RESULTSfeature {} -- empty list of clients, so private points : ARRAY[INTEGER]feature -- the default is public played : INTEGER total : INTEGER ...end
Information Hiding in Eiffel
In C++ and Java, a class instance has access to the private data members of all other instances of the class.
In Eiffel, however, this is not the case: Given the class declaration above, the
following code will not compile because the current instance of the class does not have access the the private features of the parameter r, even though it is an instance of the same class.
Information Hiding in Eiffel
class RESULTS ... compare( r : RESULTS ) is
do if r.points[0] > points[0] then
io .put_string("They did better.")
endend
end
points arrayof r
points arrayof this instance
Information Hiding in Eiffel
In a way this is logical: the declaration feature {} says "no class has access to this feature", i.e., not even other instances of the same class.
To make this example work, you must explicitly grant this access:
class RESULTS
feature {RESULTS} points : ARRAY[INTEGER] ...
end
Information Hiding in Eiffel
An alternative approach would be to leave the original access level but provide an access function get_points to use where direct access to the array is blocked.
An argument can be made that this is preferable, as it isolates direct access of the array data structure to one place; if this data structure was subsequently changed, it would be simpler to update the class if only one function made use of it.
Information Hiding in Eiffel
Information hiding is in general provided in Eiffel by specifying in a feature clause all the classes that have access to the features in that clause.
In fact, access is granted not only to the named class, but also to all of its subclasses.
Information Hiding in Eiffel
To give all classes access to a feature you can write:
because all classes are descendants of the root class ANY.
This is the default case, and is the equivalent of public in C++ and Java.
feature {ANY}
Information Hiding in Eiffel
A declaration like
gives access not only to the results class but also all of its subclasses. In other words, this is similar to defining the feature to be protected, in C++ and Java.
feature {RESULTS}
Information Hiding in Eiffel: Summary
feature speed :
DOUBLE
Read only access to clients. No write access.
feature {} speed :
DOUBLE
No access to clients,including the instances of
the same class.
feature {CAR} speed :
DOUBLE
No access except to CAR and its
descendants (protected).feature {ANY}
speed : DOUBLE
Access to all the classes(public).
Information Hiding and DBC
A routine's pre and postcondition form a contract between the writer of the routine and its client.
It seems logical then that features that are mentioned in a routine's specification should be accessible to potential clients of the routines; otherwise, the client would be in the position of not being able to examine the contract.
Eiffel enforces this requirement for preconditions. In the RESULTS class, the following code will not compile:
Information Hiding and Preconditions
add_result( pts : INTEGER ) is require points[played + 1] = 0 do
played := played + 1points.put( pts, played )total := total + pts
end
points arraynot accessible to
clients
Information Hiding and Preconditions
In the previous example, precondition is not accessible to the client, who is therefore unable to check that the precondition is satisfied before calling the routine.
One way round this, if it is a problem, is do define an accessor function which returns the data necessary to write the precondition.
Information Hiding and Post-conditions
The situation with postconditions is slightly different.
Postconditions can mention private features, as the effect of a routine on such features can be a crucial part of the routine's specification:
Information Hiding and Preconditions
add_result( pts : INTEGER ) is do
played := played + 1points.put( pts, played )total := total + pts
ensure points[played] = pts end
points arrayis accessible to post-condition
Information Hiding and Post-Conditions
A postcondition mentioning a private feature is considered to be a private postcondition, however, and not part of the routine's contract.
Warning: it is sometimes stated that private postconditions do not appear in the interface view of a class, but this is not consistently supported by EiffelStudio.
Class invariants are not affected by the access level of features, as invariants are not accessible to the clients of a class.
top related