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?

Tuesday, March 29, 2016

POODR Notes - Chapter 3: Managing Dependencies


Welcome to the 2nd post in the POODR review series; here, we'll discuss my highlights from Chapter 3: Managing Dependencies. As discussed in the last post, object-oriented programming is about objects and behavior, and the Single Responsibility Principle (SRP) guides us to write objects with their own narrow and clearly delimited scope of data and behavior. In order for the app or program to achieve its mission, it needs those objects to collaborate with each other - this is where managing dependencies comes in, and it is another part of writing classes that are TRUE (Transparent, Reasonable, Useable, and Exemplary).

Identifying Dependencies

Your object has a dependency whenever it has to rely on another object in order to do its own work. To identify these situations, consider whether an object knows the:
  • Name of another class,
  • Name of a message/method it intends to send to someone other than self,
  • Arguments that a message/method requires, or
  • Order of the arguments required.
Each of these kinds of "knowledge" represent a dependency; we call it "managing" dependencies because some reliance on other objects is expected and required, but we don't want it to get out of hand. So if your object knows any of the things in that list, it knows too much, and refactoring requires a harm reduction approach. POODR describes various techniques for managing dependencies and making code as TRUE as possible.

Injecting Dependencies

One way to manage a dependency is to inject it by passing the object in as a parameter. Building the method this way abstracts out references to the type of the object so we can focus on the behavior we are trying to get to, instead of on the kind of thing that can do that behavior. For our examples, we'll continue within the domain of the fully automated library that runs on Ruby.

Here we are writing the signature so that we are passing in an object that responds to length, width, and depth. This makes our code re-useable; if we expand our library collection to include DVDs, our code still works and nothing has to change (except maybe the name of the argument; something like "storable" would work nicely). This way, we are still expecting Shelf to know the name of a message (length) it wants to send to something else, but it's not concerned with what kind of thing that something else is - anything that can respond to that message will work.

Removing Argument-Order Dependencies - WITH RUBY 2.0 UPDATE!!

This technique addresses the 3rd and 4th kind of dependency traps in our list above, and it's pretty straightforward, as Metz describes it, and even more so thanks to updates in Ruby 2.0 and 2.1. In previous versions, the way to get away from argument-order dependencies was to use a hash, which also had the benefit of giving us a way to assign default values, as demonstrated in the example below:

Metz pointed out that even though this adds some verbosity, adding a few extra lines of code for the sake of clarity and reusability is warranted. However, in Ruby versions 2.0 and greater, we can get this functionality for free, thanks to first-class support for keyword arguments. Version 2.1 added in support for required keyword arguments. Read this great post that explains more about Ruby 2+ keyword args, and see the gist below for the updated syntax for our Book initialize method.

Abstracting External Messages with Wrapper Methods

Yet another way to deal with dependencies, especially in cases where we can only change certain parts of the code we are using, is to make them more explicit by abstracting into a wrapper method whatever behavior we are trying to get at - I think of this as leaving a warning sign for the next developer. Metz doesn't talk about this yet, but this will also help with testing; we'll discuss this more in future posts.

This is a bit of a contrived example, but the idea is that we want to compose our methods so that we're relying on messages sent to self as much as possible. In the version of class without a wrapper, buried in the complicated store method is a message sent to book. This is potentially dangerous because it is easy to overlook that external message in a method with a lot of other things going on. In order to be able to focus on just the essential work of the store method, we can abstract out the message sent to book, as we do in the second version of Shelf.

Conclusion

So far, we can sum up what we've learned from POODR this way: TRUE code is a pleasure to work on and relatively inexpensive for the business - this is our aim. If the code is TRUE, a class has one and only one responsibility, and when it must collaborate with other objects, it should know as little as possible about its neighbor in order to get the job done. It may know the name of a message it wants to send to its neighbor, but having to know the order of arguments for that message is asking too much. Using the techniques in this post, we can manage those dependencies and go on blissfully changing our code whenever we get a new requirement.

Tuesday, March 15, 2016

POODR Notes - Chapter 2: Designing Classes with a Single Responsibility

These days, I've been thinking a lot about architecture and design, and I'm revisiting "Practical Object Oriented Design in Ruby." I've read POODR before, many times, but always in bits and pieces, often on the subway. I'd look for insight on a particular point (for example, dependency injection), without reading more about the bigger picture (say, the whole chapter about managing dependencies). So I decided to study smarter by reading whole chapters at a time, underlining and taking margin notes, then going back through to write out (using actual paper!!!) a summary of the main points and what I've learned.

Code needs to be TRUE

This is what I'll be sharing with you, starting with Chapter 2: Designing Classes with a Single Responsibility. This chapter is all about how to organize your code so that it does what you need it to do at the moment and is easy to change and reorganize as you get more information about what the app needs to do. A mnemonic for this idea is that code needs to be TRUE: Transparent about the cost of change, which should be Reasonable relative to the benefit that change would produce, Usable now and in the case of future changes, and Exemplary so that the style and patterns you lay out are worthy of replication as the app grows.

To be the most TRUE, classes should follow the Single Responsibility Principle (SRP), which means it should have one and only one responsibility.  When describing an app, the nouns you use represent classes, as long as they have both data and behavior. As an example, let's say we have a fully automated library that runs on Ruby. A patron comes to the circulation kiosk to identify a book they want to rent and the librarian robot retrieves it from its location in the shelves. Since this is a library, let's start with our Book class.

What is the responsibility of our Book class? As it is now, a book's responsibility is to manage its own properties, like author and title, and to manage its own storage. There are two things in that last sentence that stand out right away. First, if we describe a class's responsibility as "doing this thing" and "doing this other thing," as we just did, there is more than one responsibility. Additionally, a book "storing itself somewhere" doesn't really make sense; a book shouldn't be concerned with its own storage because that is the job of another object, which in our case is Shelf.

Is my class abiding by the SRP?

Now we have two classes: Shelf and Book, each of them abiding by the single responsibility principle. Our code is TRUE: our program does what we need it to do now, and is also organized in a way that will make it easy to change in the future. I'll leave you here with some questions that will help to assess whether a class has one and only one responsibility are:

  • What is the responsibility of this class? If the description includes the word "and" or "or" between two different types of concerns, isolate the different responsibilities in separate classes.
  • Should this domain object be its own class? The answer is yes if it has both data and behavior.
In the next post, we'll look at Chapter 3: Managing Dependencies and how that will help us to write TRUE code. In the comments, share your strategies for making sure your code follows the SRP and how you make it as TRUE as can be.