Anybody know at least a project with heavy debts, with complex and undocumented business rules, consisting of several large classes or files with thousands of lines. This kind of project where it is only thanks to the witchcraft that it is still standing.
The apparent complexity of this code is induced by multi-role and multi-behavioral components, including also many nested tests, to be executed according to different states of the staged components.
These are not very maintainable productions: to complete or modify its behavior is a sensitive operation. They are difficult to test because of very many cases to test.
There is a design pattern to isolates each behavior in a dedicated section while allowing objects to take a desired behavior, as well as changing them during execution. This is the pattern “State”. [Read the article on Wikipedia]
As example, consider a bilingual person speaking French and English. It must, according to the country where it is located, give in the good selected language its personal records.
Above, in blue the code dedicated to the French behavior, in red to the English behavior. They are distributed in each of the methods of our object, in pink to the behavior selection.
Now apply the pattern “State” to group the French and English outputs in two different states. The hello() and sayBirthday() methods will call these states directly.
Both behaviors have been isolated in two dedicated sections. They are invisible to external objects, which always interact with our object Person. It is itself which, depending on the context, will transfer the call to the active state.
The selection of enabled states is not specific to the class managing the context (the class Person in this example). The states can be also enabled by :
- Via an explicit external call to the object Person.
- At the beginning of a call to a method of the object Person.
- When running a method from one of its states.
- Automatically based on the attribute values of our object Person.
In our example, at each call, the object Person determine the current state based on the first parameter.
On edge effects with this design pattern is that it allows you to create “defective by design” code. We can add a method to the English behavior, to know if the weather is nice.
To call this method in the context France will cause an error, which will interrupt the operation, but which will perform perfectly in the context England. In our example, this is not trivial, but to implement a system of user rights, the approach can be interesting.
In the next article I will introduce the use of the States library to implement this design pattern in PHP.