We've started with an extremelly simple interface SimpleAssignment. It has only one prescribed behavior (simpleCapability()). This interface will act on behalf of the service provider objects we will decide to implement. The service consumer (or, the client) will talk to this interface only.
interface SimpleAssignment{
public void
simpleCapability();
}
We have decided to implement the functionality prescribed in SimpleAssignment interface by creating a SimpleImplementation class. This class implements the simpleCapability() behavior (we are faking this behavior by simply printing a short message to the console).
class SimpleImplementation implements
SimpleAssignment {
public void
simpleCapability(){
System.out.println("from inside SimpleImplementation -- simple capability");
}
}
We have then decided to play with inheritance, and to extend the capability of the SimpleImplementation class. The new class is called MoreComplicatedImplementation, and it introduces a new behavior, notSoSimpleCapability().
class MoreComplicatedImplementation
extends SimpleImplementation {
public void
simpleCapability(){
System.out.println("from inside MoreComplicatedImplementation -- simple
capability");
}
public void
notSoSimpleCapability(){
System.out.println("from inside more complicated impl -- not so simple
capability");
}
}
Furthermore, we have introduced yet another more specialized class, that elaborates upon the capabilities of MoreComplicatedImplementation. That class was, quite frivolously, named EvenMoreComplicatedImpl. It extends (subclasses or inherits) from the MoreComplicatedImplementation
class EvenMoreComplicatedImpl extends
MoreComplicatedImplementation {
public
void simpleCapability(){
System.out.println("from inside EvenMoreComplicatedImplementation -- simple
capability");
}
}
Finally, our consumer object (the Client class) simply uses the interface to send messages to the underlying implementations. Note how the implementation is not being mentioned in the client's code. The only thing that's being mentioned there is the SimpleAssignment interface -- this is the bias of our system. In other words, the system is calibrated to be dependent on the presence of the SimpleAssignment interface. Everything else is completely interchangeable (flexible).
The particular implementation of the service will be defined outside of the application. In this case, we will simply pass it in as a command line parameter. In real life, it will be defined in a separate *.properties file, or in a database. That way, the implementation is controllable by the application administrator (or, the object librarian).
class Client {
public static
void main(String[] args) {
try{
SimpleAssignment simpleAss = (SimpleAssignment)Class.forName(args[0]).newInstance();
simpleAss.simpleCapability();
}
catch(Exception e){
System.out.println("Problem in client " + e);
}
}
}
Note how the client will try to talk to the interface. This is due to the pessimistic nature of the language, which has a built-in awareness that things may, in all likelihood, go wrong. If you remove the try/catch block surrounding the attempt to talk to the service provider, the Client class will simply refuse to compile.