GRASP Templates: Creator

Hi, Habr. My name is Vladislav Rodin. Currently, I am the head of the High Load Architect course at OTUS, and I also teach courses on software architecture.

Specially for the start of a new course enrollment “Architecture and design patterns” I wrote a little material, which I gladly share with you.


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

Last time we discussed the principle of Information Expert. Now I propose to consider a creator similar to it.

Creator

Wording

This pattern solves the same typical problem as its predecessor: class instances must be created by the class that needs them.

Violation example

Consider all the same problem with orders and goods. Assume that the code we wrote complies with Information Expert:

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

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

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

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

Despite the seemingly triviality of the principle we are studying, again, in some client code, you can find this:

public class Client {
    public void doSmth() {
        Good good = new Good("name", 2);
        OrderItem orderItem = new OrderItem(good, amount);
        List orderItems = new ArrayList<>();
        orderItems.add(orderItem);
        Order order = new Order(orderItems, "abc");
        // client code 
    }
}

If you build a UML class diagram, you can find that the Client class now depends on the Order class and all its internals: OrderItem and Good. Thus, we cannot reuse the Client class without the above classes, which the Client does not need. We actually nullified the result of all efforts to comply with the Information Expert, because the Client class created all the objects.

In legacy projects, you can often see how one class creates an object of another and forwards it as a parameter in the method through 5-6 classes, inside which this object is not used. This is nothing more than adding a few dependencies from scratch.

Application example

Let’s adjust the distribution of responsibility between classes so that the distribution satisfies not only Inforamation Expert, but also Creator:

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

    public void addOrderItem(int amount, String name, int price) {
       orderItems.add(new OrderItem(amount, name, price));
   }
}

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

    public OrderItem(int amount, String name, int price) {
        this.amount = amount;
        this.good = new Good(name, price);
    }

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

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

Now the number of dependencies between classes will be minimal. Client code is somewhat simplified and may look like this:

public class Client {
    public void doSmth() {
        Order order = new Order("address");
        order.addOrderItem(amount, name, price);
        // client code 
    }
}

Conclusion

Creator, which developers often forget about, can be considered as a special case of Information Expert, because a constructor call is the same as a method call. Its observance together with Information Expert allows to achieve a minimum number of connections between classes and greater reuse.


Learn more about the course.


Similar Posts

Leave a Reply