Thursday, April 1, 2010

SOLID Principles

As we know one of the cores of software maintenance is "Refactoring". It is about restructuring code without changing its behavior. In this article, we will be discussing some basic guidelines for code refactoring.

As Stephen Covey writes that before doing anything we should find the basic principle about that. This would yield the best possible result. It is good to know that, in addition to refactoring patterns, software industry has also come up with some principles for code refactoring. One of them are S.O.L.I.D principles. It would be injustice to just categorize them as Refactoring principles but they can also be specified as general Object Oriented Design principles.

As you might have noticed, I have been using SOLID as a plural. It is because of the reason that it is not a single principle but it is a list of principles. S.O.L.I.D is an acronym. Let us see what it is composed of:

  1. S: Single Responsibility Principle
  2. O: Open Closed Principle
  3. L: Liskov Substitution Principle
  4. I: Interface Segregation Principle
  5. D: Dependency Inversion Principle

Let us discuss about each of them separately.

Single Responsibility Principle (S):
"There should never be more than one reason for a class to change" (Robert Martin). As we say in the object oriented world, a class should be highly cohesive, which is basically High Cohesion GRASP (General Responsibility Assignment Software Pattern) pattern.

If you could find more than one reason to change a class, it might not have been designed using High Cohesion GRASP principle i.e. the class would have more than one responsibility that is why you might need to change it for more reasons.

Open Closed Principle (O):
"Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification." — Robert Martin

This is basically based on Strategy pattern. As you might already know that strategy pattern is implemented using inheritence and polymorphism. So you would be needing them to implement Open Closed principle too.

Let's say we have a new requirement and we decide about updating the definition of one of the classes. Open Closed Principle suggests that we should not just pick up our tools and start hammering the main class. We should rather extend from this class and override the behaviors that we want to upgrade. Now instead of using object of main class, we can use the object of child class. We can still use base class reference because of the support of polymorphism in the object oriented ted languages.

But I do believe that there are certain instances in which a class implementation must be changed. The greatest example is code fix. There might be certain issues with the current implementation and we want to fix the code. For all such instances, a software entity (class) should still be open for modification.

If you would search for a text for open / closed principle on the internet, you could find two types of implementation. One implementation uses abstract base classes in accordance with the original proposal by Robert Mayer in late 80s. The other implementation is based on abstract interfaces. I would recommend going with interface based approach because it is the contract which is not changing so contract should be closed but new implementation should be defined if needed. But there are instances in which contract should also be changed. In that case interface (contract) can also be extended. But that would make the discussion a little more complex.

It must be noted that both of these options are making the original software entity (base class or interface) closed for modification. They are rather about defining new software entities which would use the original entity and build it's own definition on top of it.

Liskov Substitution Principle (L):
This principle is about semantic relationship when subtyping a software entity (e.g. class). Since this was introduced by Barbara Liskov (late 80s), it is named as "Liskov Substitution Principle". Liskov defined this principle as follows:

"Let q(x) be a property provable about objects x of type T. Then q(y) should be true for objects y of type S where S is a subtype of T".

In accordance with the above definition, if a class is inheriting a class or implementing an interface, we should be able to use this class in all of the cases in which we could use the base class or interface. There might not be any scenario which stops us from using the child class when we can use the base class or any other interface.

Interface Segregation Principle (I):
First let us see how Robert Martin puts this:
"Clients should not be forced to depend upon interfaces that they do not use." This principle is against interface pollution or having fat interfaces in your design.

If we require implementing a piece of code that involves implementing some fat interface and we think that the only partial implementation of the interface would do the work. In this case we should be segregating the interface creating new interfaces with the partial contracts and should be implementing new interfaces for new requirements.

Dependency Inversion Principle (D):
"Abstractions should not depend upon details. Details should depend upon abstractions." — Robert Martin

This abstraction includes inheritence of classes and implementing interfaces. This also includes the abstraction in the design by introduction of several design layers. It implies that less abstract layers should depend on more abstract ones. i.e. User Interface layer should depend upon the Business layer but not the other way around.

We should not be confusing dependency inversion with another great principle, Dependency Injection. Dependency Inversion is about using interface references i.e. using IEngine interface reference instead of directly using concrete Engine class reference. So there is no direct coupling between classes. All classes depend upon, are some contracts. They don't care about whatever way the concrete objects implement those interfaces. Dependency Injection is a container based approach in which container takes responsibility for providing the concrete object implementation to the dependent objects.

Note:
As you know a picture is worth a thousand words. Lostechies has great pictorial representation of these principles. You can find them here:

http://www.lostechies.com/blogs/derickbailey/archive/2009/02/11/solid-development-principles-in-motivational-pictures.aspx

References:
http://www.davesquared.net/2009/01/introduction-to-solid-principles-of-oo.html
http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod

No comments: