Previous Contents Next

Relations between Classes

Classes can be related in two ways:
  1. An aggregation relation, named Has-a:
    class C2 is related by Has-a with class C1 when C2 has a field whose type is that of class C1. This relation can be generalized as: C2 has at least one field whose type is that of class C1.
  2. An inheritance relation, named Is-a:
    class C2 is a subclass of class C1 when C2 extends the behavior of C1. One big advantage of object-oriented programming is the ability to extend the behavior of an existing class while reusing the code written for the original class. When a class is extended, the new class inherits all the fields (data and methods) of the class being extended.

Aggregation

Class C1 aggregates class C2 when at least one of its instance variables has type C2. One gives the arity of the aggregation relation when it is known.

An Example of Aggregation

Let us define a figure as a set of points. Therefore we declare class picture (see figure 15.2), in which one of the fields is an array of points. Then the class picture aggregates point, using the generalized relation Has-a.

# class picture n =
object
val mutable ind = 0
val tab = Array.create n (new point(0,0))
method add p =
try tab.(ind)<-p ; ind <- ind + 1
with Invalid_argument("Array.set")
-> failwith ("picture.add:ind =" ^ (string_of_int ind))
method remove () = if (ind > 0) then ind <-ind-1
method to_string () =
let s = ref "["
in for i=0 to ind-1 do s:= !s ^ " " ^ tab.(i)#to_string() done ;
(!s) ^ "]"
end ;;
class picture :
int ->
object
val mutable ind : int
val tab : point array
method add : point -> unit
method remove : unit -> unit
method to_string : unit -> string
end


To build a figure, we create an instance of class picture, and insert the points as required.

# let pic = new picture 8;;
val pic : picture = <obj>
# pic#add p1; pic#add p2; pic#add p3;;
- : unit = ()
# pic#to_string ();;
- : string = "[ ( 0, 0) ( 3, 4) ( 3, 0)]"


A Graphical Notation for Aggregation

The relation between class picture and class point is represented graphically in figure 15.2. An arrow with a diamond at the tail represents aggregation. In this example, class picture has 0 or more points.


Figure 15.2: Aggregation relation.


Furthermore, we show above the arrow the arity of the relation.

Inheritance Relation

This is the main relation in object-oriented programming. When class c2 inherits from class c1, it inherits all fields from the parent class. It can also define new fields, or redefine inherited methods to specialize them. Since the parent class has not been modified, the applications using it do not need to be adapted to the changes introduced in the new class.

The syntax of inheritance is as follows:

Syntax


inherit name1 p1 ...pn [ as name2 ]
Parameters p1, ..., pn are what is expected from the constructor of class name1. The optional keyword as associates a name with the parent class to provide access to its methods. This feature is particularly useful when the child class redefines a method of the parent class (see page ??).

An Example of Simple Inheritance

Using the classic example, we can extend class point by adding a color attribute to the points. We define the class colored_point inheriting from class point. The color is represented by the field c of type string. We add a method get_color that returns the value of the field. Finally, the string conversion method is overridden to recognize the new attribute.

Note


The x and y variables seen in to_string are the fields, not the class initialization arguments.

# class colored_point (x,y) c =
object
inherit point (x,y)
val mutable c = c
method get_color = c
method set_color nc = c <- nc
method to_string () = "( " ^ (string_of_int x) ^
", " ^ (string_of_int y) ^ ")" ^
" [" ^ c ^ "] "
end ;;
class colored_point :
int * int ->
string ->
object
val mutable c : string
val mutable x : int
val mutable y : int
method distance : unit -> float
method get_color : string
method get_x : int
method get_y : int
method moveto : int * int -> unit
method rmoveto : int * int -> unit
method set_color : string -> unit
method to_string : unit -> string
end


The constructor arguments for colored_point are the pair of coordinates required for the construction of a point and the color for the colored point.

The methods inherited, newly defined or redefined correspond to the behaviors of instances of the class.

# let pc = new colored_point (2,3) "white";;
val pc : colored_point = <obj>
# pc#get_color;;
- : string = "white"
# pc#get_x;;
- : int = 2
# pc#to_string();;
- : string = "( 2, 3) [white] "
# pc#distance;;
- : unit -> float = <fun>
We say that the class point is a parent class of class colored_point and that the latter is the child of the former.

Warning


When redefining a method in a child class, you must respect the method type defined in the parent class.


A Graphical Notation for Inheritance

The inheritance relation between classes is denoted by an arrow from the child class to the parent class. The head of the arrow is a closed triangle. In the graphical representation of inheritance, we only show the new fields and methods, and redefined methods in the child class. Figure 15.3 displays the relation between class colored_point and its parent point.




Figure 15.3: Inheritance Relation.


Since it contains additional methods, type colored_point differs from type point. Testing for equality between instances of these classes produces a long error message containing the whole type of each class, in order to display the differences.

# p1 = pc;;
Characters 6-8:
This expression has type
colored_point =
< distance : unit -> float; get_color : string; get_x : int; get_y :
int; moveto : int * int -> unit; rmoveto : int * int -> unit;
set_color : string -> unit; to_string : unit -> string >
but is here used with type
point =
< distance : unit -> float; get_x : int; get_y : int;
moveto : int * int -> unit; rmoveto : int * int -> unit;
to_string : unit -> string >
Only the first object type has a method get_color



Previous Contents Next