When to Use Inheritance
Costs of using inheritance
[Skrien §3.7] The yo-yo effect. Given the
title, can you describe what this is talking
about?
Outline for Lecture 18
I. Costs of inheritance
II. Two examples
A. Drawing polygons
B. Sorting
III, Inheritance vs.
delegation
IV. Readability
Tight coupling of classes and subclasses. Due to information hiding,
the author of a class can be sure that client classes depend only on
the public interface of a class, not on its implementation.
Unfortunately, the same is not true of subclasses.
The example given in the text is a specialized class of Rectangle
that keeps track of how many times its width is changed.
The superclass contains these methods:
public void setWidth(int newWidth)
{ width = newWidth; }
public void setHeight(int newHeight)
{ height = newHeight; }
public void setSize(int w, int h)
{ setWidth(w); setHeight(h); }
The subclass overrides setWidth:
public void setWidth(int newWidth) {
if( newWidth != width ) {
widthChangeCounter ++;
width = newWidth;
}
Lecture 18
Object-Oriented Languages and Systems
1
If the implementation of setSize is changed, the subclass’s
setWidth may not work anymore. Why not?
This is one possible “gotcha” with respect to the implementation.
Another pitfall is described in the text.
Unfortunately, this example seems somewhat contrived. Can you
think of a better one?
Two examples
Drawing polygons
[Skrien §3.9] A drawing program that allows the user to draw
polygons (rectangles, squares, triangles, etc.).
The drawing program displays a window with two parts:
• a large drawing canvas and
• a toolbar across the top of the window with tool buttons,
one for each polygon.
The user clicks on a tool
button to select a polygon
and then clicks in the
drawing canvas.
A copy of the selected
polygon appears in the
canvas centered where
the user clicks.
The canvas contains a
collection of Polygons.
How should they be
represented?
Well, the various kinds of polygons could be subclasses of Polygon.
CSC/ECE 517 Lecture Notes
© 2007, Edward F. Gehringer
2
To display the polygons, we simply iterate through the list:
public void paint(Graphics g) {
for( int i = 0; polygons.size(); i++ ) {
Polygon poly = (Polygon) polygons.get(i);
poly.draw(g);
}
}
But should the different kinds of polygons be represented as
subclasses of Polygon?
Sorting
Goal: Show how inheritance + interfaces = elegant code.
Let’s start with this code:
public class Sorter
{
public static void sort(String[] A)
{ ...code for sorting String arrays... }
public static void sort(Integer[] A)
{ ...code for sorting Integer arrays... }
...methods for sorting other kinds of arrays...
Lecture 18
Object-Oriented Languages and Systems
3
}
What is it supposed to do?
But, how many sort methods should Sorter implement?
Also, won’t it be inelegant to duplicate most of the code for sorting in
each method?
Or if we want to sort in reverse order, or sort strings by length?
Before seeing how we can overcome these shortcomings, let’s take a
look at the code for sorting.
public static void sort(Integer[] data)
{
for (int i = data.length-1; i >=1; i--) {
// in each iteration through the loop
// swap the largest value in data[0..i] into pos’n i
//find the index of the largest value in data[0.. i]
int indexOfMax = 0;
for (int j = 1; j <= i; j++) {
if (data[j]>data[indexOfMax])
indexOfMax = j;
}
// swap the largest value into position i
Integer temp = data[i];
data[i] = data[indexOfMax];
data[indexOfMax] = temp;
}
}
public static void sort(String[] data)
{
for (int i = data.length-1; i >=1; i--) {
// in each iteration through the loop
// swap the largest value in data[0..i] into pos’n i
//find the index of the largest value in data[0.. i]
int indexOfMax = 0;
for (int j = 1; j <= i; j++) {
if (data[j].compareTo(data[indexOfMax]) > 0)
indexOfMax = j;
}
CSC/ECE 517 Lecture Notes
© 2007, Edward F. Gehringer
4
// swap the largest value into position i
String temp = data[i];
data[i] = data[indexOfMax];
data[indexOfMax] = temp;
}
}
How can we factor out the differences in these two routines so we
can use a single sort routine?
public class Comparator
{
public int compare(String o1, String o2)
{
return s1.compareTo(s2);
}
public int compare(Integer o1, Integer o2)
{
int i1 = o1.intValue();
int i2 = o2.intValue();
return i1 – i2;
}
...compare(…) methods for other types of data
}
Let us decide that compare methods will return …
a negative integer if o1 is “less than” o2.
0 if o1 is “equals” o2.
a positive integer if o1 is “greater than” o2.
Now we can sort arbitrary objects with the same sort method:
Lecture 18
Object-Oriented Languages and Systems
5
public static void sort(Object[] data, Comparator comp)
{
for (int i = data.length-1; i >=1; i--) {
// in each iteration through the loop
// swap the largest value in data[0..i] into pos’n i
//find the index of the largest value in data[0.. i]
int indexOfMax = 0;
for (int j = 1; j <= i; j++) {
if (comp.compare(data[j], data[indexOfMax]) > 0)
indexOfMax = j;
}
// swap the largest value into position i
Object temp = data[i];
data[i] = data[indexOfMax];
data[indexOfMax] = temp;
}
}
The problem is that the compiler can’t determine which compare
method to call. Why?
So, let’s give Comparator a compare method that takes two
Objects as parameters:
public class Comparator
{
public int compare(Object o1, Object o2)
{
if( o1 instanceof String && o2 instanceof String )
return ((String) o1).compareTo((String) o2);
else if
(o1 instanceof Integer && o2 instanceof Integer) {
return (Integer) o1 – (Integer) o2
}
else
...deal with all the other types of data...
}
}
This is quite inelegant. Why?
CSC/ECE 517 Lecture Notes
© 2007, Edward F. Gehringer
6
OK, let’s think. What feature of Java can we use to have the
compiler tell us that we need to add new methods when we add a
new class?
public
Comparator
{
public int compare(Object o1, Object o2);
}
Now, how are we going to code these for our Integer and String
classes?
public class StringComparator
{
public int compare(Object o1, Object o2) {
String s1 = (String) o1;
String s2 = (String) o2;
return s1.compareTo(s2);
}
}
public class IntegerComparator
{
public int compare(Object o1, Object o2) {
return (Integer) o1) – (Integer) o2);
}
}
Does this solve the problem of trying to compare different kinds of
objects?
Here's how a client who wants to sort an array of Strings
alphabetically might use our sort method.
String[] data = ...initialize the data array...
comp = new
Lecture 18
Object-Oriented Languages and Systems
7
Sorter.sort(data, comp);
Here is a UML class diagram showing these relationships.
To sort an array of Strings ignoring the upper or lower case of the
letters, the user can do exactly the same thing except define a
different class implementing Comparator as follows:
public class StringIgnoreCaseComparator {
public int compare(Object o1, Object o2) {
String s1 = (String) o1;
String s2 = (String) o2;
return s1.compareToIgnoreCase(s2);
}
}
The text carries this example through in more detail, giving the final
code for all of the classes. Note that Comparator is defined in
java.util.Comparator.
Inheritance vs. delegation
[Skrien §3.12] Delegation can often achieve the same effect as
inheritance. Let’s look at another example.
Consider the java.util.Stack class. How many operations does
it have?
Suppose in a program you want a “pure” stack class—one that can
only be manipulated via push(…) and pop().
CSC/ECE 517 Lecture Notes
© 2007, Edward F. Gehringer
8
Why would you want such a class, when Java already gives you that
and more?
What is the “simplest” way to get a pure Stack class?
Or you could create Stack class “from scratch.” What’s wrong with
doing this?
Another option is to create your own Stack class, but have it include
a java.util.Stack.
What is the name for the approach are we using here?
Here’s what this class might look like.
public class MyStack
{
private java.util.Stack stack;
public MyStack(){stack = new java.util.Stack();}
public void push(Object o) { stack.push(o); }
public Object pop() { return stack.pop(); }
public object peek() { return stack.peek(); }
public boolean isEmpty(){return stack.empty();}
}
Let’s consider inheritance and delegation, from these perspectives.
Polymorphism.
Interface.
Lecture 18
Object-Oriented Languages and Systems
9
Efficiency.
Amount of code.
Readability
[Skrien §4.0] “Programs must be written for people to read, and only
incidentally for machines to execute.”—Abelson & Sussman
Use a good set of coding conventions, such as the ones given in
http://java.sun.com/docs/codeconv/.
Guideline: Give a variable the narrowest scope that you can.
Give an example of this principle.
Why is this a good principle?
Guideline: Using standard idioms, make code as concise as
possible.
Example: In the following statement, b is a boolean variable:
if (b == true)
return true;
else
return false;
This statement is far too verbose. An equivalent and much more
readable statement is—
In most cases, this can be made even more readable. How?
Guideline: Variable names should be neither too short nor too long.
Consider a variable that controls whether a while-loop is exited.
CSC/ECE 517 Lecture Notes
© 2007, Edward F. Gehringer
10
while (variable) {
…
}
What is a good name for this variable?
Guideline: Factor out duplicated code.
If a program has two places where the same sequence of
instructions is being executed, it is almost always beneficial to move
the duplicated code into a separate procedure.
Example: Suppose you are developing a class of objects one of
whose responsibilities is to parse an input string, such as a
complicated mathematical expression.
Part of the process of parsing involves checking that the input is
valid. So the class might have a method like this:
public void parse(String expression)
{
...do some parsing...
if( ! nextToken.equals("+") ) {
//error
System.out.println
("Expected +, but found " + nextToken);
System.exit(1);
}
...do some more parsing...
if( ! nextToken.equals("*") ) {
//error
System.out.println
("Expected *, but found " + nextToken);
System.exit(1);
}
...
}
Lecture 18
Object-Oriented Languages and Systems
11
How can we clean this code up?
private void handleError(String message) {
System.out.println(message);
System.exit(1);
}
public void parse(String expression)
{
...do some parsing...
if( ! nextToken.equals("+") )
...do some more parsing...
if( ! nextToken.equals("*") )
...
}
Besides being more readable, this code has another advantage.
What?
Guideline: A method should do only one thing and do it well.
Here's an example of a method to avoid:
void doThisOrThat(boolean flag) {
if( flag ) {
...twenty lines of code to do this...
}
else {
...twenty lines of code to do that...
}
}
CSC/ECE 517 Lecture Notes
© 2007, Edward F. Gehringer
12
How should we change it?
Lecture 18
Object-Oriented Languages and Systems
13
© Copyright 2026 Paperzz