For instance, let us look at a generic queue → A queue that can store object of any type. Sample Implementation:
public class Queue<E>{
public void add(E element) { ... }
public E remove() { ... }
public int size() { ... }
...
}
Create two separate implementations:
public class CircularArrayQueue<E>{
public void add(E element){ ... }
public E remove() { ... }
public int size() { ... }
...
}
public class LinkedListQueue<E>{
public void add(E element){ ... }
public E remove(){ ... }
public int size(){ ... }
...
}
Now the user can choose either of the implementations like below:
CircularArrayQueue<Date> dateq;
LinkedListQueue<String> stringq;
dateq = new CircularArrayQueue<Date>();
stringq = new LinkedListQueue<String>();
What if the user decides at a later point that he need to use another implementation of Queue???→ In this example , if user realizes that he need the size of the dateq
to be flexible (i.e. Implemented using LinkedList).
CircularArrayQueue
to LinkedListQueue
CircularArrayQueue
, the function headers of those functions will have the function parameter declared as CircularArrayQueue
.So everywhere this type comes, the user will need to replace the code.Solution: Adding Indirection
Create a Queue interface.
public interface Queue<E>{
abstract void add(E element);
abstract E remove();
abstract int size();
}
Now provide concrete implementations by implementing this interface:
public class CircularArrayQueue<E> implements Queue<E>{
public void add(E element){ ... }
public E remove(){ ... }
public int size(){ ... }
...
}
public class LinkedListQueue<E> implements Queue<E>{
public void add(E element){ ... }
public E remove(){ ... }
public int size(){ ... }
...
}