GRASP Templates: Information Expert

Hi, Habrovsk. In touch Vladislav Rodin. I am currently teaching courses on software architecture and high load software architecture on the OTUS portal. This time I decided to write a little copyright material in anticipation of the start of a new course “Architecture and Design Patterns” . Enjoy reading.





Introduction


Described in Craig Larman's book Applying UML and patterns, 3rd edition, GRASP patterns are a generalization of GoF patterns, as well as a direct consequence of OOP principles. They complement the missing step in the logical ladder, which allows you to get GoF patterns from the principles of OOP. GRASP templates are more likely not design patterns (like GoF's), but fundamental principles for the distribution of responsibility between classes. Practice shows that they are not very popular, however, analysis of designed classes using the full set of GRASP patterns is a prerequisite for writing good code.

A complete list of GRASP templates consists of 9 elements:

  • Information expert
  • Creator
  • Controller
  • Low coupling
  • High cohesion
  • Polymorphism
  • Pure fabrication
  • Indirection
  • Protected Variations

I suggest considering the most obvious and most important pattern from the list: Information Expert.

Information expert


Wording


Avoiding scientific formulations, the essence of this pattern can be expressed as follows: information should be processed where it is contained.

Violation example


Despite the apparent simplicity and obviousness, I am sure that in the code of any project you can find many violations of this principle.

Consider the simplest class system: Order (order), containing a list of OrderItem'ov (order lines), the elements of which in turn contain Good (product) and its quantity, and the product may contain, for example, price, name, etc.:

@Getter
@AllArgsConstructor
public class Order {
    private List<OrderItem> orderItems;
    private String destinationAddress;
}

@Getter
@AllArgsConstructor
public class OrderItem {
    private Good good;
    private int amount;
}

@Getter
@AllArgsConstructor
public class Good {
    private String name;
    private int price;
}


We have a simple task: to calculate the amount of the order. If you approach the solution to this problem not very thoughtfully, you can immediately write something like this in client code that works with objects of the Order class:

public class Client {
    public void doSmth() {
        
    }
    
    private int getOrderPrice(Order order) {
        List<OrderItem> orderItems = order.getOrderItems();
        
        int result = 0;
        
        for (OrderItem orderItem : orderItems) {
            int amount = orderItem.getAmount();
            
            Good good = orderItem.getGood();
            int price = good.getPrice();
            
            result += price * amount;
        }
        
        return result;
    }
}


Let's analyze this solution.

Firstly, if we start adding business logic related to pricing, the Client :: getOrderPrice method code will not only inevitably grow, but also become surrounded by all kinds of if-s (discount for pensioners, discount on holidays, discount due to wholesale purchases), which in the end will lead to the fact that this code will be impossible to read, much less change.

Secondly, if you build a UML diagram, you can find that there is a dependency of the Client class on as many as 3 classes: Order, OrderItem and Good. It draws all the business logic for working with these classes. This means that if we want to reuse OrderItem or Good separately from Order (for example, to calculate the price of goods left in warehouses), we simply can not do this, because the business logic lies in the client code, which will lead to inevitable duplication of code.

In this example, like almost everywhere where there is a chain of get'ov, the principle of Information Expert is violated, because the client code processes the information and contains its Order.

Application example


Let's try to redistribute responsibilities according to the principle:

@Getter
@AllArgsConstructor
public class Order {
    private List<OrderItem> orderItems;
    private String destinationAddress;
    
    public int getPrice() {
        int result = 0;
        
        for(OrderItem orderItem : orderItems) {
            result += orderItem.getPrice();
        }
        
        return result;
    }
}

@Getter
@AllArgsConstructor
public class OrderItem {
    private Good good;
    private int amount;

    public int getPrice() {
        return amount * good.getPrice();
    }
}

@Getter
@AllArgsConstructor
public class Good {
    private String name;
    private int price;
}

public class Client {
    public void doSmth() {
        Order order = new Order(new ArrayList<>(), "");
        order.getPrice();
    }
}


Now the information is processed in the class containing it, the client code depends only on Order, not suspecting anything about its internal structure, and the classes Order, OrderItem and Good, or OrderItem and Good can be assembled into a separate library that can be used in different parts of the project.

Conclusion


Information Expert, resulting from encapsulation, is one of the most fundamental principles of GRASP responsibility sharing. Its violation can be easily determined and eliminated by increasing the simplicity of perception of the code (the principle of least surprise), adding the possibility of reuse and reducing the number of connections between classes.

We invite you to a free webinar within the framework of which it will be possible to study the features of a monolithic application, multi-level and serverless architectures. Take a closer look at the event-driven system, the service-oriented system, and the microservice architecture.

All Articles