We use the idea of message passing in both object-oriented programming as well as concurrent programming. In OOPS, message passing is a way for objects to communicate within a program. Similarly, in concurrent programming, we use it to communicate messages between processes or threads.
In this blog, we will discuss how message passing works in OOPS. For concurrent programming, we will write a separate blog later.
Message passing is similar to the idea of sending and receiving messages in real life. Suppose we are at a hotel and we want to order room service. For this, we write a message (place an order) to the room service department and put it in the internal messaging system of the hotel. Here message contains details about what we want and our room number.
Similarly in OOP, one object can send a message to another object (room service department) with specific data (order details). Then other object performs the requested action based on the information provided in the message.
In OOPS, objects are self-sufficient units that possess their own data and behaviour. Suppose one object (Sender) wants to communicate with another object (Receiver) to access data or carry out an action by calling its methods. For this, Sender will send a message to Receiver and then Receiver will perform the desired action or returns the requested data.
Here is a simple java code example of message passing:
class ObjectA {
public int sendMessageToB() {
ObjectB objectB = new ObjectB(5);
int result = objectB.message(); // ObjectA is sending a message to ObjectB
return result;
}
}
class ObjectB {
private int variable;
public ObjectB(int value){
variable = value;
}
public int message() {
// doing some calculations with variable
return variable + 1; // returning response to the message
}
}
public class Main {
public static void main(String[] args) {
ObjectA objectA = new ObjectA();
int result = objectA.sendMessageToB();
System.out.println("ObjectB's response: " + result);
}
}
In the above example, ObjectA creates an instance of ObjectB and sends a message by invoking ObjectB's public method message(). ObjectB's response is the return value of message(), which is variable + 1.
The above code is not loosely coupled. So the critical question is: How to make it loosely coupled? Here are some key steps:
interface MessageSender {
int message();
}
class ObjectB implements MessageSender {
private int variable;
public ObjectB(int value){
variable = value;
}
@Override
public int message() {
// doing some calculations with variable
return variable + 1; // returning response to the message
}
}
class ObjectA {
public int sendMessageTo(MessageSender sender) {
int result = sender.message(); // ObjectA is sending a message to a generic sender
return result;
}
}
public class Main {
public static void main(String[] args) {
ObjectB objectB = new ObjectB(5);
ObjectA objectA = new ObjectA();
int result = objectA.sendMessageTo(objectB);
System.out.println("ObjectB's response: " + result);
}
}
In the following java code example, transferMoney method of BankAccount class transfers money from one bank account to another by taking the recipient BankAccount object as an argument. After this, the transferMoney method decreases the balance of the sender and calls the receiveMoney method on the recipient BankAccount object to increase its balance.
class BankAccount {
private double balance;
public BankAccount(double balance) {
this.balance = balance;
}
public void transferMoney(BankAccount recipient, double amount) {
this.balance = this.balance - amount;
// Message passing
recipient.receiveMoney(amount);
}
public void receiveMoney(double amount) {
this.balance = this.balance + amount;
}
public double getBalance() {
return this.balance;
}
}
public class Main {
public static void main(String[] args) {
BankAccount account1 = new BankAccount(1000.0);
BankAccount account2 = new BankAccount(500.0);
account1.transferMoney(account2, 200.0);
System.out.println("Account1 balance: " + account1.getBalance());
System.out.println("Account2 balance: " + account2.getBalance());
}
}
For example, let's take an example when a sender object wants to send a message to receiver objects to notify it of an event that has occurred. Here receiver object will process the message and takes appropriate action based on the event.
class Event {
private String eventName;
public Event(String eventName) {
this.eventName = eventName;
}
public String getEventName() {
return eventName;
}
}
class EventNotifier {
private List<EventListener> listeners = new ArrayList<>();
public void addEventListener(EventListener listener) {
listeners.add(listener);
}
public void removeEventListener(EventListener listener) {
listeners.remove(listener);
}
public void notifyEvent(Event event) {
for (EventListener listener : listeners) {
listener.handleEvent(event);
}
}
}
interface EventListener {
void handleEvent(Event event);
}
class EventReceiver implements EventListener {
private String receiverName;
public EventReceiver(String receiverName) {
this.receiverName = receiverName;
}
public void handleEvent(Event event) {
System.out.println(receiverName + " Received " + event.getEventName());
// Perform appropriate action based on the event
}
}
public class Main {
public static void main(String[] args) {
EventNotifier notifier = new EventNotifier();
EventListener receiver1 = new EventReceiver("Receiver 1");
EventListener receiver2 = new EventReceiver("Receiver 2");
EventListener receiver3 = new EventReceiver("Receiver 3");
notifier.addEventListener(receiver1);
notifier.addEventListener(receiver2);
notifier.addEventListener(receiver3);
notifier.notifyEvent(new Event("Event 1"));
}
}
This idea is similar to the observer design pattern where objects (observers) register with a subject (observable) to receive notifications. When the state of the subject changes, it sends a message to all the registered observers to notify them of the change. On another side, some other design patterns in object-oriented programming also use the idea of message passing:
All of these patterns use message passing to some extent, either to coordinate the actions of different objects or to allow objects to interact with each other in a loosely coupled manner. Note: We highly recommend exploring various code examples for each of the above scenarios.
Message passing helps us to create modular, loosely coupled and self-contained objects that can easily interact with each other. In addition to this:
There are several drawbacks to this approach if we do not implement and utilize message passing correctly. For example:
Please share your thoughts in the message below. Enjoy learning, Enjoy OOPS!