views:

306

answers:

4

Loose coupling, high cohesion for a maintainable application

This is the battle-cry that I hear over and over. There is plenty of advice on how to loosely couple components.

  • Base on interfaces and inject all dependencies
  • Use events
  • Use a service bus
  • etc.

However, I feel like I've never really heard any specific suggestions for increasing cohesion. Does anyone have any to offer?

You can start answering there but I have as specific situation in mind that I would like advice on also.


I have a fairly loosely coupled C# Windows Forms MVP application that bases many of its components off of interfaces, injects them through constructors and uses an Inversion of Control container (Castle Windsor) to assemble it all together.

I would say its quite well architected - it has undergone several big change requests already and handled them quite easily. I am overall quite satisfied with it but I just can't let go of a nagging doubt that it's not particularly cohesive. This is not a problem for me as the sole developer but I fear it can get quite confusing for someone that comes into the application for the first time.

Let me give you an example - the application is used by Company A to fill and process outgoing trucks with product. This means that there is an OutgoingTransactionInfo object, an OutgoingTransactionInfoControl (for actually entering said information), OutgoingTransactionValidator, and OutgoingTransactionPersister. With the app in production, we got a request to handle incoming transactions as well - these get different information attached, different validation, and a different way of persisting them. Then Company B wanted to use the application for their transaction processing too, the idea is the similar but again, info, validations, persistence, and maybe a few other components differ.

Because my application has a good testing suite and is loose I've been able to accommodate these requests quite easily. However I recognize that it is all too easy to accidentally configure it in an invalid state. For example, you can wire it to use OutgoingTransactionInfo objects while validating with an IncomingTransactionValidator. If the differences are subtle the error might even go undetected for some time.

Do you have any suggestions for me? What techniques have you use to mitigate such risk?

A: 

You achieve a highly cohesive system when each component of the system is focused on one responsibility. As the saying goes, "Do one thing and do it well." When you feel like a class is starting to do "too much" you can refactor it into two or more classes, each of which is responsible for one thing. This leads to loose coupling: because the responsibilities have each been localized to a particular class, the classes are more likely to have very discrete interactions with one another.

Quick suggestion for your wiring problem: couldn't your OutgoingAbc class check to ensure that it's receiving a OutgoingXyz when wired together and throw an exception if it's of the wrong type?

cliff.meyers
Well that just sounds like another term for separation of concerns and single responsibility. Maybe I have the definition of cohesion wrong but I thought it had to do with highlighting related components in such a way that it is obvious they are related.
George Mauer
I think that's actually what cohesion means.http://en.wikipedia.org/wiki/Cohesion_(computer_science)"In object-oriented programming, if the methods that serve the given class tend to be similar in many aspects the class is said to have high cohesion."
cliff.meyers
+2  A: 

Cohesion means that you don't put unrelated stuff together in a module (class, ...). From your description you seem to do this nicely. Proper testing (which you seem to do as well) should keep the risks in check.

You could probably use the type system (generics / templates) to make sure that info and its validator fit together and possibly save some code / make it more uniform (make sure the added complexity is worth it, though).

Otherwise keep up the good work.

starblue
+1  A: 

One thing that comes to mind is to control visibility. (I'll speak in Java terms, but I guess that the concepts tranfer to other languages). What can "see" OutgoingTransactionValidator? Is it legitimate for it to be visible outside the my.org.outgoing package?

So miswiring can be controlled by not declaring something public.

Taking this concept a stage further OSGi allows you to explicitly declare which packages a bundle exports and which packages a bundle uses, so the dependencies become much more explicit, and controlled.

djna
Hmm yes, I can see what you mean by that. Does that not interfere with IoC use though? Or do you have containers at separate tiers?
George Mauer
with my OSGi work I haven't needed much IOC. So I've not fully explored the idea. In terms of cohesion the OSGi bundle seems a useful idea. I wonder whetehr the the miswiring problems you experience might be better addressed by finer-grained interface defintions?
djna
+1  A: 

Here's an interesting presentation by Jim Weirich on the issues of coupling and cohesion from a Ruby point of view from the 2009 Mountain West Ruby Conference. Deals with the various degrees and when they may or may not be appropriate.

James Thompson