Thoughts on CTCI 8.2.
I’m currently reading Crack The Code Interview book and sometimes it makes me a sad panda. Really, the way the interviewees are supposed to think about object-oriented design doesn’t make any steps beyond Stroustrup’s book with meowing cats and barking dogs. Hello, children, here you have an Animal and now please subclass it as much as you can.
There are some words about “six Ws”: who, what, where, when, how, why. But all of this starts and ends at clarifying the initial task, not on the further process.
I believe there is a significantly better way to think about OO design, even when we talk about small interview tasks. Instead of beginning with thinking in terms of classes and representations, which leads to sinking in inheritance and relationships generalizing every move, you should first think about the task in terms of actors and their APIs.
I think it’s a process that is commonly referenced as the phase of requirements analysis, but I’ve never seen any good and short material that says “Think about APIs”. I haven’t still read a lot of books considered good, so probably I’m just telling things everyone already knows. Still worth trying to express the thoughts.
Imagine you have a call center with three levels of employees: respondent, manager, and director. An incoming telephone call must be first allocated to a respondent who is free. If the respondent can’t handle the call, he or she must escalate the call to a manager. If the manager is not free or not able to handle it, then the call should be escalated to a director. Design the classes and data structures for this problem. Implement a method dispatchCall() which assigns a call to the first available employee.
So let’s think what (and who) is this system for. In other words, let’s begin desinging from the APIs for all possible actors.
First, there is a user who calls a call center. He probably has the following API:
- Check the call status (or, maybe, poll for the answer for some time)
- Talk and listen (let’s assume that it is passed to the telephone and our system shouldn’t support simplex channels and/or text chats)
- End the call
Then, there is a respondent. And there is no difference between APIs for a simple respondent and for manager:
- Check if there is an incoming call (or poll for call event with some callback)
- Answer the call
- Talk and listen
- Redirect the call to the next level [with some message]
- End the call
And the only difference in director API is that he can’t redirect the call to anyone. In fact, maybe, he can redirect a call to a manager if he solves a problem and there are only some routine tasks remaining; so we should keep the whole API, changing only “the next level” to “the previous level”.
Except the actors listed in the task, there should be some control center, which has to check the call center productivity and so on. It’s API may look like
- Get call (received/redirected/resolved) statistics for a particular employee
- Get summary call statistics
This lead us to the decision that the user API should be expanded and “End the call” should contain and additional parameter indication that “the problem was solved”.
Now we listed all possible actors so there is a rough implementation for this task. The most valuable thing is that we already have a set of actions that should be supported, so there is no need to stop and think “Hm, what else can an employee do that we might need later”.
We need a Dispatcher to dispatch calls between employees, and there also should be a Scheduler class for undispatched message queueing and hiding all external communications. Both should be thread safe, because the system is multithreaded by design. Both should perform the most common tasks in O(1), so we need a queue for every respondent type.
Actual request routing is not shown in the scheme below because it is beyond the scope of the task. The implementation doesn’t pretend to be good or something, the main idea of this post is the way of thinking shown above.
There is no interface for statistics (because the task doesn’t ask for it although it’s easy to implement). There is also no object interpretation for User, because we don’t have any user-related data at the time.