The State Design Pattern and its application to the material optimizer


Software Architecture Software Development Design Patterns Behavioral Design Patterns SOLID Principles

This post is about the State design pattern. It is the first of a series of articles that we will publish with the aim of making a practical explanation of some of the most used design patterns currently used in software development.

The State pattern is part of the group of behavioral design patterns. These patterns define a series of rules focused on modeling logic (algorithmic), as well as the assignment of responsibilities between different objects to carry out such functionality.

This pattern allows us to manage in a simple way the different behaviors that an object can have depending on its state. It is really useful for modeling state machines without having to implement complex conditional structures. The correct implementation of the pattern allows us to create a much more maintainable and scalable code.

Taking a real case in which we have had to apply this pattern, is the product Material Optimizer, since some problems that usually appear in this type of software arose. In this particular case, the limitations in extensibility and the need to perform more complex functionalities for critical cases in the analysis of materials required special behaviors based on these states. A code transformation was performed to accommodate this behavior pattern, facilitating the incorporation of different adjustment techniques (costs and distribution) based on material, logistic chain and supply periods (states) constraints.

The reporting (report generation and visualization of orders) and optimizer (options evaluator and optimization calculation) modules and its components had strong dependencies with the state of the order management process, so it was promoted to a structure based on the State pattern.

namespace G4L\MaterialOptimizer\src\Domain\Order;

class MaterialOrder
{
    private $state;
    //...

    public function generateReport() {
        // clause guard: each case with a return
        if(this->state === 'created')
        {
            // report not available
        }
        if($this->state === 'processing')
        {
            // show materials project screen
        }
        if($this->state === 'optimized')
        {
            // generate PDF report for the provider
        }
        //...
        if($this->state === 'closed')
        {
            // generate PDF report with final details
        }
    }
}

Simplification of the ordering of materials (purchase order module) and its connection with the generation of reports to demonstrate the constraints based on the status of the purchase order.

As can be seen in the previous example, adding new states to our orders required increasing the complexity of the existing code by having to modify it and not being able to extend it, in clear violation of the OCP (Open-Close Principle).

The solution we chose for this problem was the application of the State pattern, creating a class for each state of the order and designing a contract that obliged these classes to implement those functionalities that depended on the state itself. We also created an additional part, MaterialOrder, which knows the state of the order and delegates the execution of the functionality to the state components. Furthermore, this object allows the transition from one state to another by substituting one object for another, being possible because all states comply with the contract (interface) mentioned above.

Simplification of the State Design Pattern in MaterialOptimizer

Diagram of the State pattern implementation in the MaterialOptimizer product (simplified case).

This solution allows us to comply with the Single Responsibility Principle (SRP) by allowing us to organize all the logic concerning a state in the class that represents it. In addition, inserting new states means creating new parts that comply with the contract created, favoring the OCP principle.

In short, the application of the State pattern allows us to model state machines without the need to create bulky conditional structures. The contracts of the State pattern establish the functions present in each of the states. Therefore, through the encapsulation of logic and the abstraction obtained by the use of such contracts, we can create highly scalable projects.

For a better understanding of SOLID development principles and their relationship to the software design best practices mentioned in the article, it is recommended to take a look at our specialized SOLID Principles course.

This website uses its own and third party cookies to analyze the traffic and offer you a better experience. By browsing or using our services the user is accepting its use.More information.