Behavioral Patterns
Behavioral Patterns - Chain of Responsibility Pattern
The chain of responsibility pattern is based on the same principle as written above. It decouples the sender of the request to the receiver. The only link between sender and the receiver is the request which is sent. Based on the request data sent, the receiver is picked. This is called “data-driven”. In most of the behavioral patterns, the data-driven concepts are used to have a loose coupling.
The responsibility of handling the request data is given to any of the members of the “chain”. If the first link of the chain cannot handle the responsibility, it passes the request data to the next level in the chain, i.e. to the next link. For readers, familiar with concepts of Java, this is similar to what happens in an Exception Hierarchy. Suppose the code written throws an ArrayIndexOutOfBoundsException. Now, this exception is because of some bug in coding and so, it gets caught at the correct level. Suppose, we have an application specific exception in the catch block. This will not be caught by that. It will find for an Exception class and will be caught by that as both the application specific exceptions and the ArrayIndexOutOfBoundsException are sub-classes of the class Exception.
Once get caught by the exception, which is the base class, it will then not look for any other exception. This is precisely the reason why, we get an “Exception is unreachable” message when we try to add a catch block with the exception below the parent exception catch block.
So, in short, the request rises in hierarchy till some object takes responsibility to handle this request.
It’s the same mechanism used for multi-level filtration. Suppose, we have a multi level filter and gravel of different sizes and shapes. We need to filter this gravel of different sizes to approx size categories. We will put the gravel on the multi-level filtration unit, with the filter of maximum size at the top and then the sizes descending. The gravel with the maximum sizes will stay on the first one and rest will pass, again this cycle will repeat until, the finest of the gravel is filtered and is collected in the sill below the filters. Each of the filters will have the sizes of gravel which cannot pass through it. And hence, we will have approx similar sizes of gravels grouped.
Let’s apply the same example in the form of code.
First, let’s talk about the request data. In this case, it is the gravel. We call it Matter. It has size and quantity. Now, the size determines whether it matches the size of filter or not and the quantity tells how much matter is below the size.
Matter.java
|
package bahavioral.chainofresponsibility; public class Matter { |
||
|
// size of matter private int size; // quantity private int quantity; /** * returns the size */ public int getSize() { return size; }
/**
/**
/** |
||
| }// End of class | ||
The next thing is now the base class. This base class in our case is Sill. Nothing escapes the Sill. All the matter is collected in the sill. Everything which cannot be filtered gets collected in the Sill. Like all the requests which cannot be handled at a lower level rise to higher level and are handled at the highest level.
Sill.java
|
package bahavioral.chainofresponsibility;
/** |
||
|
/** * Method collect. * Everything is collected here. */ public void collect(Matter gravel) { } |
||
| }// End of class | ||
And of course, the next class will be the chain. In the example, I have just created one single class called Filter1. This class extends from the Sill. And the chain grows on. Every class like Filter2, Filter3 etc extends from Filter1, Filter2 and so on.
Filter1.java
|
package bahavioral.chainofresponsibility;
/** |
||||
|
private int size; public Filter1(int size) { this.size = size; } /** * over-ridden method from base class */ public void collect(Matter gravel) { |
||||
|
// for the entire quantity of matter for(int i = 0; i < gravel.getQuantity(); i++) { |
||||
|
// if gravel size is less than size of filter, // the gravel will pass to the next level. |
||||
|
if(gravel.getSize() < size) { super.collect(gravel); } else { //collect here. that means, only matter with less // size will pass... } |
||||
| } | ||||
| } | ||||
| }// End of class | ||||
This is how, this pattern works. Based on principles of decoupling, the pattern is totally data-driven. The famous example is the Exception hierarchy.
The other advantage is distribution of responsibilities. There can be such a scenario when none of the objects in the chain can handle the request. In this case, the chain will discard the request. The basic object can also be an interface depending on needs.
Behavioral Patterns - Command Pattern
This is another of the data-driven pattern. The client invokes a particular module using a command. The client passes a request, this request gets propagated as a command. The command request maps to particular modules. According to the command, a module is invoked.
This pattern is different from the Chain of Responsibility in a way that, in the earlier one, the request passes through each of the classes before finding an object that can take the responsibility. The command pattern however finds the particular object according to the command and invokes only that one.
It’s like there is a server having a lot of services to be given, and on Demand (or on command), it caters to that service for that particular client.
A classic example of this is a restaurant. A customer goes to restaurant and orders the food according to his/her choice. The waiter/ waitress takes the order (command, in this case) and hands it to the cook in the kitchen. The cook can make several types of food and so, he/she prepares the ordered item and hands it over to the waiter/waitress who in turn serves to the customer.
Let’s have a look at this example with Java code.
First thing is the Order. The order is made of command which the customer gives the waiter.
Order.java
|
package bahavioral.command;
/** |
||
|
private String command; public Order(String command) { this.command = command; } |
||
| }// End of class | ||
The other thing is the waiter who takes the order and forwards it to the cook.
Waiter.java
|
package bahavioral.command;
/** |
||
|
public Food takeOrder(Customer cust, Order order) { Cook cook = new Cook(); Food food = cook.prepareOrder(order, this); return food; } |
||
| }// End of class | ||
The waiter calls the prepareFood method of the cook who in turn cooks.
Cook.java
|
package bahavioral.command;
public class Cook { |
||
|
public Food prepareOrder(Order order, Waiter waiter) { Food food = getCookedFood(order); return food; }
public Food getCookedFood(Order order) { |
||
| }// End of class | ||
Now, here, the waiter takes command and wraps it in an order, the order is associated to a particular customer. For, the cook, the order is associated to a cook and also Food is associated to the Order.
The order is an object which depends on the command. The food item will change as soon as the command changes. This is loose-coupling between the client and the implementation.
Behavioral Patterns - Interpreter Pattern
The Interpreter Pattern defines a grammatical representation for a language and an interpreter to interpret the grammar. The best example you can get for this is Java itself which is an interpreted language. It converts the code written in English to a byte code format so as to make possible for all the operating systems to understand it. This quality of it makes it platform independent.
The development of languages can be done when you find different cases but, somewhat similar, it is advantageous to use a simple language which can be interpreted by the system and can deal with all these cases at the same time.
To make this interpreter clearer, let’s take an example. The “musical notes” is an “Interpreted Language”. The musicians read the notes, interpret them according to “Sa, Re, Ga, Ma…” or “Do, Re, Me… ” etc and play the instruments, what we get in output is musical sound waves. Think of a program which can take the Sa, Re, Ga, Ma etc and produce the sounds for the frequencies.
For Sa, the frequency is 256 Hz, similarly, for Re, it is 288Hz and for Ga, it is 320 Hz etc etc…
In this, case, we need these values set somewhere so, that when the system encounters any one of these messages, we can just send the related frequency to the instrument playing the frequency.
We can have it at one of the two places, one is a constants file, “token=value” and the other one being in a properties file. The properties file can give us more flexibility to change it later if required.
This is how a properties file will look like:
MusicalNotes.properties
# Musical Notes Properties file
# This denotes the frequencies of musical notes in Hz
Sa=256
Re=288
Ga=320
Here are the other classes used for this system:
NotesInterpreter.java
|
package bahavioral.interpreter;
public class NotesInterpreter { |
||
|
private Note note; /** * This method gets the note from the keys pressed. * Them, this sets it at a global level. */ public void getNoteFromKeys(Note note) { Frequency freq = getFrequency(note); sendNote(freq); } /** * This method gets the frequency for the note. * Say, if the note is “Sa”, it will return 256. */ private Frequency getFrequency(Note note) { // Get the frequency from properties // file using ResourceBundle // and return it. return freq; } /** * This method forwards the frequency to the * sound producer which is some electronic instrument which * plays the sound. */ private void sendNote(Frequency freq) { NotesProducer producer = new NotesProducer(); producer.playSound(freq); } |
||
| }// End of class | ||
NotesProducer.java
|
package bahavioral.interpreter;
public class NotesProducer { |
||
|
private Frequency freq; public NotesProducer() { this.freq = freq; } /** * This method produces the sound wave of the * frequency it gets. */ public void playSound(Frequency freq) { } |
||
| }// End of class | ||
This is how an interpreter pattern works in its most simple implementation. If you are using interpreter pattern, you need checks for grammatical mistakes etc. This can make it very complex. Also, care should be taken to make the interpreter as flexible as possible, so that the implementation can be changed at later stages without having tight coupling.
Other advantage of Interpreter is that you can have more than one interpreter for the same output and create the object of interpreter based on the input. E.g. “Sa” or “Do” can also be implemented as “Download” activity in some other language. In this case, you can use same input and different outputs by getting the proper interpreter from the InterpreterFactory.
This is not a very common pattern.
Behavioral Patterns - Iterator Pattern
The Iterator pattern is one, which allows you to navigate through a collection of data using a common interface without knowing about the underlying implementation.
Iterator should be implemented as an interface. This allows the user to implement it anyway its easier for him/her to return data.
We use iterators quite frequently in everyday life. For example, remote control of TV. Any remote control we use, either at home/hotel or at a friend’s place, we just pick up the TV remote control and start pressing Up and Down or Forward and Back keys to iterate through the channels.
What sort of interface can Iterator be in case of Remote Controls?
|
/** * Iterator interface has method declarations for iterating through * TV channels. All remote controls implement Iterator. */ public interface Iterator { |
||
|
public Channel nextChannel(int currentChannel); public Channel prevChannel(int currentChannel); |
||
| }// End of interface | ||
The channel iterator is common for all the remote controls. It’s like a specification implemented by all the remote control manufacturing companies.
|
/** * ChannelSurfer is a part of remote control which implements the Iterator * interface. This class overrides the nextChannel and prevChannel methods. */ public ChannelSurfer implements Iterator { |
|||
|
/** * nextChannel – method which takes the current channel number * and returns the next channel. */ public Channel nextChannel (int currentChannel) { |
|||
|
Channel channel = new Channel(currentChannel+1); return channel; |
|||
| } | |||
|
/** * prevChannel – method which takes the current channel number * and returns the previous channel. */ public Channel prevChannel (int currentChannel) { |
|||
|
Channel channel = new Channel(currentChannel-1); return channel; |
|||
| } | |||
| }// End of class | |||
|
/** * RemoteControl class is the actual remote control and it behaves and makes * use of ChannelSurfer. */ public class RemoteControl { |
|||
|
private ChannelSurfer surfer; private Settings settings; public RemoteControl() { |
|||
|
surfer = new ChannelSurfer(); settings = new Settings(); |
|||
| } | |||
|
/** * getProgram returns the program for that channel. * */ public getProgram(ChannelSurfer surfer) { |
|||
| return new Program(surfer.nextChannel()); | |||
| } | |||
| }// End of class | |||
We all know that every channel is associated to a program and it’s basically the program and not the channel number which a user wants to see. And so, the implementation which returns a program for channels surfed.
This tells us that we can apply some logic before returning the elements through iterator. We can set rules. The Iterator here, can also be programmed to return the ‘programs’ straight away rather than returning the channels.
The common Java iterator is Enumeration which has implicit
hasMoreElements()
and nextElement() methods.
The benefits of Iterator are about their strength to provide a common interface for iterating through collections without bothering about underlying implementation.
Behavioral Patterns - Mediator Pattern
The mediator pattern deals with the complexity which comes in the coding when number of classes increase. I will explain this. When we begin with development, we have a few classes and these classes interact with each other producing results. Now, consider slowly, the logic becomes more complex and functionality increases. Then what happens? We add more classes and they still interact with each other but it gets really difficult to maintain this code now. Mediator pattern takes care of this problem. It makes the code more maintainable. It promotes loose-coupling of classes such that only one class (Mediator) has the knowledge of all the classes, rest of the classes have their responsibilities and they only interact with the Mediator.
A very common example can be airplanes interacting with the control tower and not among themselves. The control tower knows exactly, where each of the airplanes is, and guides them whereas the airplanes have their own responsibilities of landing and takeoff. Another popular example is Stock exchange. In old days when there were no stock markets, the individual brokers used to buy or sell commodities among themselves. They used to face huge risks, of defaulting of counterparty, limited information (as, only limited deals in limited areas were possible), limited geographical reach, price variance (everyone could quote whatever price they wanted) and many more.
So, the concept of stock exchange came into play. For ex: BSE or NSE in India and NYSE, NASDAQ etc in the US. The stock exchange started acting like a mediator and the traders did not need to know other traders and services provided by them to make a deal. The traders have their own responsibilities as buyers and sellers and it was stock exchange’s responsibility to match their deals and get the settlement done. Many of the above mentioned risks were mitigated by this. But, there were some standardization procedures which came into picture because of this. All the traders who wanted to deal on stock exchange had to follow these standardization procedures.
Let’s look at the code part:
|
/** * StockExchange – this is the mediator class */ public class StockExchange { |
||
| public static void doTransaction (String typeOfTransaction, int quantity, Scrip scrip, Trader trader) { | ||
|
Transaction transaction = new Transaction(typeOfTransaction, quantity, scrip, trader); // try and match the current transaction // with the ones saved in DB and find out // whether a counter transaction is there or // are there many such transactions which could // fulfill requirement of this transaction. matchTransaction(transaction) |
||
|
} public static getPrice (Scrip scrip) { |
||
|
// try and match this transaction with all // the saved ones. If they match till whatever extent // trade for that. Then save, with status Traded for // number of shares traded and save the rest as New. |
||
| } | ||
| }// End of class | ||
|
/** * Trader1 – this trader wants to sell 100 shares of company XYZ */ public class Trader1 { |
|||
| public void doTransaction (String typeOfTransaction, int quantity) { | |||
|
int expectedPrice = 320; Scrip scrip = new Scrip(“XYZ”); int price = StockExchange.getPrice(scrip); if(typeOfTransaction.equals(“SELL”)){ |
|||
|
if(price >= expectedPrice){ StockExchange.doTransaction(“SELL”, 100, scrip, trader1); } |
|||
| }else if(typeOfTransaction.equals(“BUY”)){ | |||
|
if(price <= expectedPrice){ StockExchange.doTransaction(“BUY”, 100, scrip, trader1); } |
|||
| } | |||
| } | |||
| }// End of class | |||
|
/** * Trader2 – this trader wants to buyl 100 shares of company XYZ */ public class Trader2 { |
|||
| public void doTransaction (String typeOfTransaction, int quantity) { | |||
|
int expectedPrice = 320; Scrip scrip = new Scrip(“XYZ”); int price = StockExchange.getPrice(scrip); if(typeOfTransaction.equals(“SELL”)){ |
|||
|
if(price >= expectedPrice){ StockExchange.doTransaction(“SELL”, 100, scrip, trader2); } |
|||
| }else if(typeOfTransaction.equals(“BUY”)){ | |||
|
if(price <= expectedPrice){ StockExchange.doTransaction(“BUY”, 100, scrip, trader2); } |
|||
| } | |||
| } | |||
| }// End of class | |||
This is simple illustration of how we can use a mediator. Here are the main features of a mediator:
• Responsibilities to individual objects.
• Mediator is the only smart delegating object.
• Transparency for other objects.
• If more objects join in, only place of registration is Mediator, other objects do not need to know anything about the new object.
• The code becomes very maintainable.
On hind side, this brings standardization, which might be cumbersome. Also, there might be a slight loss in efficiency.
