MSDN Magazine - October 2008 - (Page 47) This type of code can be problematic in a couple of ways. In a system like this, you potentially have duplication because the business rules for an entity are scattered in procedural code outside of the entities. You might duplicate logic unknowingly because it’s not obvious where the previously written business logic would be. Figure 6 shows the same code, but this time following the Tell, Don’t Ask pattern. In this code, I moved the business rules for purchasing and accounts into the Purchase and Account classes themselves. When we go to make a purchase, we just tell the Account class to deduct the purchase from itself. Account and Purchase know about themselves and their internal rules. All a consumer of Account has to know is to call the Account.Deduct(Purchase, PurchaseMessenger) method. The Account and Purchase objects are easier to use because you don’t have to know quite so much about these classes to execute our business logic. We’ve also potentially reduced duplication in the system. The business rules for Accounts and Purchases can be effortlessly reused throughout the system because those rules are inside the Account and Purchase classes instead of being buried inside the code that uses those classes. In addition, it should be easier to change the business rules for Purchases and Accounts as those rules are found in just one place in the system. Closely related to Tell, Don’t Ask is the Information Expert pattern. If you have a new responsibility for your system, in what class should the new responsibility go? The Information Expert pattern asks, who knows the information necessary to fulfill this responsibility? In other words, your first candidate for any new responsibility is the class that already has the data fields affected by that responsibility. In the purchasing sample, the Purchase class knows the information about a purchase that you would use to determine any possible discount rates, so the Purchase class itself is the immediate candidate for calculating discount rates. Figure 6 Telling Your App What to Do public class Purchase { private readonly double _subTotal; public Purchase(double subTotal) { _subTotal = subTotal; } public double Total { get { double discount = _subTotal > 10000 ? .10 : 0; return _subTotal*(1 - discount); } } } public class Account { private double _balance; public void Deduct( Purchase purchase, PurchaseMessenger messenger) { if (purchase.Total < _balance) { _balance -= purchase.Total; } else { messenger.RejectPurchase(purchase, this); } } } public class ClassThatObeysTellDontAsk { public void MakePurchase( Purchase purchase, Account account) { PurchaseMessenger messenger = new PurchaseMessenger(); account.Deduct(purchase, messenger); } } Say It Once and Only Once As an industry we’ve learned that writing reusable code deliberately is very expensive, yet we still try for reuse because of the obvious benefits. It may behoove us to look for duplication in our system and find ways to eliminate or centralize that duplication. One of the best ways to improve cohesion in your system is to eliminate duplication wherever you spot it. It’s probably best if you assume that you don’t know exactly how your system is going to change in the future, but you can improve your code’s ability to accept change by maintaining good cohesion and coupling in class structures. I was peripherally involved years ago with a large shipping application that managed the flow of boxes on a factory floor. In its original incarnation, the system polled a message queue for incoming messages, then responded to those messages by applying a large set of business rules to determine where the boxes went next. The next year the business needed to start the box-routing logic from a desktop client. Unfortunately, the business logic code was far too tightly coupled with the mechanisms for reading and writing to MQ Series queues. It was judged too risky to untangle the original business logic code from the MQ Series infrastructure, so msdnmagazine.com the entire corpus of business rules were duplicated in a parallel library for the new desktop client. That decision made the new desktop client feasible, but it also made all future efforts more difficult because each change to the box-routing logic required a parallel change to two very different libraries—and those kinds of business rules change frequently. This real scenario presents us with a couple of lessons. Duplication in the code had a real cost to the organization that built the system, and that duplication was largely caused by poor coupling Duplication in your code is an opportunity to improve your design later. and cohesion qualities in their class structure. This stuff can directly affect a company’s profit and loss. One way of looking at duplication in your code is as an opportunity to improve your design later. If you find yourself with two or more classes that duplicate some piece of functionality, you can say that the duplicated functionality must be a completely different responsibility. One of the best ways to improve the cohesion quality of your codebase is to simply pull out duplication into separate classes that can be shared across the codebase. October 2008 47 http://www.msdnmagazine.com
For optimal viewing of this digital publication, please enable JavaScript and then refresh the page. If you would like to try to load the digital publication without using Flash Player detection, please click here.