Welcome in Part 2 in series about GRASP. Today I would like to tell you more about Indirection and Information expert patterns. As a reminder, these patterns allow us to see how to allocate responsibility in our code.
Indirection
Problem: Where to assign a responsibility to avoid direct coupling between two or more things?
For better understanding where we can use this pattern, let's imagine the following situation. We have a system for booking people at an event. The client can choose from many employees due to their specialization and add them to their project. In this case, we need to set each employee as booked for the specified date, we must also inform the project that new people have been added to it, and finally update the fee the client will pay.
As you can see, in this case, the Employee component needs to call and know about the Project component so that it can update the information about booked employees. In addition, there would be another binding between the project and client charges. We want these three components to be independent, so this solution is not good for us.
So what we need? A new component that will be kind of orchestrator for others. It will determine the order of operations. We can use some of these design patterns to introduce it:
- bridge
- facade
- adapter
- mediator
Why? Because we need an object that will be responsible for communication between all modules. This way the components will know nothing about each other. In addition, there will be no problems to find out which component is looking at which one, because none is looking at any.
Here is a result of introducing mediator to our code:
Information expert
Problem: What is a basic principle by which to assign responsibilities to objects?
This pattern tells us that we should add new responsibility to the class that has the information needed to fulfill it. We do it to avoid implicitly connections between class. So this is very important to aggregate similar responsibilities in the same class.
Let take a look at the following example:
class ShoppingCart {
orders: Order[];
addOrder(order: Order) {
this.orders = [order, ...this.orders];
}
getTotal() {
return this.orders.reduce((total, order) => {
return (total += order.cost);
}, 0);
}
}
So if we want to add a new feature, to count the total cost of our shipping card, we should add this function to the ShoppingCart class. Why? Because this class knows everything about orders, so this is the best place for this. Someone may be wondering if such an addition of methods will not make the class grow too much? It's possible. But is it really a problem for us if all functions are linked to the same data? I don't think so.
Conclusion
In the next part, I will talk more about cohesion and coupling. These are very interesting topics, so I will try to tell you a little more about them.
Top comments (0)