Sunday, October 28, 2007

Comparing Apples Using the Adapter Pattern

Last week my pair and I came up with an elegant solution to a problem that could have seriously bloated an already busy class.
The class was called Matcher and part of its job is to Match a Person to a Contact (the objects represent models from 2 different systems) with the method isMatch. The rules for matching were to first check that the full names were the same and, if they were, then check to see if either the email addresses match or one of the 6 phone fields. If only the names matched the method should return false.
The problem itself isn't very hard, but looking at the rest of the class I did not want to clutter this method up with a bunch of if-then-else statements and, more importantly, I wanted the code to be readable.
Looking at both Contact and Person, my pair and I noticed that they both had similar attributes (first name, last name, email address, day phone, etc), however, the field names were slightly different. Our approach was to use the Adapter Pattern to change Contact and Person into MatchableContacts. MatchableContact would have a method match() which would take in a MatchableContact and would return a Boolean based on the Names AND (Email Addresses OR Day Phones OR Evening Phones OR etc...)
What this did was turn 50 lines of code in isMatch() to 3 lines and made the code significantly more readable. Instead of if-then-else we had:
MatchableContact matchable1 = MatchableContact.getInstance(person);
matchableContact matchable2 = MatchableContact.getInstance(contact);
return matchable1.match(matchable2);
And the match method looked like this:
return this.getFullName.equals(matchableContact.getFullName) && (this.getEmail.equalsIgnoreCase(matchableContact.getEmail) || etc...)
As an added bonus my pair realized that he could now re-use our new MatchableContact class for a problem that he had been working on for the past 2 days!

Thursday, October 18, 2007

Domain Driven Design

At my current client, a colleague started a book club and proposed the book Domain Driven Design by Eric Evans. Today we had our first session and I thought it went very smoothly. Before we started I felt I had a pretty good idea of what Domain Driven Design was but after the discussion I was surprised about how much I learned. I am looking forward to next week's session.

Things I learned:
* Domain Driven Development is the idea of creating a ubiquitous language, meaning the words that you use to talk about your program are the same whether you are talking to a Developer or a Business user.
Example: At the my client we have an Object called ShowPerson, which when I started was a very confusing name. This object represents a cross reference table in the database which relates a Show to a Person. In the business world they call this a Guest of the Show. Well, not only would it have helped me learn the code base (and domain) quicker if this Object would have just been called Guest, but the business users now have to learn what ShowPerson means. How are they supposed to understand a Developer at the stand up when he says "The ShowPerson had a null address that caused the exception." Compare that sentence with "The Guest had a null address that caused an exception." Doesn't it just sound better?
* To say a Domain Model is good, is to say that the model (Object, POJO, Class, etc) contains all the attributes a business user would expect.
* Once you have a well defined rich object graph your output (GUI, Reports, etc) are all window dressing.
* How do you balance Easy Testing verse a good Domain Model?
Example: It was mentioned that in order to make our Show Object a better Domain Object we should have created a method on it called submit(). Instead there is a Submit service that gets passed a Show object. First, why is this not good Domain Driven Design, well to a business user what is a Submit Service? Doesn't it make more sense to Submit a Show rather than to get an instance of Submit Service and pass it a Show?
In the meeting we decided that it would have made a lot more sense if the submit behavior was located on the Show object. But there was already a really nice framework setup to mock the submit show service, if we were to encapsulate the service into Show then we would have to mock out that service.
We strive for easy ways to test our behavior but we also want to strive for a rich domain model. In this case I feel the time it took to come up with this easy to use testing framework would have not been necessary if the models would have encapsulated this logic in the beginning.
*What is the difference between the Schema in a Database and a Rich Domain? Behavior?
Schema only represents data, a rich domain layer represents behavior and interactions between models.
*My colleague gave a great analogy. Having an object with no behavior is like having a Car frame with no engine. If you want the Car to move you have to call a service locater that will get the engine for you. Does that make sense??? NO! You want the car to contain the engine, you don't want to go get the engine every time you want to drive.
* Two separate groups like Development and Business need thick conduits for communication. It isn't good for communication and team building if 1 Developer meets with 10 Business users. Conversely, it is also not healthy for 1 Business user to meet with 10 Developers.
Example: Our Customer Proxy has been meeting with us for quit a while. I finished up a feature and she sent me the instructional text that should be added to the new screens I created. After inserting the text I asked the Tech Support manger to stop by and go through the new feature to see if it made sense to him (Since he would be the one supporting the feature. Even better he had not been apart of any of the planning meetings and would be testing the feature by using only the instructional text and the GUI we had come up with). Looking over the new screen we had come up with, he said "This is terrible, the instructions read like a developer wrote them. We are going to get a bunch of calls from our users if we release this." In this example the Customer Proxy had adapted to developer lingo and had inadvertently written instructions that used Database/Object names and if-then-else like sentences.