javaoop

Object-oriented design: Giving too much responsibility to a value object


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.


Solution

  • 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 Employees and callhandlers 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.