Software Paradigms (Lesson 3) Object-Oriented Paradigm (2)

glintplainvilleSoftware and s/w Development

Nov 18, 2013 (3 years and 9 months ago)

86 views

Software Paradigms


Software Paradigms (Lesson 3)


Object
-
Oriented Paradigm (2)


Table of Contents

1

Reusing Classes

................................
................................
................................
....

2

1.1

Composition

................................
................................
................................
.....

2

1.2

Inheritance

................................
................................
................................
........

4

1.2.1

Ex
tending Classes

................................
................................
....................

5

1.2.2

Method Overriding

................................
................................
..................

7

1.2.3

Initialization

................................
................................
.............................

8

1.3

Combining Composition and Inheritance

................................
.........................

9

2

Abstract Classes

................................
................................
................................
.

10

2.1

Creating Abstract Classes

................................
................................
...............

10

2.2

Extending Abstract Classes

................................
................................
............

11

3

Interfaces

................................
................................
................................
.............

12

3.1

Creating Interfaces

................................
................................
.........................

12

3.2

Implementing
an Interface

................................
................................
..............

12

3.3

Multiple Inheritance

................................
................................
.......................

13

4

Polymorphism

................................
................................
................................
.....

15

4.1

Static Binding

................................
................................
................................
.

15

4.2

Dynamic Binding

................................
................................
...........................

15

4.3

Inter
changeable Objects & Upcasting

................................
............................

16

4.4

Extensibility

................................
................................
................................
...

16

Software Paradigms


1


Reusing Classes

Once a class has been created and tested, it should (ideally) represent a useful unit of code. If it
happens that such a class
has a good design and is useful we might want to
reuse

this class. Code
reuse is one of the greatest advantages that object
-
oriented programming languages provide.

There are two possibilities to reuse classes in object
-
oriented programs:



Reusing implementa
tion



Reusing interface

If we want to reuse implementation we basically
compose

a new class from the already existing
classes, thus creating a composite class. We say that the composite class reuses the
implementation from its components.

On the other hand,

if we want to reuse interface of a class we create a subclass of that class. Then
we say that the subclass
inherits

functionality (methods) from the superclass. Thus, the subclass
reuses the interface of the superclass.

1.1

Composition

The simplest way to reu
se a class within a new class is to place an object of that class inside the
new class. The new class can be made up of any number and type of other objects, in any
combination that is required to achieve the functionality that is needed. Because we are
co
mposing a new class from existing classes, this concept is called
composition

(
aggregation
).
Composition is often referred to as a “has
-
a” relationship, as in “a car has an engine.”

Let us look on the following example to see how composition works. Suppose

we want to create
the class definition for simple 2D shape objects. Each shape object has a color, a position where it
is placed on the screen, a dimension (width and height), and so on. Now, suppose that we already
have classes that represent color objec
ts, point objects and dimension objects. For instance, the
Color class might look as follows:

public class Point{


private int x_;


private int y_;


public Point(){

}

public Point(int x, int y){


x_ = x;


y_ = y;

}

Software Paradigms


public int getX(){


return x_;

}

public i
nt getY(){


return y_;

}

public void setX(int x){


x_ = x;

}

public void setY(int y){


y_ = y;

}

}

Suppose that similar to that the Color class and the Dimension class have been defined
and that they encapsulate the RGB values, and the values for width and

height,
respectively. Further, they provide a number of methods to manipulate the encapsulated
data, as well as constructors for a parameterized initialization of objects.

Now we might compose the Shape class from the Color, Point and Dimension classes:

p
ublic class Shape{


private Color color_;


private Point position_;


private Dimension dim_;


p
ublic Shape(){

}

public Shape(Point position, Dimension dim){


this
(position, dim, new Color(0, 0, 0));


}

public Shape(Point position, Dimension dim, Color colo
r){


position_ = position;


dim_ = dim;


color_ = color

}

// here comes some code

Software Paradigms


// …

public Rectangle getBounds(){


return new Rectangle(position_.getX(),

position_.getY(),




dim_.getWidth(),

dim_.getHeight());

}

public void setBounds(Rectangle bounds
){


position_.setX(bounds.getX());


position_.setY(bounds.getY());

dim_.setWidth(bounds.getWidth());


dim_.setHeight(bounds.getHeight());

}

}

In two methods, printed in bold in the above code, we see an example of providing the Shape
class with a certain f
unctionality (getting and setting the bounds) by reusing the functionality,
which is already provided by the Point and Dimension classes.

Another interesting thing to notice in the above example is how we nested constructor calls. The
second constructor, w
hich takes a Point and a Dimension object calls the third constructor, which
takes a Point, a Dimension and a Color object. This is achieved by using the self
-
reference of an
object:
this
.

Summarizing, composition comes with a great deal of flexibility. Th
e member objects of
the new class are usually private, making them inaccessible to the client programmers
who are using the class. This allows programmers to change those members without
disturbing existing client code. They can also change the member obje
cts at run
-
time, to
dynamically change the behavior of the composed objects.

1.2

Inheritance

The second way to reuse a class code can be achieved through
inheritance
. Inheritance allows
programmers to define a class as a subclass of an existing class. The subc
lass
inherits

in this way
all instance variables and all methods from the basic class, as long as they declared as public or
protected.

The level of inheritance can be arbitrarily deep. That means that we can create a subclass from a
class that is already

a subclass of another class. If we have more then one level of inheritance then
a subclass inherits variables and methods from all of ancestors of its superclass.

Software Paradigms


The subclass can use the instance variables or methods as is, or it can hide the member var
iables
or override the methods.

1.2.1

Extending Classes

The inheritance concept is called extending a class in Java. Thus, when we create a subclass of a
class we say that we extend that class.

Let us look again on an example. We use again the same example fro
m the previous section.
Suppose we have the Shape class representing 2D shapes that we can draw on the screen. In the
previous section we presented this class to demonstrate the composition principle. Here we give
the complete code for the Shape class. Not
e, that the code now includes methods that reflect a
typical behavior that we would expect from the Shape class. Thus, we have methods that we can
call in order to draw a shape or erase a shape for example.

public class Shape{


protected Color color_;


pro
tected Point position_;


protected Dimension dim_;


public Shape(){

}

public Shape(Point position, Dimension dim){


this(position, dim, new Color(0, 0, 0));


}

public Shape(Point position, Dimension dim, Color color){


position_ = position;


dim_ = dim;


c
olor_ = color

}

public void draw(Graphics g){


// here comes implementation

}

public void erase(){


// here comes implementation

}

Software Paradigms


public Rectangle getBounds(){


return new Rectangle(position_.getX(),

position_.getY(),




dim_.getWidth(), dim_.getHeight()
);

}

public void setBounds(Rectangle bounds){


position_.setX(bounds.getX());


position_.setY(bounds.getY());

dim_.setWidth(bounds.getWidth());


dim_.setHeight(bounds.getHeight());

}

}

Suppose now that we want to represent different Shape objects, such as
circles, rectangles,
polygons, triangles, ellipses, etc. Obviously all these objects are shapes and share the same
characteristics and behavior, such as they all have a color, a position, a dimension, or they can all
be drawn, moved, erased, and so on. How
ever, they all differ from each other in some way. For
example, a triangle has three distinct points, whereas a polygon can have 6 points, etc.

Obviously, the Shape class that we introduced represents very well the things that all shapes have
in common, bu
t we need more specialized classes in order to represent different shapes. These
subclasses would extend the Shape class and add required instance variables or implement
different, more specialized behavior. Thus, the Circle class would draw a circle on a
user screen
and the Triangle class would draw a triangle.

Note also that we changed the access modifiers for instance variables of the Shape class from
private to protected. We did so because we want subclasses of the Shape class to obtain access to
its in
stance variables.

Let us now look on an example for the Circle class:

public class Circle
extends

Shape{


public void draw(Graphics g){



// here comes the Circle specific code

}

}


First, we notice the keyword
extend
, which declares a class to be a subcla
ss of another class.
Then we see that the Circle class just
overrides

(implements in another way) the draw method of
the basic Shape class in order to implement a specific behavior for circle objects.

Software Paradigms


Let us look now on another example:

public class Polygo
n extends Shape{


private Point[] points_;


public Polygon(Point[] points){



points_ = points;

}


public void draw(Graphics g){



// here comes the Polygon specific code

}

}

Note that the Polygon class not only overrides the draw method, but also adds a n
ew, specific
instance variable to the Polygon class. It is an array of Points that represent points of that
polygon.

1.2.2

Method Overriding

The ability of a subclass to override a method in its superclass allows a class to inherit
from a superclass whose behavi
or is "close enough" and then override methods as
needed. We saw this already on the examples of the Circle and the Polygon class that
override the draw method of the basic Shape class.

Let us look more closely on these two overridden methods:

public class

Polygon extends Shape{


public void draw(Graphics g){



g.setColor(color_);



for(int i = 0; i < (points_.length


1); i++)



g.drawLine(points_[i].getX(), points[i].getY(),


points_[i+1].getX(), points_[i+1].getY());

}

// here is other code

}

Software Paradigms


public clas
s Circle extends Shape{


public void draw(Graphics g){



g.setColor(color_);



g.drawOval(position_.getX(), position_.getY(),




dim_.getWidth(), dim_.getHeight());

}

}


Thus, we see that these two classes provide different implementations for the draw me
thod and
therefore a different behavior for the two classes.

Another important thing to notice is that the return type, method name, and number and type of
the parameters for the overriding method must match those in the overridden method.

1.2.3

Initialization

A
nother important aspect of the inheritance mechanism is the initialization of objects of a
subclass. Basically, an object of a subclass is an instance of both classes: subclass and
superclass. Thus, a proper initialization of both “objects” has to be accom
plished.

In Java, the default empty constructor of a class is automatically called when an object is created.
If a class is a subclass of another class then the Java system automatically invokes first the
constructor of the superclass.

In the case that pro
grammer wants to define constructors that takes arguments, then he/she has to
call the constructor of the superclass by him/herself.

Let us look on the following example to demonstrate this:

public class Polygon extends Shape{


private Point[] points_;


pu
blic Polygon(Point[] points, Point position,

Dimension dim){



super(position, dim);



points_ = points;

}

// here comes the rest

}

The code in bold calls the constructor of the superclass to initialize its instance variables properly.
Note that this call

has to be the first thing that a subclass constructor executes.

Software Paradigms


1.3

Combining Composition and Inheritance

Sometimes we want to create more complex classes that possible combine composition
and inheritance mechanism.

The following example shows the creation o
f a more complex class, using both inheritance and
composition. Suppose that we want to have a composite shape object, which groups together a
number of other shapes. In that way we can treat all shapes object that are put into the grouped
object as just o
ne shape object.

Obviously, such object is a special shape object, i.e., it can be represented by a subclass of the
Shape class and at the same time it is a composite object composed of say an array of other shape
objects. The code for such Group class mig
ht look as follows:

public class Group
extends

Shape{


private Shape[] shapes_;


public Group(Shape[] shapes){



shapes_ = shapes;

}

public void draw(Graphics g){


for(int i = 0; i < shapes_.length; i++)



shapes_[i].draw(g);

}

}

The cold in bold from the
example above represents inheritance and composition
mechanisms.

Software Paradigms


2

Abstract Classes

Sometimes, a class that we define represents an abstract concept and, as such, should not
be instantiated. Rather it should just serve as a base superclass for a number of mo
re
specialized classes. That means a basic abstract class defines just an interface that should
be shared among its subclasses.

Let us revisit our Shape class example and try to think about it in terms of an abstract class.
Basically, we used the Shape cla
ss just as a superclass for a number of different subclasses, such
as the Circle, Polygon, Group class and so on. We didn’t use the Shape class to directly create
instances of it.

If we think more about it we can conclude that it is not necessary to creat
e instances of the Shape
class, because an instance of a Shape class that is not also an instance of a special subclass is of
no much use, because such Shape instance is not aware of how to draw itself, for example. It
knows that it is a Shape but it doesn
’t know which Shape it is.

Obviously, we could and should (to prevent creation of objects that don’t know how to draw
themselves, for example) define the Shape class to be an abstract class. Technically, to define an
abstract class means that we define a n
umber of its methods to be abstract, i.e., we don’t provide
the implementation for them but rather leave the subclasses to do so. Obviously, the draw method
and for example the erase method of the Shape classes should be defined abstract, because special
c
lasses should implement this special behavior themselves.

2.1

Creating Abstract Classes

To create an abstract class we must declare it to be abstract and in addition to that we must
declare a number of its methods to be abstract. Let us revisit the Shape class

from above and
define it to be an abstract class:

abstract

public class Shape{


protected Color color_;


protected Point position_;


protected Dimension dim_;


public Shape(){

}

public Shape(Point position, Dimension dim){


this(position, dim, new Color(0
, 0, 0));


}

Software Paradigms


public Shape(Point position, Dimension dim, Color color){


position_ = position;


dim_ = dim;


color_ = color

}

abstract public void draw(Graphics g);

abstract public void erase();

public Rectangle getBounds(){


return new Rectangle(position_.
getX(),

position_.getY(),




dim_.getWidth(), dim_.getHeight());

}

public void setBounds(Rectangle bounds){


position_.setX(bounds.getX());


position_.setY(bounds.getY());

dim_.setWidth(bounds.getWidth());


dim_.setHeight(bounds.getHeight());

}

}

Note the

code in bold declaring the Shape class and two methods of it to be abstract.

Another important thing to notice here are constructors of the abstract Shape class. Although the
Shape class defines a number of constructors we cannot use these constructors di
rectly to create
instances of the Shape class. The only way we can use these constructors is from subclasses of the
Shape class. Thus, if we have a subclass of the Shape class, say the Polygon class, it can invoke
as the first line in its constructor a con
structor from the Shape class in order to initialize instance
variables from the Shape class.

2.2

Extending Abstract Classes

Basically, extending an abstract class is same as extending a normal class. The only difference is
that a subclass in order to become a

normal class, i.e., a class from which we can create instances
has to implement all abstract methods from its abstract superclass. Otherwise this subclass is
considered to be an abstract class as well and therefore it is not possible to create its instanc
es.

Software Paradigms


3

Interfaces

In the last section we saw in which way we can use and create abstract classes. Let us now think
about an abstract class that has all its methods declared to be abstract. Obviously, such abstract
class provides just an
interface

for all of i
ts subclasses.

In Java this concept obtained a special keyword:
interface
. An
interface

defines a protocol of
behavior that can be implemented by any class anywhere in the class hierarchy. An interface
defines a set of methods but does not implement them.
A class that implements the interface
agrees to implement all the methods defined in the interface, thereby agreeing to certain behavior.
This class obtains also the type of that interface.

Because an interface is simply a list of unimplemented, and theref
ore abstract methods, what is
the difference between an interface and an abstract class? The differences are significant:



An interface cannot implement any methods, whereas an abstract class can.



A class can implement many interfaces but can have only one

superclass.



An interface is not part of the class hierarchy. Unrelated classes can implement the same
interface.

3.1

Creating Interfaces

Let us now look on an example of an interface. We already saw how we can use collections to
store objects of different t
ypes. We also introduced the concept of an
iterator
, which is an object
that we use to traverse through the objects of a particular collection. The iterator object is a
typical example of where we would use an interface instead of a class.

Basically, an it
erator object allows us to go forth and back, for example, between members of a
collection. These members are stored within that collection, but an iterator just agrees to traverse
behavior. It does not contain the objects from that collection. We could de
fine an iterator
interface like this:

public
interface

Iterator{


public Object next();


public boolean hasNext();

}

Note, the
interface

keyword in bold. We also see from the above example that an interface just
declares a number of methods similarly to an

abstract class. The difference here is that we don’t
need to use the abstract keyword to do so.

3.2

Implementing an Interface

If a class chooses to extend an interface we say that this class
implements

that interface. The
principle here is the same as with ab
stract classes. If a class implements an interface it should
implement all of its methods in order not to be an abstract class. Otherwise the class is considered
to be an abstract class and therefore we cannot create instances of that class.

Let us look on

an example of a class that implements the Iterator interface from above:

Software Paradigms


public class ArrayList
implements

Iterator{


private int cursor_;



public boolean hasNext(){


return (cursor_ != size());

}

public Object next(){


return get(cursor_++);

}

public It
erator iterator(){


cursor_ = 0;


return this;

}


// other code comes here

}

In the example above the ArrayList class implements the Iterator interface. The ArrayList class
implements all methods declared in the Iterator interface, thus this class is not a
n abstract class
and we can create its instances.

There are several important issues of the example above that we have to mention here:



ArrayList class implements itself the Iterator interface, therefore the method iterator
returns
this

reference, i.e., th
e reference of the ArrayList object itself. Recollect that a
class implementing an interface has also the type of that interface.



Because the ArrayList class implements the Iterator interface, the methods that override
that interface might call other metho
ds of the ArrayList directly. For example, the method
next calls the method get of the ArrayList class.



Another class could be created, say ArrayListIterator, which could be a separate class
only implementing the Iterator interface. In that case the iterat
or method from the
ArrayList class would create a separate instance of the ArrayListIterator class and return
this instance.

3.3

Multiple Inheritance

As we mentioned before a class can extend another class in order to specialize its characteristics
and its beh
avior. In some object
-
oriented languages a class can extend not only one class but also
a number of other classes. This is called
multiple inheritance
.

There are a number of problems with this concept. For instance, suppose a class extends two
classes that

declare two same methods but with other implementations (e.g. in both classes we
have one method with the same name, arguments and return values but with a different
implementation). Now, if we call this method on an instance of the subclass, it is not cl
ear to
which code to bind it, i.e., should we call this method from the first or from the second
superclass.

Software Paradigms


Such and similar issues caused that there is no support for pure multiple inheritance in Java. That
means in Java a class can extend just one supe
rclass.

However, this might be too restrictive in some cases and therefore Java supports “a kind of”
multiple inheritance in the following way. A class in Java might implement a number of
interfaces, thus it “inherits” from multiple sources. Since interfa
ces are just declarations of
methods and there is no implementation defined by interfaces the problem that we described
before does not exist in Java.

Software Paradigms


4

Polymorphism

Let us look again on the example of the Group shape class from above. Precisely, let us
inve
stigate in more details its draw method:

public class Group extends Shape{

public void draw(Graphics g){


for(int i = 0; i < shapes_.length; i++)



shapes_[i].draw(g);

}

// …

}

An instance of the Group class holds an array of instances of other Shape class
es, such as circles,
polygons, triangles, etc. The draw method of the Group class iterates through all shapes and
draws them one by one on the user screen. Lines of the code in bold above show the invocation of
the draw method of each particular Shape obje
ct.

Here we encounter a very important mechanism in object
-
oriented languages. We call in our code
the method of an instance of a base class (the Shape class), but at the run
-
time the system calls the
right method of each particular object, i.e., if it enc
ounters a circle object it calls the draw method
of that circle object; if it encounters a polygon object it calls the draw method of the polygon
object, etc. As the effect of that shape objects are drawn in the way we expect them to be drawn.

This mechani
sm is called
polymorphism
. Technically, due to polymorphism objects are
interchangeable

or
substitutable

in object
-
oriented programs. This means we can write code that
talks to shapes and automatically handle anything that fits the description of a shape,
i.e., handle
anything that is a subclass of the Shape class.

Let us now examine how object
-
oriented programming languages implement polymorphism.

4.1

Static Binding

In traditional programming languages, e.g. in procedural programming languages such as C, the
c
ompiler makes a function call
statically
. It means the compiler generates a call to a specific
function name, and the linker resolves this call to the absolute address of the code to be executed.
This principle is called
static binding

or
early binding

of
a function call.

The static binding means that in the case of the above Shape example the compiler would bind
the call to the draw method of the Shape class at the compile time, thus at the run
-
time the draw
method of the base Shape class would be called f
or each particular shape object regardless of its
real type.

4.2

Dynamic Binding

As we already saw static binding cannot be applied if we want to support polymorphism.
Therefore, object
-
oriented languages support another type of binding function calls. Binding

supported by object
-
oriented languages is called
dynamic binding

or
late binding
.

Software Paradigms


Dynamic binding means that when you send a message to an object, the code being called isn’t
determined until run
-
time. The compiler does ensure that the function exists an
d performs type
checking on the arguments and return value, but it doesn’t know the exact code to execute.

To perform late binding, Java uses a special bit of code in lieu of the absolute call. This code
calculates the address of the function body, using i
nformation stored in the object. Thus, each
object can behave differently according to the contents of that special bit of code. When you send
a message to an object, the object actually does figure out what to do with that message.

4.3

Interchangeable Objects

& Upcasting

Polymorphism allows us to interchange (substitute) an object of the type A for an object of the
type B, where the type A is a subclass of the type B (no matter at which level of inheritance). Let
us look again on the Shape example:

Shape[] sha
pes = new Shape[2];

shapes[0] = new Circle(new Point(100, 100), 50);

shapes[1] = new Polygon(new Point(0, 0), 100, 50);

Group group = new Group(shapes);

First, we notice that we substitute a circle and a polygon object for objects of its base Shape class
(
e.g., shapes[0] and shapes[1]). We treat instances of inheriting classes as instances of a base
class.

We call this process of treating a derived type as though it were its base type
upcasting
. The name
cast
is used in the sense of casting into a mold and
the
up

comes from the way the inheritance
diagram is typically arranged, with the base type at the top and the derived classes fanning out
downward. Thus, casting to a base type is moving up the inheritance diagram: “upcasting.”

4.4

Extensibility

Polymorphism
allows object
-
oriented programs to be easily extensible. Imagine that we write the
code for another Shape class, say Ellipse class. It is clear that we don’t need to change the code
for the Group class in order to include ellipse objects in new group objec
ts. Since the Ellipse class
is a subclass of the Shape class everything perfectly fits. It doesn’t matter that we created the
Ellipse class additionally.