Sunday, April 6, 2014

Using the "parameter object" pattern reduces coupling and code changes

I have seen a pattern before something like this:
ThingA thinga = createThingA()
ThingB thingb = createThingB()
ThingC thingc = createThingC()
ThingD thingd = createThingD()
 
HandlerA handlera = new HandlerA()
 
handlera.process(thinga, thingb, thingc, thingd)
 
HandlerA {
 void process(ThingA thinga, ThingB thingb, ThingC thingc, ThingD thingd) {
    handlerb.process(thinga, thingb, thingc, thingd)
 }
 
HandlerB {
 void process(ThingA thinga, ThingB thingb, ThingC thingc, ThingD thingd) {
    thinga.doSomething()
    thingb.doSomethingAlso()
    handlerc.process(thinga, thingb, thingc, thingd)
 }
 
HandlerC {
 void process(ThingA thinga, ThingB thingb, ThingC thingc, ThingD thingd) {
    thinga.doSomethingMore()
    thingc.doSomethingAlso()
    handlerd.process(thinga, thingb, thingc, thingd)
 } 
 
HandlerD {
 void process(ThingA thinga, ThingB thingb, ThingC thingc, ThingD thingd) {
    thingd.doSomethingMore()
 }
Notice that ultimately it is not until 4 layers deep that thingd is actually used, but because the objects are all created and owned at the top level and handlerd is the class that needs thingd, it has to be passed all the way through the various layers, even though only handlerd cares about thingd. This is a bad form of coupling and should smell funny.

However, this gets really bad when requirements change and now we need a thinge which is created and owned at the top level and handlerd needs it to implement the new changes. Now we have to change the method signature of all the higher up methods to pass this value in. Now this stinks, not just smells.

Assuming that we need to pass these values around, a better way is to create another class, maybe HandlerContext. HandlerContext contains all the things. And only the HandlerContext is passed to each handler. Then when need to add a new thing, only the HandlerContext class changes and only handlerd even needs to know about thinge. No signatures change.
ThingA thinga = createThingA()
ThingB thingb = createThingB()
ThingC thingc = createThingC()
ThingD thingd = createThingD()
ThingE thinge = createThingE()
 
HandlerA handlera = new HandlerA()
HandlerContext context = new HandlerContext(things, thingb, thingc, thingd, thinge)
 
handlera.process(context)
 
HandlerA {
 void process(HandlerContext context) {
    handlerb.process(context)
 }
 
HandlerB {
 void process(HandlerContext context) {
    context.thinga.doSomething()
    context.thingb.doSomethingAlso()
    handlerc.process(context)
 }
 
HandlerC {
 void process(HandlerContext context) {
    context.thinga.doSomethingMore()
    context.thingc.doSomethingAlso()
    handlerd.process(context)
 }
 
HandlerD {
 void process(HandlerContext context) {
    context.thingd.doSomethingMore()
    context.thinge.doSomethingMore()
 }
A nice reference page for this is located at http://sourcemaking.com/refactoring/introduce-parameter-object. He also does a nice job about showing how to more effectively encapsulate common logic associated with parameter objects as well.

No comments:

Post a Comment