Combine State and Behavior

 class​ Hull {
 int​ holes;
 }
 
 
 class​ HullRepairUnit {
 
»void​ repairHole(Hull hull) {
 if​ (isIntact(hull)) {
 return​;
  }
  hull.holes--;
  }
 
»boolean​ isIntact(Hull hull) {
 return​ hull.holes == 0;
  }
 }

The combination of state and behavior is one of the cornerstones of object-oriented programming. Classes containing only behavior but lacking state indicate OO-design problems.

In the code snippet here, the Hull class captures state and tracks the amount of its holes. The HullRepairUnit can fix those holes, and it captures behavior.

The code above separates state and behavior into two distinct classes. This separation is something you’ll find quite often in a beginner’s code. Generic examples are a User and a UserController, or an Order and an OrderManager.

The problem is that this separation prohibits information hiding, and it makes the code more verbose. The Hull class has to provide read and (much worse) write access of its state to the HullRepairUnit. It isn’t easy to prevent other objects from accessing and modifying the number of holes this way. On top of that, there’s also no validation for the hull parameter.

A separation of state and behavior is sometimes hard to detect. As a rule of thumb, you can look out for classes that are simply too large or that operate only on their method parameters. Try to simplify such classes by grouping variables and methods that perform similar tasks into one class each. After that, make sure to do a before-and-after comparison to see if you’ve actually improved the design.

Let’s see how we can build a more convincing combination of state and behavior!

 class​ Hull {
 int​ holes;
 
»void​ repairHole() {
 if​ (isIntact()) {
 return​;
  }
  holes--;
  }
 
»boolean​ isIntact() {
 return​ holes == 0;
  }
 }

The solution above requires substantially less code. The HullRepairUnit is completely gone. Instead, the Hull repairs itself. This may look strange at first, because in reality, there’s probably a robot that repairs the Hull. But as long as this unit has no state and behavior in our program, there shouldn’t be a class that represents it.

The Hull class can provide the functionality itself instead. Similarly, an Order or User in a different type of system can provide functionality on its own without a manager, controller, service, or any other stateless class.

Generally speaking, we combined state and behavior in this solution. In the problem, they were separated into two classes. In the solution, they’re very close to each other, inside the same class. The classes’ methods can simply work with the internal state directly. We also reduced the number of method parameters and made the methods easier to understand. Before, parameters had to be validated, but that’s no longer necessary. And there’s no longer a need to expose the holes attribute to the outside via getters or setters.

To sum up, watch out for methods that only work with their input parameters but not with the instance variables of the class they live in. Those methods indicate a separation of state and behavior and prevent information hiding. With too much information in the open, bugs can easily occur.

Sometimes, frameworks require you to go against this rule. For example, controllers in web frameworks are typically stateless—that is, they have no fields, only method parameters. That’s by design to make it possible to create lots of such controllers to handle a large amount of parallel requests—with the state residing in the database only.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.143.254.151