A Hidden Delegation of Middle Men
I remarked back in I get it – pun intended that the Hide Delegate refactoring can help turn your code into OO code.
This is certainly the case when the objects are domain objects. There are situations where you should hide delegates, and times when you shouldn’t.
Take the following two exercises from the Refactoring Workbook:
“Consider the following classes:”
- “Write the implied classes (and tests). The maxDays() method computes the largest number of days for any ShippingOption in the purchase.”
I won’t do this but there will be a lot of delegation.
- “Apply Hide Delegate so Cart accesses only Purchase directly.”
Ditto.
- “Hide Delegate causes the Middle Man (Purchase) class to have a wider interface; that is, it exposes more methods. But applying that refactoring can open up a way to make the interface narrower. Explain this apparent contradiction.”
Now this made me stop and think. And this will probably require some code. After applying Hide Delegate, Purchase would probably look like this:
interface Purchase
{
int whatIsTheCostOfTheItem();
int whatIsTheCostOfTheShippingOption();
int howLongWillTheShipmentTake();
}
The wider interface refers to having three methods instead of two. The question is how this can help you make the interface narrower. What I think this question is getting at is the fact that once you hide the delegate, you can make the interface whatever you want. - “Use this line of reasoning to narrow the Purchase interface.”
enum Attribute { COST_OF_ITEM, COST_OF_SHIPPING, LENGTH_OF_SHIPMENT };interface Purchase
{
int whatIsThe(Attribute attrName);
}// usage:
int totalCost = somePurchase.whatIsThe(COST_OF_ITEM) + somePurchase.whatIsThe(COST_OF_SHIPPING);
- “Notice that the primitive int type is used to represent money. Would it be easier to introduce a Money class before or after the delegate changes?”
I think the amount of pain is the same either way.
The next exercise shows when not to remove Middle Men.
“Consider these classes:
(This code has been greatly simplified; the queue wasn’t originally storing strings.)”
Queue.java
import java.util.ArrayList;
public class Queue {
ArrayList delegate = new ArrayList();
public Queue() {}
public void addRear(String s) {delegate.add(s);}
public int getSize() {return delegate.size();}
public String removeFront() {
String result = delegate.get(0).toString();
delegate.remove(0);
return result;
}
}
QueueTest.java (selection)
public void testQ() {
Queue q = new Queue();
q.addRear(“E1″);
q.addRear(“E2″);
assertEquals(“E1″, q.removeFront());
assertEquals(“E2″, q.removeFront());
assertEquals(0, q.getSize());
}
- “Remove Middle Man so that the queue is no longer a middle man for the ArrayList. Is this an improvement?”
Clearly not; the Queue class provides an abstraction on top of the ArrayList. It controls access to the ArrayList based on Queue semantics.
These two examples show when hiding delegates are appropriate: when either an interface can be made narrower or the abstraction benefits from the encapsulation.
In a broader sense, I think hiding delegates applies at the DDD Aggregate level. And in general, domain objects shouldn’t have any getThis().getThat() chains if you are disciplined in making sure that all of your methods have domain meaning.
But, my biggest beef with textbooks, and college in general, is that the real world rarely works out like this, or at least not every situation is like this in The Industry.
You see, in The Industry, there is UI display and persistence. There is also a lot of book-keeping going on that doesn’t necessarily have any behavior attached. In my world, these classes would not have any methods, since they have no behavior. I would give them some restricted way, of course, for UI factories, regular factories, and repositories to get at their internals.
And chances are, such objects are rarely flat; they contain other objects which are similarly devoid of behavior. Restricted access to the internals of a top-level object will end up having a getter of some kind that returns the inner object:
customer.getOrders()
By induction, this leads to the Message Chain smell, and we’re inclined to Hide Delegate. But how would you decide where to hide?
You can’t follow the contours of the problem, because there aren’t any. These methods are strictly for domain-related concerns. The Fowler book suggests to consider how the retrieved object is being used and to Move or Extract Method accordingly. In this case, the use is the same in all cases: we’re getting an object so we can instantiate or persist.
I’m inclined to suggest that objects such as factories are, by their nature, coupled to the structure of their products. This is true in the case of the Friend Class pattern.
So we could relent and let factories be coupled to the structure of their products, from top object all the way down. But this can get out of hand if the structure is elaborate. This begs the question: how many factories should you have?
It always depends. The Evans doctrine is wisely noncommittal: whenever a constructor starts getting out of hand.
Thus, it would be unwise to always delegate to the factories of sub-objects, especially if those sub-objects don’t need factories to start with. Also unwise is turning the top-level object into a Middle Man. I think the right level of abstraction will emerge with the natural friction of how complicated the object becomes. You have to pick your spots.
The “real world” is a world where you have to pick and choose your spots when it comes to patterns and refactoring. Just because you can do it, doesn’t mean you should or need to.
This entry was posted on October 1, 2008 at 7:37 pm and is filed under Design Issues with tags getters and setters are evil, hide delegate, message chains, middle man, refactoring. You can follow any responses to this entry through the RSS 2.0 feed You can leave a response, or trackback from your own site.
