I came across this design question in the book Cracking the Coding Interview:
Imagine you have a call center with three levels of employees: fresher, technical lead (TL), product manager (PM). There can be multiple employees, but only one TL or PM. An incoming telephone call must be allocated to a fresher who is free. If a fresher can’t handle the call, he or she must escalate the call to technical lead. If the TL is not free or not able to handle it, then the call should be escalated to PM. Design the classes and data structures for this problem. Implement a method getCallHandler().
Solution in the book
public class CallHandler {
static final int LEVELS = 3; // we have 3 levels of employees
static final int NUM_FRESHERS = 5; // we have 5 freshers
ArrayList<Employee>[] employeeLevels = new ArrayList[LEVELS];
// queues for each call’s rank
Queue<Call>[] callQueues = new LinkedList[LEVELS];
public CallHandler() { ... }
Employee getCallHandler(Call call) {
for (int level = call.rank; level < LEVELS - 1; level++) {
ArrayList<Employee> employeeLevel = employeeLevels[level];
for (Employee emp : employeeLevel) {
if (emp.free) {
return emp;
}
}
}
return null;
}
// routes the call to an available employee, or adds to a queue
void dispatchCall(Call call) {
// try to route the call to an employee with minimal rank
Employee emp = getCallHandler(call);
if (emp != null) {
emp.ReceiveCall(call);
} else {
// place the call into queue according to its rank
callQueues[call.rank].add(call);
}
}
void getNextCall(Employee e) {...} // look for call for e’s rank
}
class Call {
int rank = 0; // minimal rank of employee who can handle this call
public void reply(String message) { ... }
public void disconnect() { ... }
}
class Employee {
CallHandler callHandler;
int rank; // 0- fresher, 1 - technical lead, 2 - product manager
boolean free;
Employee(int rank) { this.rank = rank; }
void ReceiveCall(Call call) { ... }
void CallHandled(Call call) { ... } // call is complete
void CannotHandle(Call call) { // escalate call
call.rank = rank + 1;
callHandler.dispatchCall(call);
free = true;
callHandler.getNextCall(this); // look for waiting call
}
}
class Fresher extends Employee {
public Fresher() { super(0); }
}
class TechLead extends Employee {
public TechLead() { super(1); }
}
class ProductManager extends Employee {
public ProductManager() { super(2); }
}
This solution is not very satisfying mainly because it involves passing a CallHandler
object to Employee
. I think Employee
should be treated as a value object meaning its job should be mostly holding data without being aware of entities that contain the real business logic (like CallHandler
). So I am interested in finding out what better ways there are to design this. I come from ActionScript background and in that I would probably use ActionScript's event model to send messages from Employee
and listen to them in CallHandler
.
There are an infinite number of ways to design this system (that's what makes developing software so fun) some ways are better than others. The provided answer is not the best but it works.
You have to have some way of an Employee to escalate a call by doing somekind of callback to the Callhandler
. Whether that is done by passing around the Callhandler
or having Employee
s and callhandler
s listening to events are both good ideas. The given solution is simpler and therefore more easy to understand for the target audience. An event based solution is more complicated, harder to write but more scalable and easier to modify.
For example, if you had to add a new feature for some kind of "overseer" to monitor how often Employees successfully resolve calls versus how many times they escalate, it would be easy to just write a new event listener than try to shoe-horn a new object between the Employee and the Callhandler.
Basically, yes your ideas are probably better than the solution, but they both answer the question.