Wednesday, December 4, 2024
10 Developement Practices Featured Technology      

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“.

Update: This post was picked up by DZone, go vote it up!

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.]

image

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.

image

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:

image

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…

image

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.

image 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.

image 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.

image 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:

image

or like this:

image

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.

Enjoy!

Similar Posts

11 thoughts on “SOLID: Software that works.
  1. Liskov Substitution Principle! I was going through Head Start C# book and had to
    deal with Abstract Classes (that's what the chapter was about lol). I discovered how difficult it is to rely on an abstract class when you have parameters in your constructor. One change to the base class constructor means changes to all sub class constructors (in my case I had around 6-8 sub classes). As a result I posted my question on ASP.NET forum, but you provided the answer I needed here. Thanks for sharing your knowledge Caleb! 🙂

  2. Hey Artin,

    Glad that helped! Yes, base classes are necessary evils. 😉 I like that I have them in my toolbox, but they are never the first hammer that I grab. If you write code that favors composition or inheritance, then base classes just naturally get used less and less…. sounds like something that I'd like to dig in to more in another post. Take care!

  3. Nice article. I love when the how is connected with the why.

    All the explanations I’d ever run into of open/closed always seemed a little pedantic to me. It has always been apparent to me that if a class consumes an interface to do a job, adding another implementation of that interface and feeding it to the consuming class was extending the behavior of the system. It hadn’t been clear to me before that open/closed was talking about extending the system rather than the class itself.

    I may have it wrong, but it seems to me like you are conflating Liskov Substitution with Dependency Inversion. My interpretation of Liskov has always been, “If A begets B and B inherits a behavior from A, then the result of that behavior should be identical between B and A.” Since interfaces don’t have behavior, it doesn’t seem like they would come up in a discussion about Liskov.

    I couldn’t agree more with you on not liking to be tethered to a specific library (i.e. NHibernate). What if I decide that an RDBMS is no longer appropriate for my architecture? What if I also have NHibernate specific attributes sprinkled throughout my domain? Guess I could do a find and replace with the empty string. Keeping your dependencies down is about reducing resistance to change in your code base which is one of the tenets of agile engineering, no?

    Would love to see an example of making “models more mobile.”

    I love how you connected a real-world business scenario—switching service providers—to a programming technique (i.e. giving a shipper the boot).

    Kudos on adding the when to the why + how: “I like constructor injectors for anything that my class requires to operate.”

    I disagree with the statement: “dependency injection is not dependent on any special tooling or frameworks”. DI does not work without a DI container, and there is a learning curve on most of them. I worked in a place where I was one of two people who knew how to configure the ORM, the DI container and the build files. This is one reason I prefer fluent interfaces to config files (especially working NHibernate) that way, if one of your models or services changes, at least there is a chance your compiler will catch it. Guess that is better saved for a conversation about keeping your bus-factor high though. Or perhaps a conversation on meaningful error messages.

    Given the fact that I have been possesed of my high school english teachers, OCD compels me to relate the following (delete if you so choose).

    “Few things have come a long OO history that resonate so well with so many developers than the SOLID principle.”

    Might read better as, “Few things have come from a long list of OO approaches that resonate so well with so many developers than the SOLID principle.”

    “The doesn’t sound like a problem until you start putting parameters in the constructor of the base class.”

    Is probably meant to read, “This doesn’t sound like a problem until you start putting parameters in the constructor of the base class.”

    Typo “classed” should be “classes”: “Now any derived classed have to enforce those same dependencies even if you are creating an entirely different implementation.”

    Not immediately clear that “Use” is intended to be read as a verb and not as an adjective: “I like to think of this principle as ‘Use Interfaces’.”

    “Side Note: Their” should read “Side Note: There” Damn you autocorrect?

    “[T]hat ‘forces*’ me to a specific domain model implementation,” would be more grammatically correct as, “that “forces*” me into a specific domain model implementation.”

    “* – no toolkit should force your architecture or design,” might be better stated as, “* – no toolkit should force your hand when choosing an architecture or design.”

    Typo: ” I don’t have anything again UPS,” should read, ” I don’t have anything against UPS.”

    Typo: “This allows us to define our IShipping service implementation completely independent of the class that’s consuming it,” should read, “This allows us to define our IShipping service implementation completely independently of the class that’s consuming it.”

    Thank you for taking the time to write this.

    1. Robert – thanks for your awesome reply. I must admit, I avoided my English teachers. Although I always right OCD as CDO, since that is in proper alphabetical order. 😉

      I will leave your corrections, because I like them. Thanks.

      On DI framework – that value and benefit of leveraging a DI framework cannot be overstated. For example, with a DI framework you can significantly reduce the over head of working in a DI style of coding and benefit from a single configuration location (I also prefer a fluent interface for configurations).

      That being said, you can take a DI style approach to your classes with out a DI framework – and it will be a PITA every time you need to “new up” a class + plus you won’t get the added benefits of central configuration and lifetime management. So, while you *can* do DI without a DI Framework, I don’t recommend it!

      1. Trying to do DI without an IoC container would be pure madness.

        What I meant to say was that, if we weren’t already doing DI (with a framework as you put it), the learning curve is so steep that I’m not sure we’d have been able to implement it. The theory is simple. It’s the implementation that’s a PITA.

        Configuring containers (or ORMs for that matter), especially in XML is a huge pain in the head. Fluent interfaces will at the very least break sometimes at compile time letting you know that since you’ve updated a class/constructor/property/method definition you also need to update your IoC configuration.

        I haven’t been doing web stuff for a while now, but when I was, it felt like I spent half my life in config files. Yikes! Aren’t the Alt.Net/Castle worshipers supposed to prefer convention over configuration? What happened to that idea?

Comments are closed.