SOLID: Software That Works
This post is a part of a series that I started with “10 Practices that Every Developer Needs to Start Right Now“.
Ok, before you dig in to the post, let’s get two things out of the way first. 1.Go read the authority on SOLID principles from the man himself, Uncle Bob Martin. 2nd.Go get the very cool Inspirational SOLID images from the guys over at Los Techies. They released them under a Creative Commons License which I think is pretty cool! Alright, got that out of the way? Good. Let’s get started.
Few things have come a long OO history that resonate so well with so many developers than the SOLID principle. One of the reasons they resonate with so many developers is because they communicate several practices that many developers have been doing all along. The beauty and power of the SOLID principals in in there ability to communicate, what I call code architecture, in such a memorable and practical way.
Like any good thing, however, taken to an extreme can become a hindrance on any project. So, I’m going to tackle these principals like I tackle everything in this series… give you my take on it. So here you go: SOLID according to Caleb.
[SOLID Motivational Posters, by Derick Bailey, is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License. Get them here.]
S – Single Responsibility Principal
“There should never be more than one reason for a class to change.” — Robert Martin, SRP paper linked from The Principles of OOD
Abuse: I’ve seen this taken to an extreme. I’ve seen good clean readable code turned in to multiple classes (even multiple projects) to break up “responsibility”. The end result was much harder to maintain and even harder to read.
Applied: “One reason to change” does not mean that every class has one and only one thing (that would be called a method), it does mean that you should focus on the area (or areas) of responsibility that a class should have and then stick with those boundaries. Code bloat (overly large classes with overly large methods) is a real code smell that you need to watch out for. The more things that a class is responsible for, the more likely you’ll have to change it and the harder it will be to test.
Your code should be broken in to manageable pieces, reduce any unnecessary couplings… Practice writing Libraries not Frameworks.
O – Open Closed Principle
“Software entities should be open for extension, but closed for modification.” — Robert Martin paraphrasing Bertrand Meyer, OCP paper linked from The Principles of OOD
Abuses – I’ve worked on code bases that were so extensible, so configurable, so full of AOP indirection and configuration that following the flow of what they were actually doing was almost impossible.
Applied – Code is going to change, that’s a part of life. The Open Closed Principal is more about reducing how often you have to change your code and in how many places. In other words: Code to Interfaces and maintain your abstraction boundaries.
I recently worked with a Linq to SQL project where the Data Context object was being passed around through out all of the layers in the application. That meant that most of the application was impossible to unit test and if I were to change a column or table in the database I would have to go through the entire code base and find all of the places that broke. We fixed that by creating a specific data interface that all interactions had to go through, only passing domain objects (DTO Models). We kept the DB Context in the Data Layer implementation where it was super useful, but no longer forced us to recompile the entire source for simple data changes. I like how approach that Jeffery Palermo described and an Onion Architecture.
I also worked on another project where 8 layers of abstract classes were used to distinguish between three different types of physical devices… any change in the application behavior had to be propagated across all of the implementations. We fixed that by concealing the device differences behind a single command interface that was then injected in to the application “behaviors” via an abstract factory.What did you just say?
So in other words… imagine having three different devices (blue, red, green) that all needed to be turned on (behavior), but the command to turn on each was different and defined by the manufacture… the code *might* look like this:
Now imagine that there were multiple points within your application where you were working with the devices… now, every time you need to support a new device you end up with this if/else statement being redone just about everywhere…
By externalizing the device differences behind a factory and encapsulating them in an Interface you now only have one place to change to add a new device. You could reduce that further using an extension manager like Microsoft MEF, but we won’t go in to that right now.
L – Liskov Substitution Principle
“Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.” — Robert Martin, LSP paper linked from The Principles of OOD
Abuses: I like to think of this principle as “Use Interfaces”. I haven’t really seen abuses of this practice, but I have seen some bad implementations. The rough ones, are where developers rely on a base abstract class instead of an Interface also. The doesn’t sound like a problem until you start putting parameters in the constructor of the base class. Now any derived classed have to enforce those same dependencies even if you are creating an entirely different implementation.
Applied: Use Interfaces. If you find that a base class would meet some of your needs more closely, that’s fine, just make sure that you back that base class up with an Interface, and then code to the Interface.. you’ll thank me later.
Side Note: Their are different schools of thought around backing domain models with Interfaces. I do, the main reason is that even if I end up using an ORM (like Entity Framework, or LinqToSQL) that “forces*” me to a specific domain model implementation, I can save myself a lot of headache later and make my models more mobile if I connect those domain models to an Interface.
* – no toolkit should force your architecture or design, any implementation can be abstracted around, Domain Model Interfaces help move your DTO’s through layers of your application without carrying heavy dependencies with you.
I – Interface Segregation Principle
“Clients should not be forced to depend upon interfaces that they do not use.” — Robert Martin, ISP paper linked from The Principles of OOD
Let’s say you have a service class for working with Invoices, your IManageInvoices interface exposes three methods, Add(IInvoice), Delete(IInvoice) and Update(IInvoice). Because of deployment and security concerns you are going to create two different classes to implement this.
One, InvoiceCreator will implement the Add method and run in untrusted environments. The other class, InvoiceUpdater, will implement the other two methods and will only run in secure, verified and authenticated context. So what should each class do with the other methods?
Violated: One “option” would be to implement them, but then throw a “Not-Implemented Exception” or set up Void methods that don’t actually do anything, both of those options are ugly and bad choices.
Applied: The better option is to split your Interface, create a ICreateInvoices Interface with the Add method, and a IUpdateInvoices interface with the other methods. That way, you are actually implementing the methods of your interface, and are not hiding are making implementation decisions that break your abstraction and require special knowledge of the class.
D – Dependency Inversion Principle
“(A) High level modules should not depend upon low level modules. Both should depend upon abstractions. (B) Abstractions should not depend upon details. Details should depend upon abstractions.” — Robert Martin, DIP paper linked from The Principles of OOD
All code has dependencies, the question is how to you resolve those dependencies.
Example: my class will access a service, I could write it like this:
or like this:
Can you spot the difference? It’s subtle, yet very very powerful. In the first instance, you are using an Interface to define your Shipping Service (and that’s a good thing!), but then you are forcing your class to be dependent on the UPS shipping service… I don’t have anything again UPS, but I do know that company contracts are constantly changing, and just because we were using UPS when we designed and had the customer (business owner) sign off on the application, doesn’t mean that that’s who we are going to use when we go to production!
You might be tempted just to replace the “new UPS” instantiation with an Abstract Shipping Factory ( shipService = factory.getShippingService() )… that wouldn’t necessarily be a bad idea, except now you’ve shifted your code from a UPS dependency to a factory dependency.
Notice in the second option, we hand our class the implementation that we want to use through the constructor. That’s called constructor injection, we could have also used a property or method to set the shipping service. I like constructor injectors for anything that my class requires to operate. This allows us to define our IShipping service implementation completely independent of the class that’s consuming it.
This also makes are code much easier to test by allowing us to creating a mock (fake) version of our IShipping service for testing the main class. We might even use something like RhinoMocks to help our automated unit tests even more, but we’ll save that discussion for another time.No Framework Required
You may have noticed that this dependency injection is not dependent on any special tooling or frameworks (so we’re not introducing new dependencies just to get rid of another!)
Dependency Injection or DI, is really a style of coding that makes your code more composable, testable and maintainable. DI Frameworks (or Containers) are specifically designed to be used in two stages.Register, then Resolve
First, you register your Interface to Class mappings, then you can reference the container anytime and resolve an Interface to a concrete class. Containers can also provide other nice benefits like controlling the life cycle of an object (singelton, vs per thread, vs per request for example). Some DI frameworks also provide the ability register special handlers (or Interceptors) that get invoked whenever a method or a property is called. This in a concept known as AOP or Aspect Oriented Programming that is useful for cross-cutting concerns like automatic logging and security checks.
For more information on Dependency Injection and Inversion of control I suggest checking out my DI in Silverlight slide deck, as well as the Ninject, Castle, Microsoft Unity and Structure Map projects.
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)