MSDN Magazine - October 2008 - (Page 44) Figure 2 Inappropriate Intimacy public void Process() { string connectionString = getConnectionString(); SqlConnection connection = new SqlConnection(connectionString); DataServer1 server = new DataServer1(connection); int daysOld = 5; using (SqlDataReader reader = server.GetWorkItemData(daysOld)) { while (reader.Read()) { string name = reader.GetString(0); string location = reader.GetString(1); } processItem(name, location); } } theme of the TrainSchedule class, move those methods and data into the TrainSchedule. One of my favorite sayings about design is applicable here: put code where you’d expect to find it. It’s most logical to me that functionality involving a train schedule would be in the TrainSchedule class. To stretch my earlier conversation analogy, a system consisting of cohesive classes and subsystems is like a well-designed online discussion group. Each area in the online group is narrowly focused on one specific topic so the discussion is easy to follow, and if you’re looking for a dialog on a certain subject, there’s only one room you have to visit. Inappropriate intimacy refers to a method in a class that has too much intimate knowledge of another class. Inappropriate intimacy is a sign of harmful, tight coupling between classes. Let’s say that we have a business logic class that calls an instance of class DataServer1 to get the data it needs for its business-logic processing. Figure 2 shows an example. In this case, the Process method has to know a lot of the inner workings of DataServer1 and a bit about the SqlDataReader class. Now, let’s rewrite the code in Figure 2 to remove the inappropriate intimacy: public void Process() { DataServer2 server = new DataServer2(); foreach (DataItem item in server.GetWorkItemData(5)) { processItem(item); } } purpose this business logic for usage against data entered directly into an Excel spreadsheet by analysts? What if we want to test or debug the business logic by itself? We can’t do any of that because the business logic is tightly coupled to the data-access code. The business logic would be a lot easier to change if we could isolate it from the other concerns. To summarize, the goals behind achieving loose coupling between classes and modules are to: 1. Make the code easier to read. 2. Make our classes easier to consume by other developers by hiding the ugly inner workings of our classes behind well-designed APIs. 3. Isolate potential changes to a small area of code. 4. Reuse classes in completely new contexts. Eliminate Inappropriate Intimacy Increase Cohesion The academic definition of cohesion is that it is a measure of how closely related all the responsibilities, data, and methods of a class are to each other. I like to think of cohesion as a measure of whether a class has a well-defined role within the system. We generally consider high cohesion to be a good thing and repeat the words “highly cohesive” like a mantra. But why? Let’s think of coding as having a conversation with the computer. More accurately, we’re having several simultaneous conversations with the computer. We’re having conversations about how security is carried out, how the infrastructure concerns are supposed to behave, and what the business rules are. When you’re at a loud party with a lot of different conversations going on at once, it can be difficult to focus on the one conversation you’re trying to have. It’s much easier to have a conversation in a quiet setting where there’s only one conversation going on. An easy test for cohesion is to look at a class and decide whether all the contents of the class are directly related to and described by the name of the class—vague class names, such as InvoiceManager, do not count. If the class has responsibilities that don’t relate to its name, those responsibilities probably belong to a different class. If you find a subset of methods and fields that could easily be grouped separately under another class name, then maybe you should extract these methods and fields to a new class. To illustrate, if you find methods and data in your TrainStation, Train, and Conductor classes that seem to most accurately fit the 44 msdn magazine As you can see in this version of the code, I’ve encapsulated all the SqlConnection and SqlDataReader object manipulation inside the DataServer2 class. DataServer2 is also assumed to be taking care of its own configuration, so the new Process method doesn’t have to know anything about setting up DataServer2. The DataItem objects returned from GetWorkItemData are also strongly typed objects. Now, let’s analyze the two versions of Process method against some of the goals of loose coupling. First, what about making the code easier to read? The first and second versions of Process perform the Figure 3 Breaking the Law public interface DataService { InsuranceClaim[] FindClaims(Customer customer); } public class Repository { public DataService InnerService { get; set; } } public class ClassThatNeedsInsuranceClaim { private Repository _repository; public ClassThatNeedsInsuranceClaim(Repository repository) { _repository = repository; } public void TallyAllTheOutstandingClaims(Customer customer) { // This line of code violates the Law of Demeter InsuranceClaim[] claims = _repository.InnerService.FindClaims(customer); } } Patterns in Practice
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.