Wednesday, March 20, 2013

SOLID vs GRASP

I have, like I'm sure you have, encountered the "experts" who ask the normal buzzword questions:

So, do you follow the "Gang of Four" patterns?

How about SOLID - do you use that in your job?

Have you used dependency injection techniques?

This reminds me a lot of how our culture was in the late '90s with people dressing in the robes of either Booch or Rumbaugh. Your school of thought and the modeling approach you used was like a religion. Fortunately UML fixed all of that (ya, remember UML?).

The SOLID question has gotten me thinking. We all know (or should know) what SOLID is all about. That doesn't mean that we agree. When this acronym was starting to become generally used about the same time most of our ".com" companies were collapsing I remember debating its merits with others I worked with. In general I was in agreement with the principles, and certainly with the spirit and goals of the rules.

Single responsibility principle - that a class should have one role or function. Absolutely. This keeps the overall design clean and it is generally easy to understand what stuff does and how they are relating to those they interact with.

Open/Closed principle - that a class is open for extension, but closed for modification. No way. More on this later.

Liskov substitution principle - that objects should be replaceable with their subtypes. On this I generally agree. But this is especially good when you use a factory, and follow the "I" of SOLID and have defined discrete, functional interfaces. In that case especially a consumer of your class doesn't care if it's the original parent instance or some later substituted child.

Interface segregation principle. Just give your customer what they want. If all they need is a small subset of what the class provides let them see your class through the eyes of a simple interface. The less any one part of a software system knows about any other the easier it is when refactoring must be performed. It is also easier to do TDD and write concise unit tests.

Dependency inversion principle. This generally good principle has led, in my opinion, to some of the most unreadable and hardest to debug code. However, when done correctly and cleanly this makes for very "agile" software design.Microsoft recommends using a service locator - and while at design time it's not always obvious which service you'll be using, that's just the point. It doesn't and shouldn't matter. Only that you'll be interacting with some service at run time that will provide, well, the services you'll need. Unit test are greatly simplified using frameworks like Moq. Back in my Java days we used Spring for injecting configuration.

So, what is my issue with "SOLID" and why have I mentioned GRASP in the title? Well there are two reasons. First I do not at all agree with the fundamental principle of the "Open" part. One of the original features of "OO" was that there was an exposed interface that provided a contract to the user. How the implementation did this was of no concern to the outside consumer. This meant that the class could be completely rewritten with no disturbance. That I might need to have the code reviewed again (a silly concept) just because the implementation has changed ignores TDD - if there are a series of complete unit tests that automatically verify that the class is abiding by its contract any and all changes should be permitted.

The interface, and the contract, of a class should never change - it can be extended if need be, though I personally feel that even this isn't a good idea. If a particular class needs to provide more and new functionality this is to me an indication of a need for a new subclass. Yet "SOLID" purists seem to feel that Bob Martin is a deity and these principles sacred. Sacred cows make the best hamburgers!

My second objection is that these principles, while great, only reveal a part of the overall field of good practice.

In my current role as an ASP.Net MVC developer the "Controller" is critical to handling all user interface interaction. Factory patterns have served me well for allowing a system entity to ask for a service or module that will provide the named function and allowing the factory to determine what to create and return.

Services, delegates, and loose coupling - these are standard motifs that we use every day that that provide robust models of software architecture.

It is not so much that I prefer "GRASP" over "SOLID" any more than I prefer Hibernate over Entity Framework. They both have their place and both their advantages. But purists that want to ask interview questions like "do you use SOLID in your current job" need to understand that there is a wider field of study that any one acronym simply does not encapsulate.

Finally, a semantic point: we don't use SOLID in our job - we become it as developers. We don't use the patterns of GRASP - they become a part of us. When they are the fabric from which we are woven we become the types of developers that create well-designed and agile software.

Yup - with that I agree. The interface and behavior is your contract. One of the guys I'm now working with, though, feels that you should not change or improve the code at all because in a large team someone may have already adapted even to your bugs or wrong behavior and if you fix it you'll then break them. While that is a good point I think it's better to figure out what/who is using the method that you're going to fix and let them know about the change and potential problem. And, if you and they both have unit tests you run the suite. If the tests pass you're golden.