Message Chains

Description (What)

Message chains are simply multiple chained calls to functions to retrieve data. Message chains create a chain of dependency throughout the program which can be harder to keep track of as the chain increases and any changes made at any part of the chain might require refactoring in other parts of the chain

How to Locate It (Where)

Look for calls resembling a().b().c().d() where data is being retrieved through a chain of function calls where each function in the call calls another function and retrieves data from it.

How It Manifests (Why)

While writing code in silos, it comes naturally that when you need to interact between Classes or Methods, it will require using chains to bring information to the client surface.

How to Fix It (Possible Treatments)

In Example 1, Hide Delegate is used to push the chain to an inner class which leads the message chain being hidden behind a single public method which itself contains a chain of private or internal methods. Hide Delegate simplifies the end call for the client.

In Example 2, Move Methods is used when the message chain comes into form due to the placements of methods.

We could address the code smell by moving the methods back to which they belong to.

Other treatments are also possible based on the specific scenario, they can be found here

Examples

Example 1

Before:

For the vehicle to get the address for the latest order to be delivered, it needs to go through a message chain of going to the Distributor object which returns a Warehouse object which returns an Order object from which the address can be retrieved.

Observed Code Smells:
- Message Chains (line 117)

After:

Applied Hide Delegate by adding a new public method getLatestOrderDestination to Distributor class to make the calls internal

Removed getWarehouse() method from Distributor so it remains private and reduces its public exposure.

Refactoring Applied:
- Message Chains
    - Hide Delegate (getWarehouse())
Observed Code Smells After Refactoring:
- None

Example 2

Before:

Here the message chains arise when, an attendance manager wants to know if an employee is on shift at a particular

point in time. The attendance manager has to get the employee instance first, and then get the schedule of that employee

finally arrives at the boolean if the employee is on shift: AttendanceManager -> Employee -> Schedule, where the class

Employee functions like an agent, but it should not be.

Attendance Manager should have this method directly inside its own class.

In other words, Attendance Manager should not go through an employee to know if they are on shift.

Observed Code Smells:
- Message Chains (line 78, line 82)

After:

The two methods of isOnShift() should sit in the class Attendance Manager because to know if an employee is on shift

is the duty of the attendance manager.


Refactoring Applied:
- Message Chains:
    - Move Method (lines 30-33, lines 35-38)
Observed Code Smells After Refactoring:
- None

When to Ignore

In cases where solving this code smell might inadvertently cause the Middle Man code smell.

More

More about Message Chains

001  import java.util.LinkedList;
002  import java.util.Queue;
003  
004  class Order {
005   private String item;
006   private String destination;
007   private boolean isDelivered;
008  
009   public Order(String item, String destination) {
010   this.item = item;
011   this.destination = destination;
012   this.isDelivered = false;
013   }
014  
015   public String getOrderDetails() {
016   return "Item: " + this.item + " to: " + this.destination + "\nDelivered: " + this.isDelivered;
017   }
018  
019   public String getDestination() {
020   return destination;
021   }
022  
023   public void markOrderDelivered() {
024   this.isDelivered = true;
025   }
026  
027   public boolean getDeliveredStatus() {
028   return this.isDelivered;
029   }
030  }
031  
032  class Warehouse {
033   private String address;
034   private String owningEntity;
035   private Queue<Order> orders;
036  
037   public Warehouse(String address, String owningEntity) {
038   this.address = address;
039   this.owningEntity = owningEntity;
040   this.orders = new LinkedList<>();
041   }
042  
043   public void addOrder(int orderId, Order newOrder) {
044   orders.add(newOrder);
045   }
046  
047   public Order getLatestOrder() {
048   return orders.peek();
049   }
050  
051   public String getWarehouseDetails() {
052   return "Owned by " + this.owningEntity + "located at " + this.address;
053   }
054  
055   public int getPendingOrderSize() {
056   return orders.size();
057   }
058  
059   public void checkAndEmptyWarehouse() throws Error {
060   for (Order order : orders) {
061   if (!order.getDeliveredStatus()) {
062   throw new Error("Warehouse is not empty!");
063   }
064   }
065   orders.clear();
066   }
067  }
068  
069  class Distributor {
070   private String owner;
071   private String address;
072   private Warehouse warehouse;
073  
074   public Distributor(String owner, String address, Warehouse warehouse) {
075   this.owner = owner;
076   this.address = address;
077   this.warehouse = warehouse;
078   }
079  
080   public Warehouse getWarehouse() {
081   return this.warehouse;
082   }
083  
084   public Order getLatestOrder() {
085   return this.warehouse.getLatestOrder();
086   }
087  
088   public String getDistributorDetails() {
089   return "Owned by " + this.owner + " located at " + this.address;
090   }
091  
092  }
093  
094  class Vehicle {
095   private String vehicleNumber;
096   private String model;
097   private Distributor distributor;
098  
099   public Vehicle(Distributor distributor, String vehicleNumber, String model) {
100   this.vehicleNumber = vehicleNumber;
101   this.model = model;
102   this.distributor = distributor;
103   }
104  
105   public String getVehicleDetails() {
106   return this.vehicleNumber + "\n" + this.model;
107   }
108  
109   public void stationVehicle() {
110   System.out.println(this.vehicleNumber + " is now stationed");
111   }
112  
113   public void startVehicle() {
114   System.out.println("Vehicle has been started");
115   }
116  
117   public void getOrderRoute() {
118   String destination = distributor.getWarehouse().getLatestOrder().getDestination();
119   System.out.println(vehicleNumber + " inbound to " + destination);
120   }
121  
122  }
123  
124  public class MCBE1 {
125  
126   public static void main(String[] args) {
127   Order boots = new Order("Timberlands", "1191 Boylston St");
128   Order hat = new Order("Red Sox Cap", "1193 Boylston St");
129   Warehouse fenway = new Warehouse("1203 Akron St", "Fenway Goods Storage");
130   fenway.addOrder(1, boots);
131   fenway.addOrder(2, hat);
132   Distributor redSox = new Distributor("Red Sox Co.", "42 Chanice Blvd", fenway);
133   Vehicle jeep = new Vehicle(redSox, "9XJ3F", "Mercedes GLX");
134   jeep.getOrderRoute();
135   }
136  }
137