Presentation is loading. Please wait.

Presentation is loading. Please wait.

Ranga Rodrigo Based on Marc Priestley's Lectures

Similar presentations


Presentation on theme: "Ranga Rodrigo Based on Marc Priestley's Lectures"— Presentation transcript:

1 Ranga Rodrigo Based on Marc Priestley's Lectures
Inheritance Ranga Rodrigo Based on Marc Priestley's Lectures

2 Quiz: Question 1 The following set of slides show two classes, point and line. Write down the output that corresponds to the main file shown.

3 class LINE create make feature p1_ : POINT p2_ : POINT make ( p1 : POINT; p2 : POINT ) is do p1_ := p1 p2_ := p2 ensure p1_ = p1 p2_ = p2 end display is io.put_string ("line from ") p1_.display io.put_string (" to ") p2_.display is_equal( l : LINE ) : BOOLEAN is Result := equal(p1_, l.p1_) and equal(p2_, l.p2_) class POINT create make feature x_ : INTEGER y_ : INTEGER make ( x : INTEGER; y : INTEGER) is do x_ := x y_ := y ensure x_ = x y_ = y end display is io.put_string ("point (") io.put_integer (x_) io.put_string (", ") io.put_integer (y_) io.put_string (")")

4 class MAIN create make feature -- Initialization origin, p1, p2, p3, p4: POINT line1, line2 : LINE make is do create origin.make (0, 0) create p1.make(1,2) create p2.make(1,2) create p3.make (5,5) create line1.make (origin, p1) create line2.make (origin, p2) p3.copy(origin) p4 := clone(origin) io.put_string ("1. ") io.put_boolean(equal(p1, p2)) io.new_line io.put_string ("2. ") io.put_boolean(p1.is_equal(p2)) io.put_string ("3. ") io.put_boolean(equal(line1, line2)) io.new_line io.put_string ("4. ") io.put_boolean(line1.is_equal(line2)) io.put_string ("5. ") io.put_boolean(deep_equal(line1, line2)) io.put_string ("6. ") io.put_boolean(line1.is_equal (line2)) io.put_string ("7. ") p3.display io.put_string ("8. ") p4.display end

5 Quiz: Question 2 Assuming that a, b, c, d, e, f, g, and h are declared as instances of type, e.g., LINE, explain the operation done by each of the flowing snippets. Indicate the depth (shallow, or deep) and whether the objects need to have been already created. b.copy(a) d.deep_copy(c) f := clone(e) h := deep_clone(g)

6 Quiz: Question 3 Consider the following code.
Why would it not compile? How would you make the played attribute only accessible/mutable by a child class of RESULTS called SOCCER_RESULTS and its descendents? How would you make the played attribute public accessible/mutable?

7 class RESULTS feature {} points : ARRAY[INTEGER] feature played : INTEGER total : INTEGER ... end compare( r : RESULTS ) is do if r.points[0] > points[0] then io .put_string("They did better.")

8 Solution: Question 1 1. True 2. True 3. False 4. False 5. True 6. True
7. point (0, 0) 8. point (0, 0) 1.5 marks each (12)

9 Solution: Question 2 One mark Each (5) Object Depth Creation Comments
a,c,e,g Not applicable Should have already been created. These are the sources for copying or cloning. b.copy(a) b will be a shallow copy of a b should have already been created. d.deep_copy(c) Deep d should have already been created. f := clone(e) Shallow f will be created. h := deep_clone(g) One mark Each (5)

10 Quiz: Question 3 Consider the following code.
1. Why would it not compile? No class has access to the feature points. So the instances of RESULTS itself do not have access to it. Therefore the line r.points[0] > points[0] will not compile. 2. How would you make the played attribute only accessible/mutable by a child class of RESULTS called SOCCER_RESULTS and its descendents? feature {SOCCER_RESULTS} Played : INTEGER 3 marks 3 marks

11 Quiz: Question 3 3. How would you make the played attribute public accessible/mutable? feature {ANY} Played : INTEGER 2 marks

12 Inheritance and Redefinition
Suppose we wanted to extend the functionality of the COUNTER class discussed in Lecture 3, so that it could support counter which emitted a sound whenever the count was incremented. One way to do this would be to define the new class as a subclass of COUNTER, and to override, or redefine, the increment routine to include the new functionality.

13 COUNTER class from Lecture 03 Page 1 class COUNTER create make feature
value: INTEGER_32 max: INTEGER_32 make (m: INTEGER_32) require valid_maximum: m > 0 do max := m ensure value_initialized: value = 0 max_initialized: max = m end increment not_at_maximum: not at_max value := value + 1 value_incremented: value = old value + 1 maximum_constant: max = old max

14 COUNTER class from Lecture 03 Page 2 at_max: BOOLEAN do
if value < max then Result := False else Result := True end ensure Result = (value = max) display io.put_integer (value) io.new_line invariant value_in_range: 0 <= value and value <= max

15 AUDIBLE_COUNTER class inherits from COUNTER class class
redefine increment end create make feature increment is do value := value + 1 io.put_string("Beep%N")

16 Inheritance in Eiffel Child classes inherit all features of their parents, so for example, the new increment routine can refer to and alter the value feature. If a routine is to be redefined in the subclass, it must be declared in a redefine clause following the name of the class it was originally declared in.

17 Inheritance in Eiffel Creation routines are inherited, as are all routines. However, the subclass must include a create clause to declare what its own creation routines are. New features can be added in child classes. Zero-argument functions can be redefined as attributes, but not vice versa. Features can be frozen, and cannot then be redefined.

18 Calling Routines from Superclass
This example repeats the code contained in the increment routine that is being redefined. In general, this is a bad idea, and Eiffel provides the following way to call the routine that is being overridden: The Precursor statement calls the increment routine from the superclass COUNTER. increment is do Precursor io.put_string("Beep%N") end

19 Polymorphism As with other object-oriented languages, references to subclasses can be stored in variables which have the type of the superclass: class MAIN create make feature c : COUNTER make is do create {AUDIBLE_COUNTER} c.make(9999) c.increment io.put_integer(c.value) end

20 Polymorphism class MAIN create make feature c : COUNTER make is do
create {AUDIBLE_COUNTER} c.make(9999) c.increment io.put_integer(c.value) end Creating an instance of the AUDIBLE_COUNTER Increment routine of the AUDIBLE_COUNTER called

21 Polymorphism Example The first line of make shows how to create an instance of the AUDIBLE_COUNTER class and store a reference to it in a variable whose compile-time type is COUNTER. In the second line, dynamic binding ensures that the increment routine from AUDIBLE_COUNTER is called. Even though c is declared to be of type COUNTER, this call will result in a beep being emitted. In the final line, the inherited value attribute of the counter is accessed as normal.

22 Renaming Features A feature cannot be redefined if its signature changes, e.g., if it has to take another parameter. This often happens with creation routines, where additional data is required to initialize subclass instances. However, Eiffel does not allow overloading, so attempting to have two routines with the same name will lead to a compilation error. A common solution is to rename the inherited routine:

23 Renaming: Page 1 class AUDIBLE_COUNTER inherit COUNTER rename
make as make_counter redefine increment end create make feature beep_tone : STRING make( m : INTEGER ; b : STRING ) is do make_counter( m ) beep_tone := b increment is ... Renaming: Page 1 Renaming the inherited routine Notice that Precursor cannot be used in the make routine because we have renamed the inherited make routine as make_counter instead of redefining it.

24 Renaming: Page 2 increment is do Precursor
io.put_string( beep_tone + "%N" ) end Renaming: Page 2

25 Inheritance and Specialization
The conventional use of inheritance, as exemplified in UML and Java and illustrated above with the counter classes, is where subclasses define specialized cases of superclasses, and can redefine or add to the inherited functionality. The important thing here is substitutability: references to subclass instances can be stored in superclass variables, and a program should not be able to tell the difference. This is implemented using polymorphism and dynamic binding, as shown above.

26 Effect of Undefining and Renaming
In Eiffel, unlike Java, the interface of a subclass need not contain everything defined in the interface of the superclass. Renaming a feature leads to this effect, and Eiffel allows other possibilities such as undefining features and changing the access level of features. This can make it look as if polymorphism will not work, as in the following example:

27 Notice that the child class
PARENT feature socialize is do io.put_string("I say old chap!") end CHILD inherit rename socialize as party redefine party party is io.put_string("Hey, dude!") PARENT class CHILD class Notice that the child class renames the method from the parent class before redefining it.

28 Effect of Undefining and Renaming
Given these classes, it looks like the code ought not to work: p is referencing an object of class CHILD, but there is no feature called socialize in the CHILD class, because it's been renamed. However, it works fine. We will see why in the next slide. p : PARENT create { CHILD } p p.socialize

29 Name-Based Look Up Why? The answer is that Eiffel uses a different method for looking up routines at run-time than C++ or Java. In those languages, a call like p.socialize would be interpreted as follows: Look at the run-time type of the object p points to (i.e., CHILD) Find a function called socialize in that class, and report a problem because there isn't one. In other words, it is a name-based look-up method. In Eiffel, this will not work, because of renaming.

30 Feature-Based Look Up Eiffel uses a different rule:
Look for the feature named in the static call, i.e., socialize in class PARENT. Find the corresponding feature in the relevant run-time class, CHILD. socialize has been renamed as party, and then party has been redefined, so the required feature is the new method in the CHILD class. So, Eiffel uses a feature-based look-up method. party in class CHILD is the same feature as do_socialize in class PARENT, even though it's got a different name.

31 Look Up Methods: Summary
Name-Based Feature-Based C++ Java Eiffel

32 Inheritance for Code Resuse
Re-use of code is nowadays mostly accomplished using composition. For example, the RESULTS class defined earlier reused the code in the ARRAY class to store data: it did this by defining an attribute which stored a reference to the array. Code reuse can also be accomplished using inheritance, however. This style of programming has gone out of fashion, but it plays an important role in the design of the Eiffel libraries. Here is the RESULTS class implemented using inheritance:

33 Renaming the constructor
class RESULTS inherit ARRAY[INTEGER] rename make as make_array end create make feature played : INTEGER total : INTEGER make( games : INTEGER ) is do make_array(1, games) add_result( pts : INTEGER ) is played := played + 1 put( pts, played ) total := total + pts Class inherits from ARRAY[INTEGER] Renaming the constructor CHILD class features

34 Implications of Inheritance
This class inherits from ARRAY[INTEGER] rather than storing a reference to an array of that type. This means that all the features of the array class are features of the RESULTS. The array constructor must be renamed and called in the constructor for the RESULTS. Instead of points.put(pts, played) the code can now simply be written as put(pts, played) as the put method is inherited by the RESULTS.

35 Disadvantage of Inheriting from ARRAY
One disadvantage of this is that all the features of the array class are inherited by RESULTS with the same access level as they had in the array class. This means that they are available to clients, who could therefore access the array of points directly. To counter this problem, we can change the access level of the features that are inherited from the array class:

36 class RESULTS inherit ARRAY[INTEGER] rename make as make_array export { NONE } all end Private inheritance

37 Private Inheritance This is essentially the same as private inheritance in C++, though Eiffel allows more control over what access levels are given to the inherited features. Different features can be given different access levels, and the access level can be specified in the same way as in a feature clause.

38 Facility Inheritance How do languages define globally accessible functions like sqrt? In C++ or Java, these are typically defined in classes which are made up of static methods only. Such classes are effectively modules, in the sense of Modula-2: there is no intention to create instances of them. In Eiffel, there are no static features of classes. So, the required functions must be defined in a normal class. They can then be used by inheriting the class that defines them, a technique known as facility inheritance:

39 class POINT inherit ARITHMETIC feature ... distance : REAL is do Result := sqrt(x*x + y*y) end

40 Facility Inheritance Here sqrt is a feature of POINT, because of inheritance. This technique seems to violate the intuition that inheritance is to do with classification of objects. It is hard to give a definition of the ARITHMETIC class: Meyer comes up with "an object that needs access to arithmetic functions", which seems a bit arbitrary, and it seems odd to say that sqrt is a feature of a point.

41 Inheritance from Interfaces
Java allows classes to implement a number of interfaces as well as extending a class. C++ and Eiffel have no specific interface construct, but can use abstract or deferred classes for much the same purpose.

42 Inheritance from Interfaces
For example, the Eiffel library includes a deferred class called COMPARABLE which defines the comparison operations >, <, >= and <=. To make these operators available, COMPARABLE should be inherited and the < operator redefined. For example, suppose we wanted to be able to compare teams in terms of who had the most points:

43 class RESULTS inherit COMPARABLE redefine infix "<" end create Make feature { } -- protected points : ARRAY[INTEGER] feature { ANY } -- public played : INTEGER total : INTEGER

44 make( games : INTEGER ) is
do create points.make(1, games) end add_result( pts : INTEGER ) is played := played + 1 points.put( pts, played ) total := total + pts infix "<" (other: like Current): BOOLEAN is Result := total < other.total

45 Deferred Class Approach
The deferred class approach does seem to have the advantage over Java's interfaces that it is possible to define a set of operations, such as >, <= and >+ in this case, in terms of a single deferred operation. If COMPARABLE was an interface, it would be necessary for the RESULTS class to redefine all these operations. However, making this useful requires that the language supports multiple inheritance.


Download ppt "Ranga Rodrigo Based on Marc Priestley's Lectures"

Similar presentations


Ads by Google