MSDN Magazine - October 2008 - (Page 45) same basic task, but which is easier to read and understand? Personally, I can more easily read and understand the business logic processing without wading through data-access code. How about making our classes easier to consume? Consumers of DataServer1 have to know how to create a SqlConnection object, understand the structure of the DataReader returned, and iterate through and clean up the DataReader. A consumer of DataServer2 only has to call a no-argument constructor and then call a single method that returns an array of strongly typed objects. DataServer2 is taking care of its own ADO.NET connection setup and cleaning up open DataReaders. And as for isolating potential changes to a small area of code? In the first version of the code, almost any change in the way DataServer works would impact the Process method. In the second, more encapsulated version of DataServer, you could switch the data store to an Oracle database or to an XML file without any effect on the Process method. The Law of Demeter is a design rule of thumb. The terse definition of the law is: only talk to your immediate friends. The Law of Demeter is a warning about the potential dangers of code, as in Figure 3. The ClassThatNeedsInsuranceClaim class needs to get InsuranceClaim data. It has a reference to the Repository class, which itself has a DataService object. ClassThatNeedsInsuranceClaim reaches inside Repository to grab the inner DataService object, then calls Repository.FindClaims to get its data. Note that the call to _repository.InnerService.FindClaims(customer) is a clear violation of the Law of Demeter because ClassThatNeedsInsuranceClaim is directly calling a method on a property of its Repository field. Now please turn your attention to Figure 4, which shows another sample of the same code, but this time it is following the Law of Demeter. What did we accomplish? Repository2 is easier to use than Repository because you have a direct method to call for the InsuranceClaim information. When we were violating the Law of Demeter, the consumers of Repository were tightly coupled to the implementation of Repository. With the revised code, I have more ability to change the Repository implementation to add more caching or swap out the underlying DataService with something completely different. The Law of Demeter is a powerful tool to help you spot potential coupling problems, but don’t follow the Law of Demeter blindly. Violating the Law of Demeter does add tighter coupling to your system, but in some cases you may judge that the potential cost of coupling to a stable element of your code is less expensive than writing a lot of delegation code to eliminate the Law of Demeter violations. Figure 4 Better Decoupling public class Repository2 { private DataService _service; public Repository2(DataService service) { _service = service; } public InsuranceClaim[] FindClaims(Customer customer) { // we’re simply going to delegate to the inner // DataService for now, but who knows what // we want to do in the future? return _service.FindClaims(customer); } } public class ClassThatNeedsInsuranceClaim2 { private Repository2 _repository; public ClassThatNeedsInsuranceClaim2( Repository2 repository) { _repository = repository; } public void TallyAllTheOutstandingClaims( Customer customer) { // This line of code now follows the Law of Demeter InsuranceClaim[] claims = _repository.FindClaims(customer); } The Law of Demeter } object what to do. Following the Tell, Don’t Ask style of object interaction is a good way to ensure that responsibilities are put in the right places. Figure 5 illustrates a violation of Tell, Don’t Ask. The code is taking a purchase of some sort, checking for the possibility of a discount for purchases of more than $10,000, and finally examining the account data to decide whether there are sufficient funds. The previous DumbPurchase and DumbAccount classes are, well, dumb. The account and purchase business rules are both encoded in ClassThatUsesDumbEntities. Figure 5 Asking Too Much public class DumbPurchase { public double SubTotal { get; set; } public double Discount { get; set; } public double Total { get; set; } } public class DumbAccount { public double Balance { get; set;} } public class ClassThatUsesDumbEntities { public void MakePurchase( DumbPurchase purchase, DumbAccount account) { purchase.Discount = purchase.SubTotal > 10000 ? .10 : 0; purchase.Total = purchase.SubTotal*(1 - purchase.Discount); if (purchase.Total < account.Balance) { account.Balance -= purchase.Total; } else { rejectPurchase(purchase, "You don’t have enough money."); } Tell, Don’t Ask The Tell, Don’t Ask design principle urges you to tell objects what to do. What you don’t want to do is ask an object about its internal state, make some decisions about that state, then tell that msdnmagazine.com } } October 2008 45 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.