Pages

Sunday, August 25, 2013

Command Query Separation

'Command Query Separation' (CQS) is a design principle by which the methods of an object are categorised separately:

Commands: Changes the state of the system and does not return value.

Public Course Courses.Find(courseName)
{
//implementation
};

Query: Returns a result without having any side effects on the state (i.e. does not alter the observable state of the system).

public void Course.AllocateFaculty(faculty)
{
//implementation
};

Whilst CQS is a sound principle, but there are exceptions in many cases. Some these violations may be acceptable but other could be potentially dangerous.

Lets consider an example where this violation maybe acceptable. The classic violation is Stack.Pop() which is a command and a query. It removes and returns the object at the top of the stack. Another simple example which breaks the pattern but may be useful in multi-threaded programs:

public int IncrementCount()
{
  int copyOfCount;
  lock(_count)
  {
    _count++;
    copyOfCount = _count;
  }
  return copyOfCount;
}
You can manage this better making sure that if a command returns a value, it will go through an out parameter and the command will have a meaningful name IncrementAndReturnCount.
public void IncrementAndReturnCount(out int latestCount)
{
  lock(_count)
  {
    _count++;
    latestCount = _count;
  }
}
There are however instances where CQS violations cause dangerous side effects that can be difficult to debug. Consider the following method in the Course class:
public void GetSchedule()
{
  if(this.Schedule==null)
    this.Schedule = new Schedule();
  return this.Schedule;
}
It may seem very harmless that the method instantiates the schedule if it is null. But imagine someone added a new operation to send an alert all the allocated faculties of courses which do not have a schedule set.
foreach(var course in Courses)
{
  if(course.GetSchedule() == null)
    SendAlertToFaculties(course.AllocatedFaculties);
}
By adding command behavior to GetSchedule(), we have accidentally broken the code. Now we must spend time to re-factor the original code.

We can say CQS is clearly intended as a programming guideline rather than a rule for good coding.

No comments:

Post a Comment