Jonathan Boccara's blog

Design Patterns VS Design Principles: Iterator, Mediator and Memento

Published September 12, 2021 - 0 Comments

In our objective to understand how the GoF design patterns relate to the GRASP design principles, we’re going to analyse 3 more Behavioural patterns today: Iterator, Mediator and Memento.

Summary of the previous episodes

As a reminder, the GRASP principles are a set of 9 design principles that help take design decisions about where to assign responsibilities in our code. Here are the 9 GRASP principles (excluding “Managing complexity”, and the hierarchy is mine):

design principles hierarchy

The GRASP principles are explained in Craig Larman’s book Applying UML and Patterns.

On the other hand, the GoF design patterns are the 23 ways of structuring our code exposed in this hugely popular book, that sits on pretty much every programmer’s desk:

design patterns book GoF

The GRASP principles are more at a more generic and fundamental level than the GoF design patterns. Or said differently, the GoF patterns are often implementations of the GRASP principles.

Our goal is to analyse each GoF design pattern and try to map it to a GRASP principle. The value of such an analysis is triple:

  • having a refresher on the GoF design patterns,
  • understanding the essential aspect of each GoF design pattern,
  • better understand the GRASP design principles.

Ultimately, the objective is to take better decisions when designing our code, that will lead us to code that is easier to understand, more expressive, and easier to make evolve.

In the previous post, we’ve seen the first three Behavioural design patterns: Chain of Responsibility, Command and Interpreter.

We now move on to the next three ones: Iterator, Mediator and Memento.

Iterator

Thanks to the STL, we C++ developers are familiar with iterators. Is the GoF design pattern Iterator similar to STL iterators?

Iterator and the STL iterators

The GoF describes iterators as classes that encapsulate the responsibility of the traversal of a collection. So far, that sounds similar to STL iterators.

In the GoF book, the iterator class is instantiated by the collection class, and has the following methods:

class Iterator
{
    First()
    Next()
    IsDone()
    CurrentItem()
};

This Iterator has almost the same responsibilities as STL iterators:

  • Next() corresponds to operator++.
  • IsDone() corresponds to a comparison with an end iterator by using operator!=.
  • CurrentItem() corresponds to operator*.

Only First() is not covered by the STL iterators: STL iterators don’t allow going back to the beginning of a collection.

In the design pattern Iterator, the collection is in charge of producing iterators that allow to traverse it. This is also the case in the STL, with the typical begin() and end() member functions being part of the conventions of the STL.

Iterator and design principles

The alternative to iterators would be that the collection itself handles its traversal, and includes that feature in its interface. The design pattern Iterator is an extraction of the responsibility of traversing the collection, into a dedicated class.

Seen this way, Iterator is a way to achieve High Cohesion. It is a Pure Fabrication as, even though collections can map to domain objects, iterators usually don’t.

Polymorphic iterators

The GoF book goes on and shows how to achieve polymorphism with iterators. Even though this is very powerful, in my opinion this is only an extension of the concept of iterator, and not its essence. Indeed, we could have iterators that are not generic nor polymorphic, and that still follow the design pattern Iterator.

For this reason, I think Iterator is more closely related to Pure Fabrication than to Polymorphism.

But for the sake of exploring design patterns, let’s review the two sorts of polymorphisms that we can achieve with the design pattern Iterator.

The first type of polymorphism is in the behaviour of the iterator itself. For example, we could imagine an iterator that skips some elements that don’t satisfy a predicate. In fact this type of polymorphism is exactly the idea behind range adaptors, in ranges libraries.

The second type of polymorphism is related to the collection. It would be nice to have a unified interface that could iterate on various data structures. For example, we would like to use the same code to iterate on contiguous sequences as well as on trees.

The GoF book suggests to have two hierarchies using inheritance in order to achieve this:

Iterator design pattern

C++ standard containers such as std::vector and std::map, which are implemented as a contiguous sequence and a tree, respectively, don’t operate this way.

They do offer polymorphism, as we can write the same code to traverse a std::vector or a std::map, but they don’t rely on inheritance. They rely on generic code, since those containers provide an interface with the same member function names (begin() and end()). This allows to write such polymorphic code:

for (auto const& element : myCollection)
{
    // do something with element 
}

It is polymorphic because it works whether myCollection is a std::vector or a std::map.

In summary, Iterator is a Pure Fabrication, that Polymorphism can make more powerful.

Mediator

The design pattern Mediator consists in introducing an object that centralises the logic of interaction between a set of other objects.

The GoF uses the example of a GUI that has many elements that interact with each other. One way to implement this would be to have each class communicate with the others when they need to trigger the appropriate reactions.

But doing this introduces intense coupling, as many objects come to interact with each other.

Mediator mitigates this problem by having all objects notify only one object, the Mediator object, whenever they need to trigger a reaction. The Mediator packages all the logic, and forwards the incoming notifications to the appropriate objects.

As a result, all the objects are connected with the Mediator, instead of all objects being connected with each other:

design pattern mediator

Seen this way, Mediator is a way to achieve the design principle of Indirection.

Memento

The design pattern Memento helps restoring an object to a previous state.

It works the following way: object A creates a Memento object, and sets in it some information about its current state. The Memento objects is stored somewhere, for example in another object B.

Then A lives on its life, and its state changes.

Later, we give back the Memento object to A, and A retrieves the information that it had put into it. After doing this, A is back to the state it was when it created the Memento object.

B is not concerned with what information is in the Memento object, nor even what kind of information it contains. It just holds it in order for it to be available to A at a later point.

To which design principle does Memento correspond?

To be honest, I’m not sure Memento relates well to one of the GRASP design principles. What do you think?

We could argue that Memento is about hiding the contents of the state of A. So if we come to modify the program and change the type of state that A needs, B won’t be affected. This seems to relate to Low Coupling.

But should we consider that it’s part of Protected Variations, in the sense that the Memento object hides the state of A? Or is it another type of Low Coupling?

Leave a comment below to express your opinion.

Three new patterns

After performing the above analyses, we understand better the Iterator, Mediator and Memento design patterns.

We classified them this way:

  • Iterator is a Pure Fabrication
  • Mediator is an Indirection
  • Memento is a Low Coupling (or perhaps Protected Variation)

This is not a definitive classification. Its purpose is also to start a discussion!

Do you agree with those three associations? What is your view on those design patterns? Do you think they implement other design principles?

You will also like

Don't want to miss out ? Follow:   twitterlinkedinrss
Share this post!Facebooktwitterlinkedin