You are on page 1of 5

Reflections

1. Inheritance vs Composition

Inheritance: is-a (car is a vehicle)


Composition: has-a (car has an engine)

When the inheritance is-a role is not “constant” throughout the application: An employee is a
person but could be just a role which might not be valid when a person becomes unemployed.
And the role could be both employee and supervisor: Use composition.

Class Person {
Role workingRole;
// …
}

2. Abstract classes vs Interface

Abstract classes: Sharing structure code among inherited classes (super- and sub-)
Interfaces: Sharing specific behavior without code.

Abstract classes: Code applicable to all sub classes:

Public abstract class AST {


//…
protected ArrayList<AST> kids;

public AST getKid( int i ) {


if ( (i <= 0) || (i > kidCount())) {
return null;
}
return kids.get(i – 1);
}
}

Interface: consistent behavior with possibly different implementation:

public interface java.util.Enumeration {


public abstract Boolean hasMoreElements();
public abstract object nextElement();
}
3. Advantages and Disadvantages of inheritance and composition

Implementing stack through inheritance from Vector can use protected members of Vector as
well as its methods such as size, isEmpty, including those that might not behave as Stacks
(Vector can remove any item while Stack can only Last-In-First-Out) . Stacks can be
implemented the same as Vectors wherever required as arguments.

Class Stack extends Vector {


Public Object push( Object item ) {
addElement( item );
return item;
}
public Object peek() {
return elementAt( size() – 1 );
}
}

Implementing stack through composition of Vector provides flexibility in changing


implementation of stack composition from vector to array without any impact affecting use of
Stack in code. Vector is embedded in Stack composition that doesn’t require studying its
behavior with smaller surface area, including those methods which doesn’t apply to Stack
(insertElementAt() becomes a behavior of Stack when it’s extended from Vector).

Class Stack {
Private Vector theData;

Public Stack() {
theData = new Vector();
}

public Boolean isEmpty() {


return theData.isEmpty();
}
}

Inheritance:
V Substitutability
V Code reusability with shorter implementation times
V Member and method accessibility (public and protected members of the super class)
V Polymorphism
X Tight coupling (between subclass and superclass)
X Changes to superclass affects all subclasses with rippling effect
X Large surface area (need to understand all superclasses to understand a class’s behavior with
inheritance)
X Possible inappropriate behavior of methods from superclass

Composition:
V Easy to understand available operations (while hiding inherited behaviors not applicable)
V Changes in private member has no effect on class usage.
X No member and method accessibility on composed class, only public members.
X No polymorphism

4. Combining Composition and Inheritance:

V Greatly simplify the inheritance hierarchy


V Very powerful

Example: Java Stream Wrappers

InputStream
ByteArrayInputStream
FileInputStream
PipedInputStream
FilterInputStream
BufferedInputStream
BufferedInputStream
// more
DataInputStream
// more

Class FilterInputStream extends InputStream {


// source of bytes is from any InputStream
protected InputStream in;

FilterInputStream( InputStream in ) {
This.in = in;
}
// etc.
}

Inheritance: FilterInputStreams can be used whenever InputStream is expected.


Composition: Orthogonal sources of variation (bytes from in) are provided with added
functionality (filtering, buffering) without exploded inheritance hierarchy.

Example: BufferedReader

Public class BufferedReader extends Reader


{
Reader in; // composition adds buffering services to the Reader it is wrapping
char[] buffer; // the buffer to store characters read from in
int pos;
int limit;
int markPos = -1;
static final int DEFAULT_BUFFER_SIZE = 8192;
private StringBuffer sbuf = null;
public BufferedReader(Reader in) // use new constructor with DEFAULT_BUFFER_SIZE
{
this(in, DEFAULT_BUFFER_SIZE);
}
public BufferedReader(Reader in, int size) // new constructor
{
super(in.lock);
if (size <= 0)
throw new IllegalArgumentException(“Illegal buffer size: “ + size);
this.in = in;
buffer = new char[size];
}
// …
public Streing readLine() throws IOException
{
// …
}

Inheritance: implementation of shared code


Composition: adding new behavior (buffered read) to its composition wrapped around

5. Dynamic Composition: Combining composition and inheritance to dynamically change


the behavior of a program at run-time.

Class Frog {
Private FrogBehavior behavior;

Public Frog() {
Behavior = new TadpoleBehavior();
}

public grow() {
// see if behavior should change
if (behavior.growUp()) {
behavior = new AdultFrogBehavior();
}
behavior.grow(); // behavior does actual work
behavior.swim();
}
}

abstract class FrogBehavior {


public Boolean growUp() { return false; }
public abstract void grow();
public abstract swim();
}

class TadpoleBehavior extends FrogBehavior {


private int age = 0;
public Boolean growUp() {
return (++age > 24)? true : false;
}
public void grow() {…}
public void swim() {…}
}

class AdultFrogBehavior extends FrogBehavior {


public void grow() {…}
public void swim() {…}
}

You might also like