Design Pattern Tour: Behavioral Patterns

Iterator

Readings:

Iterator is an approach to provide collections(or containers) a common way to traverse, while hiding the inner structure of the collections. The application of it is obvious: Java and CPP both use this pattern.

For the structure of this pattern, the collection class normally provides an interface of getIterator(), the iterator class, on the other hand, offers interfaces of hasNext() and getNext(). In the concrete iterator class, we usually need to add a variable as a reference to the collection instance.

Concrete iterators can implement different kinds of traversal methods(e.x. DFS, BFS, etc.) easily, which makes it quite convenient to add new collections and iterators, or to replace current collections and iterators with new one in the client code. This design is not only good for extensibility but also Open/Closed Principle.

The separation of collections and iterators decouples the traversal from business logics, embodying Single Responsibility Principle. However, it sometimes might make things more complicated.

Chain of Responsibility(CoR) vs. Decorator

Readings:

The intent of CoR is to deal with requests: A request can be passed through a bunch of bandlers and each of the handlers can decide if it wants to handle the request or just to pass it on to the next handler.

It looks very similar to Decorator, because both of them have something passing through a chain structure. However, Decorator, as a structural pattern, has a fundamental functionality, which anyhow must be executed. And the other decorators are just working to improve this functionality. On the other hand, CoR, as a behavioral pattern, doesn’t have a basic function. All handlers can decide to jump out of the chain without passing the request to the following handlers.

A typical use case is sequential access filter, including login authentication(checking if the username/password is correct, the user has access permission, etc.). Another example is event bubbling(https://www.jianshu.com/p/1f45f5fae39e).

As for advantages, it, first, follows the Single Responsibility Principle, making logic more clear. And it follows the Open/Closed Principle, so not only we or the client can easily add handlers(by extending the handler interface). What’s more, the client can decide which handlers she wants to use and the order of them in the chain(which is similar to Decorator).

The implementation usually requires an abstract class of handler, consisting of a reference to the next handler(nextHandler), a function handleRequest() and a function handle()😦https://www.jianshu.com/p/1f45f5fae39e)

1
2
3
4
5
6
Handler handler1=new Handler1();
Handler handler2=new Handler2();
Handler handler3=new Handler3();
handler1.nextHandler=handler2;
handler2.nextHandler=handler3;
handler1.handleRequest(request);

Command

Readings:

The kernel of Command is to turn the requests(or method call) to stand-alone objects. Some typical applications include delayed execution(e.x. To add requests to a queue because of the long execution time) and undo/redo(e.x. In editor or painting).

A generic application is GUI, where we can add a layer of Command to decouple UI layer and business logic layer. Without the Command layer, the UI layer has to know all the method calls in the business logic layer. However, with the help of the Command layer, the UI layer only needs to parameterize(abstract) the method calls to command objects, so that command objects can interact with business logic instead.

The critical part of the implementation is the function executeCommand(new CutCommand(editor)) and the function command.execute(). (https://refactoring.guru/design-patterns/command/java/example#example-0--editor-Editor-java)

A more generic application is to parameterize method calls and delegate the function calls from the sender to the receiver(unidirectional).

Mediator

Readings:

Mediator is to use an intermediate object to encapsulate interactions between a series of objects(e.x. components). From the communication structure of the system aspect, using Mediator reshapes the structure to a star from a net. In other words, a mediator works as a transportation hub.

Mediator is similar to generic Command. But Mediator allows bidirectional communication between senders and receivers. And compared to Command, Mediator doesn’t stress the conversion from method calls to objects.

From the intent aspect, both Mediator and Facade are trying to simplify APIs(or decouple). But Mediator, from my understanding, is more like a bidirectional Facade. Facade is aiming to simplify function calls from the client to subsystems, which don’t realize the existence of Facade. However, all senders/receivers are at equal positions and they all know Mediator exists.

The advantages of Mediator is to extract the communication(interaction) from the sender/receiver classes (following the Single Responsibility Principle) and manage them in a single class. From the aspect of senders/receivers, the communication interface is simplified(probably to a single function notify()). However, there’s a huge problem: To manage the communication between many objects, Mediator has to reference all the related objects(to delegate functionalities), making Mediator class too bulky and operations much more complicated.

Observer

Readings:

Observer is the subscription(or listener) mechanism: Objects can subscribe/unsubscribe an event(or a kind of events) to a publisher(an object). And the event occurs, the publisher will notify all subscribers, updating the states of the subscribers.

From the aspect of communication structure, Observer and Mediator are the same, which both have a star structure. However, all edges in Mediator all bidirectional, while all edges in Observer are from the center to the outside. So, I think Observer is a specialized Mediator for Sub/Pub scenarios.

There are many use cases, including forums, video websites and reading websites, applying this pattern.

Strategy

Readings:

The intent of Strategy is to move different algorithms of the same functionality(using the same interface) to different classes, making them interchangeable. Normally, it references an instance of the algorithm class as a member variable. (the variable delegates the functionality in the context class)

To implement the same pattern, we can also extend concrete context classes and override the algorithm interface(this is actually Template Method pattern, which I will talk about later). So, applying Strategy is just for Single Responsibility Principle and separate algorithms with the logics in the context, and as well, using composite instead of inheritance.

Of course, if the programming language supports function as arguments, then we can simply pass function names (or using anonymous functions) as arguments, instead of adding complexity.

State

Readings:

State’s intent is to change the member variable(composite variable, which is always an object implementing a specific interface) to perform different functionalities(behaviors).

I think State is a special case of Strategy. Though they both try to change their own behaviors by changing the delegating variable, delegating objects in State are always interdependent, because they need to transfer from one to another within their own logic, like automaton. In Strategy, however, delegating variables are invisible to each other. It will work as long as they implement the same interface.

It can be used when the behavior of a class relies on different states and all the states have transferrable mechanisms among them.

Actually, I have met a similar use case where I was implementing TCP in my Computer Networks course. I was using an enum to represent the state and using switch for different behaviors, which follows neither Single Responsibility Principle nor Open/Closed Principle because I had to change the code snippet of switch when I changed the behavior or added new states.

Template method

Readings:

Template Method is aiming to solve the code reuse problem where two concrete have the same structure, but some of the detailed implementations are different.

Usually, Template Method is inheritance from an abstract class.

I’ve already mentioned it in the section of Strategy. And I think using Template Method or Strategy is determined by whether it follows Single Responsibility Principle.

Visitor

Readings:

When you want to add the same operation to all elements of a complicated object(e.x. Hierarchy and tree) and you want to change as little code as possible, You can apply Visitor pattern with the help of Double Dispatch.

Actually, when I see the intent for the first time, my first idea is to add a new interface and all the classes then can extend this interface, like Composite pattern. However, This violates the Single Responsibility Principle. As for Open/Closed Principle, I think they both follow it(or they neither follow it. It depends.) at the same level because both of them modify the classes(adding new functions).

BUT, if I’ve already added an operation and now I want to add another. Then, using Visitor pattern will be a better choice, because we can reuse accept() without modifying the classes this time. The only thing we need to do will be to implement a new concrete visitor(from Visitor interface) ConcreteVisitor2 and to call object.accept(new ConcreteVisitor2()). This is why all materials on the Internet say one of Visitor’s advantages is following the Open/Closed Principle.

Memento

Readings:

The intent of Memento is to create snapshots of objects and to allow restore from snapshots without knowing the details of the objects(or even cannot directly access the object’s fields/getters/setters).

A typical application is serialization, such as Serializable interface in Java. The client doesn’t have to know what member variables an object has, but she can serialize/deserialize it as long as the class implements the Serializable interface.

Now, I want to mention Visitor. Because if we can directly access the object’s fields/getters/setters, then we can create snapshots and restore from snapshots using Visitor pattern.

Finally, I’d like to tell the difference between Prototype(Creational pattern) and Memento(Behavioral pattern): Prototype is to solve the issue of copying objects, which happens in RAM, while Memento is to persist objects, usually to external devices, such as HD or networks.