Software Development

SOLID design principles improve object-oriented development

Justin James highlights how five principles -- single responsibility, open/closed, Liskov substitution, interface segregation, dependency inversion -- help guide object-oriented developers.

People who are involved in the more rigorous, formal side of object-oriented (OO) development have a large number of various principles, or lists of principles to guide their development work. Recently, a reader asked me if I could write an article about the SOLID principles, and it seemed like a good idea.

SOLID is a set of five guiding principles that help developers design objects that are easy to maintain and use. Like many similar sets of principles in OO, SOLID is a guide not the goal. Let's take a look at each of the five principles.

S - Single responsibility principle (SRP)

SRP is the idea that objects should do one thing and one thing only. It is tempting to think this means that a class should represent an entire piece of business logic or data, but that is not quite correct. The idea is not to group all of the functionality that is related together, but all of the functionality that achieves the same goal together.

For example, if you have a class representing printer, putting all of the printer operations (getting toner levels, printing a page, getting an error message) in the printer class may make sense, but it is not really the right approach. Instead, you would put all of the functions related to printing in the same class, and all of the functions for reporting status in a class, and so on. You break the work down into small, digestible chunks. The payoff here is that you can make changes to a select piece of functionality without possibly affecting a ton of other items.

O - Open/closed principle (OCP)

This very simple idea is that classes can allow themselves to be extended but not modified. The source code for the original class should only be modified if a bug is found. The reason behind this is that changing the code will mean that everything depending upon it will need to be retested as well.

L - Liskov substitution principle (LSP)

LSP states that the subclass of an object can be used in place of the superclass without changing things like what the program does, whether it compiles, etc. For example, if class "elephant" is a subtype of class "animal," then you should be able to use "elephant" where you use "animal," and the "walk" method will still "walk" (even though the implementation may be different), the "numberOfLegs" property will still return the number of legs (though again, it may be a different number from the base class), the "eat" method will not throw any exceptions that it would not throw in the "animal" class, and so on. It is much easier to make changes and test when you follow LSP.

I - Interface segregation principle (ISP)

ISP dictates that interfaces need to have as little functionality in them as possible. This allows the consumer to only deal with the functionality they are concerned with, which reduces testing needs and the impact of changes to the system.

D - Dependency inversion principle (DIP)

With DIP, high-level objects are abstracted away from low-level objects. It is normal to think of the low-level objects as just being smaller pieces of a bigger one. For example, let's say that you want to query a database and fill a data set. The temptation is to pass in the database connection information to the data set and let it make the connection. Instead, you pass in a connection that is already configured, and in this way, the database can change without needing to retest the entire data set object.

J.Ja

Related TechRepublic post: Poll: Is object-oriented programming too complicated?

Keep your engineering skills up to date by signing up for TechRepublic's free Software Engineer newsletter, delivered each Tuesday.

About

Justin James is the Lead Architect for Conigent.

3 comments
lupino.eu
lupino.eu

a very unprofessional article for Techrepublic.

wjames1
wjames1

In this paragraph I am somewhat confused about "Instead, you pass in a connection that is already configured, and in this way, the database can change without needing to retest the entire data set object." Should I be doing the latter as opposed to the previous? "The temptation is to pass in the database connection information to the data set and let it make the connection."