Command query separation

From CSSEMediaWiki
Jump to: navigation, search

Command-Query separation (CQS) states that every method should either be a command that performs an action, or a query that returns data to the caller, but not both. Methods that return a value should be referentially transparent, and posses no side effects. wikipedia.org

A trivial example of this is getters and setters: Setters are commands that perform actions (setting a value), and getters are queries that return information about the state of the program. Getters do not change the state, and setters do not return a value.

CQS can be used quite well with a design by contract approach to programming. In design by contract, assertions on the state of the program are considered as annotations, rather than programming logic. This means that querying the state (eg assertions) should not effect the program in anyway. When using CQS, any assertion can call a "Query" method, without having to worry about modifying the system's state.

CQS can simplify the development of a program, as programmers will always know that a method that returns a value has no side effects. Programmers are free to query the state of the system at any point without concern.

CQS suits the object-oriented approach to programming, but is not limited to it; it can be implemented in a procedural language with similar effects.

Contents

Observable vs Unobservable State

An issue when considering CQS is that of what object state is 'observable'. Take for instance a class that caches particular data the first time it is requested:

 class DataSet {
   private Double values[];
   private Double mean;
   ...
 
   public int getMean() {
     if(mean == null) {
       mean = calculateMean();
     }
     return mean;
   }
 }

This class has a getter method getMean, which calculates the mean on the first call. Does this break CQS? The query modifies the objects internal state by setting the field mean, but this field is not visible to other classes, and will not affect further queries to the same object.

Arguably, a Query method should be allowed to alter an object's internal state, so long as it does not affect the state visible to clients of the class (observable state). Actually determining which state is observable could be quite difficult in more complicated examples.

This issue is discussed in detail by Martin Fowler : [1].

Examples

A simple example of a pattern that breaks CQS:

private int x;

public int value() {
  x = x + 1;
  return x;
}

Obeying CQS:

private int x;

public int value() {
  return x;
}

public void increment_x() {
  x = x + 1;
}

CQS broken in Java:

The Java Iterator class breaks CQS. The next() method is both a command and a query. It is a command because it moves the pointer to the next element; it is a query because it returns the element from the collection. The Java developers probably did this because they felt that every time you get an element, you will also want to update the pointer, so they thought it would save one call if next() did both functions. It can be argued that this is going too far in trying to make it simple, and results in an unclean design.

A solution would be to break next() into two calls: current(), that returns the current element, and increment() which moves the pointer to the next element.

Java implementation of Iterator: (breaks CQS)

Iterator1.png


Possible redesign: (obeys CQS)

Iterator2.png

Automatability

This rule can be automated, to an extent. The general idea is to detect query methods, and in each one (and in any methods they call) find any assignments to fields. If there are any, then the query is modifying software state, and we're breaking CQS. However, there are several problems with this approach.

Firstly, how can we detect what is supposed to be a query? We can use a heuristic, such as any method that returns a value, or perhaps any method which has a name starting with 'get' or 'is'. The latter won't return false positives (all getters should be queries), but will return false negatives (for example the top() method of a stack is probably an undetected query).

Secondly, how can we tell what is observable state (see above)? If we walk the method bodies, and find an assignment to a field, it is difficult (impossible?) to tell whether this affects observable state. Even if the field is private, it might be accessible through a getter. Or, perhaps it is passed later to a visitor as a parameter using Double dispatch.

Refactor it

Separate Query from Modifier

See also

Personal tools