Thursday 3 June 2010

Object-Oriented Design Principles - Part 1

Almost every developer that I know would be able to give a reasonable explanation about inheritance, encapsulation and polymorphism. However, there is much more to Object-Oriented Programming (OOP) than that. In order to come up with a good and clean design, we need to bear in mind some Object-Oriented Design (OOD) principles. Although many of these principles were already published in books and blogs, they are known to just a very small percentage of developers. I don't want to show my age here, but very rarely we find young developers talking about OOD Principles.

There are quite a few OOD principles out there, created/coined by different developers and academics, but I will be listing here just the OOD principles that I consider to be the most important ones.

The following 5 principles together are known by the mnemonic acronym "SOLID". They were first put together by Robert C. Martin (Uncle Bob) in the early 2000s and should be applied at class level. They are:

The Single Responsibility Principle (SRP)

There should never be more than one reason for a class to change. 

The SRP is the simplest OOD Principle and probably one of the hardest to get right. It is also one of the most violated principles.

Let's have a look at the following class:

public class TripItinerary {
    public void addPlace(Place place) { ... }
    public void removePlace(Place place) { ... }
    public List<Place> getPlaces() { ... }
    public void displayOnMap() { ... }
    public Place findPlaceByName(String placeName) { ... }

The class above has 3 different responsibilities, that means, 3 different reasons to change:
  1. Store the places to be visited.
  2. Display the itinerary on a map.
  3. Finds a place by name.

Storing places on the itinerary may have rules like not adding repeated places, keeping the sequence that places will be visited, etc.
Displaying on the map may vary according to the map API being used like Google Map, Bing Map, Yahoo Maps, etc.
Find a place by name may involve calling a web-services to see if the place exists, if there are more than one place with the same name, checking the type of place (city, town, waterfall, monument, castle, etc).

Ideally we would have three different classes to do that, each one with its own responsibility.

public class TripItinerary {
    public void addPlace(Place place) { ... }
    public void removePlace(Place place) { ... }
    public List<Place> getPlaces() { ... }

public class ItineraryMapService {
    public void displayOnMap(TripItinerary tripItinerary) { ... }

public class PlaceService {
    public List<Place> findPlaceByName(String placeName) { ... }

With these approach, we can change the internals of all classes without having the risk of breaking any of the behaviour of the other classes. Without this separation, the design becomes fragile and might break in unexpected ways when changed.

When thinking about a single responsibility, think cohesion at class level. Before creating a class, we need to define what its responsibility should be and the reason for its existence. Before adding any other public method to an existing class, check if this new method relates to the other public methods (the class interface). When creating public methods for a class, have them at least at a communicational cohesion level.

On the next posts, I'll be covering the remaining SOLID OOD principles.

Object-Oriented Design Principles - Part 2



Post a Comment