Interfaces are useful when you have two classes which need to work together but should be decoupled from each other as much as possible. A common example of this is when you use listeners to connect model and view together in the model-view-controller design pattern.
For example, let's say you had a GUI application where users could log in and log out. When users log out you might, say, change your "Currently logged in as So-and-So" label and close all of the visible dialog windows.
Now you have a User
class with a logOut
method, and whenever logOut
is called you want all of these things to happen. One way to do that is have the logOut
method handle all of these tasks:
// Bad!
public void logOut() {
userNameLabel.setText("Nobody is logged in");
userProfileWindow.close();
}
This is frowned upon because your User
class is now tightly coupled to your GUI. It would be better to have the User
class be dumber and not do so much. Instead of closing userProfileWindow
itself it should just tell userProfileWindow
that the user has logged out and let userProfileWindow
do whatever it wants to do (it wants to close itself).
The way to do this is by creating a generic UserListener
interface with a method loggedOut
that is called by the User
class when the user logs out. Anybody who wants to know when the user logs in and logs out will then implement this interface.
public class User {
// We'll keep a list of people who want to be notified about logouts. We don't know
// who they are, and we don't care. Anybody who wants to be notified will be
// notified.
private static List<UserListener> listeners;
public void addListener(UserListener listener) {
listeners.add(listener);
}
// This will get called by... actually, the User class doesn't know who's calling
// this or why. It might be a MainMenu object because the user selected the Log Out
// option, or an InactivityTimer object that hasn't seen the mouse move in 15
// minutes, who knows?
public void logOut() {
// Do whatever internal bookkeeping needs to be done.
currentUser = null;
// Now that the user is logged out, let everyone know!
for (UserListener listener: listeners) {
listener.loggedOut(this);
}
}
}
// Anybody who cares about logouts will implement this interface and call
// User.addListener.
public interface UserListener {
// This is an abstract method. Each different type of listener will implement this
// method and do whatever it is they need to do when the user logs out.
void loggedOut(User user);
}
// Imagine this is a window that shows the user's name, password, e-mail address, etc.
// When the user logs out this window needs to take action, namely by closing itself so
// this information isn't viewable by other users. To get notified it implements the
// UserListener interface and registers itself with the User class. Now the User.logOut
// method will cause this window to close, even though the User.java source file has no
// mention whatsoever of UserProfileWindow.
public class UserProfileWindow implements UserListener {
public UserProfileWindow() {
// This is a good place to register ourselves as interested observers of logout
// events.
User.addListener(this);
}
// Here we provide our own implementation of the abstract loggedOut method.
public void loggedOut(User user) {
this.close();
}
}
The order of operations will look like this:
- The application starts and a user logs in. She opens her
UserProfileWindow
.
- The
UserProfileWindow
adds itself as a UserListener
.
- The user goes idle and doesn't touch the keyboard or mouse for 15 minutes.
- An imagined
InactivityTimer
class notices and calls User.logOut
.
User.logOut
updates the model, clearing the currentUser
variable. Now if anybody asks, there's nobody logged in.
User.logOut
loops through its listener list, calling loggedOut()
on each listener.
- The
UserProfileWindow
's loggedOut()
method is invoked, which closes the window.
This is great because this User
class knows absolutely nothing about who needs to know about log out events. It doesn't know that the user name label needs to be updated, that the profile window needs to be closed, none of that. If later we decide more things need to be done when a user logs out, the User
class does not need to be changed at all.
So, the listener pattern is one example of where interfaces are super useful. Interfaces are all about decoupling classes, removing ties and dependencies between classes that need to interact with each other but should not have strong ties in their code to each other.