If you are comfortable with the single responsibility principle, the interface segregation principle will make perfect sense. It’s the same thing, only for interfaces:
An interface should describe one set of behaviors.
You can run into the same trouble with interfaces as with classes. You need to add a new capability to an existing interface, rather than creating a new one. Now all implementing classes have one more thing they have to account for. 😐
Keeping interfaces small and to the point decreases coupling. Coupling refers to how closely connected two pieces of software are. The more an interface defines, the more an implementing class needs to do as well. That makes that class less reusable.
How Do We Apply the Interface Segregation Principle to Our Code?
Since you eventually want to implement a GUI for the view, you can extract an interface for the controller to interact with. To do this, pull the methods currently in the view, and put them into a new interface called GameViewable.
Let's do it together!
public interface GameViewable {
void setController(GameController controller);
void promptForPlayerName();
void showPlayerName(int playerIndex, String name);
void showFaceDownCardForPlayer(int playerIndex, String name);
void promptForFlip();
void showCardForPlayer(int playerIndex, String name, String rank, String suit);
void showWinner (String winnerName);
void promptForNewGame();
}
Then the CommandLineView just implements the methods. 😎
That said, it’s easy to add responsibilities to an interface that doesn’t belong there. In that case, the interface loses cohesion. For example, our GameViewable deals with commands sent from the controller to the view (following good MVC separation). If you wanted to add a button to reshuffle and re-deal cards (before figuring out the winner), it would be tempting to jump to the conclusion that the view could do it:
public interface GameViewable {
// bad placement of this idea
public void restartGame() {
// doing things without being told
clearOutCardDisplay();
resetNames();
}
};
But now the interface is manipulating its items without a command coming from the controller. 😦 This restarting game concept is functionality that should go into the GameController class, not the GameViewable interface. We are also skipping essential logic. The cards have to come back from the player's hands and put back into the deck. So while this implementation would look like the game was reset, it isn't as far as the controller and business logic is concerned.
Try It Out for Yourself
It's your turn! Add the restarting game functionality to the GameController class to follow the interface segregation principle. For the solution, check out the screencast below.
Let's Recap!
Interface segregation is the single responsibility principle for interfaces.
It’s easy to violate the principle. The temptation is to add a new method to an existing interface, since it’s already doing something.
When in doubt, it's better to have two interfaces with few methods to implement, rather than a single interface with many responsibilities.
In the next chapter, we will look at ensuring that our low-level implementations don't drive higher-level classes' implementations!