Wednesday, June 8, 2016

POODR Notes - Chapter 4: Creating Flexible Interfaces

POODR Chapter 4 highlights the importance of the messages that objects send to each other - their public interfaces. Metz points out that when thinking about design it can be easy to get caught up in focusing on Classes, but that it's more useful to direct your focus to the interaction between classes. This helps to reveal responsibilities that may or may not be fulfilled in your project, and in the latter case, can point you in the direction of creating a new object to fill in the gap.

Creating Flexible Interfaces Key Takeaways:
  • Focus on the messages that pass between domain objects
  • Think of a public interface (the public methods of an object) as "a contract that articulates the responsibilities of your class"
  • Focus on what, not how: when objects are interacting with each other, they should know only what they need from the other, but not how their neighbor implements it
  • Design messages to be as context-independent as possible to make them optimally reusable. See the notes on Chapter 3: Managing Dependencies for tips on how to achieve this.
To explore the design of public interfaces, let's revisit the automated library that runs on Ruby. The use case we need to consider is that a user returns a book to the library; when this happens, their account should be updated so that they can rent out another book (increase their allowance), the book should be stored on a shelf, should have its location updated, and should be marked as available for renting.

My first iteration of the design for this feature was taken quite literally from the description above; the user sends a return message to the book, and to the shelf, but right away that feels wrong. It makes sense that book would have a return method in its public interface, but it is beyond the user's responsibility to send that message to book. There is another responsibility - routing the books - that is missing in our domain so far.

Figure 1: User returns a book use case sequence diagram
Figure 1 illustrates an improved design for this feature. We've added the BookRouter class to take on the responsibility of receiving the user's return(book) message, then communicating with Book and Shelf to implement the rest of the behind-the-scenes business logic. Notice that BookRouter asks for what it wants using Shelf's public interface, store, while trusting that Shelf will know how to go about fulfilling its responsibility.

To apply this to your own work, ask these questions when designing interfaces between objects:
  • Does it make sense that this message is in this object's public interface? In other words, does this message describe a core responsibility of the object?
  • Does it make sense for this message to be received by this receiver?
  • Does it make sense for this message to be sent to this receiver from this sender?