OOD Use Case: Solving call center problem
Muhammad Salem
Posted on July 5, 2024
Call Center: 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.
Thought Process and Design Decisions
Identify Core Entities: The primary entities are
Call
,Employee
,Respondent
,Manager
, andDirector
. Each employee can handle a call or escalate it if they are unable to handle it.-
Class Hierarchy:
-
Employee
is the base class forRespondent
,Manager
, andDirector
. -
Call
represents an incoming call.
-
-
Behavior:
-
Employee
has methods tohandleCall
andescalateCall
. - Each
Employee
can be in one of the three states: available, busy, or on break.
-
-
Data Structures:
- A queue to manage incoming calls.
- Separate lists for available respondents, managers, and directors.
-
Method
dispatchCall
:- Check for the availability of respondents first, then managers, and finally directors.
- Escalate the call if no lower-level employees are available.
Class Design
using System;
using System.Collections.Generic;
public enum EmployeeLevel
{
Respondent,
Manager,
Director
}
public enum CallStatus
{
Waiting,
InProgress,
Completed
}
public class Call
{
public int Id { get; set; }
public CallStatus Status { get; set; }
public Call(int id)
{
Id = id;
Status = CallStatus.Waiting;
}
}
public abstract class Employee
{
public int Id { get; set; }
public EmployeeLevel Level { get; set; }
public bool IsFree { get; set; } = true;
public Employee(int id, EmployeeLevel level)
{
Id = id;
Level = level;
}
public abstract void HandleCall(Call call);
}
public class Respondent : Employee
{
public Respondent(int id) : base(id, EmployeeLevel.Respondent) { }
public override void HandleCall(Call call)
{
if (IsFree)
{
Console.WriteLine($"Respondent {Id} is handling call {call.Id}");
IsFree = false;
call.Status = CallStatus.InProgress;
}
else
{
Console.WriteLine($"Respondent {Id} cannot handle call {call.Id} and needs to escalate.");
}
}
}
public class Manager : Employee
{
public Manager(int id) : base(id, EmployeeLevel.Manager) { }
public override void HandleCall(Call call)
{
if (IsFree)
{
Console.WriteLine($"Manager {Id} is handling call {call.Id}");
IsFree = false;
call.Status = CallStatus.InProgress;
}
else
{
Console.WriteLine($"Manager {Id} cannot handle call {call.Id} and needs to escalate.");
}
}
}
public class Director : Employee
{
public Director(int id) : base(id, EmployeeLevel.Director) { }
public override void HandleCall(Call call)
{
if (IsFree)
{
Console.WriteLine($"Director {Id} is handling call {call.Id}");
IsFree = false;
call.Status = CallStatus.InProgress;
}
else
{
Console.WriteLine($"Director {Id} cannot handle call {call.Id}.");
}
}
}
public class CallCenter
{
private Queue<Call> callQueue = new Queue<Call>();
private List<Respondent> respondents = new List<Respondent>();
private List<Manager> managers = new List<Manager>();
private List<Director> directors = new List<Director>();
public CallCenter(int numRespondents, int numManagers, int numDirectors)
{
for (int i = 1; i <= numRespondents; i++)
respondents.Add(new Respondent(i));
for (int i = 1; i <= numManagers; i++)
managers.Add(new Manager(i));
for (int i = 1; i <= numDirectors; i++)
directors.Add(new Director(i));
}
public void ReceiveCall(Call call)
{
callQueue.Enqueue(call);
DispatchCall();
}
public void DispatchCall()
{
if (callQueue.Count == 0)
return;
Call call = callQueue.Dequeue();
foreach (var respondent in respondents)
{
if (respondent.IsFree)
{
respondent.HandleCall(call);
return;
}
}
foreach (var manager in managers)
{
if (manager.IsFree)
{
manager.HandleCall(call);
return;
}
}
foreach (var director in directors)
{
if (director.IsFree)
{
director.HandleCall(call);
return;
}
}
Console.WriteLine($"No available employee to handle call {call.Id}. Adding back to queue.");
callQueue.Enqueue(call);
}
public void EndCall(Call call, Employee employee)
{
Console.WriteLine($"Call {call.Id} completed by {employee.Level} {employee.Id}");
call.Status = CallStatus.Completed;
employee.IsFree = true;
DispatchCall();
}
}
Explanation and Trade-offs
-
Class Hierarchy:
- Using inheritance (
Respondent
,Manager
,Director
derive fromEmployee
) allows us to define shared behaviors and properties in the base class (Employee
). This makes the code more maintainable and reduces duplication.
- Using inheritance (
-
Method Responsibility:
-
HandleCall
method is abstract inEmployee
and implemented by each subclass. This ensures that each type of employee can have specific handling logic if needed. -
DispatchCall
inCallCenter
coordinates the allocation of calls, ensuring that calls are escalated appropriately if lower-level employees are busy.
-
-
State Management:
- The
IsFree
property onEmployee
ensures that we can check if an employee is available to take a call. This simplifies the logic for dispatching calls.
- The
-
Data Structures:
- Using a
Queue
for calls ensures that calls are handled in the order they are received, which is typically expected in a call center. - Separate lists for each level of employee (
respondents
,managers
,directors
) allow us to easily iterate and find the first available employee at each level.
- Using a
Trade-offs and Flexibility
-
Scalability:
- The design can scale to larger numbers of employees without significant changes. Adding more respondents, managers, or directors only involves updating the initialization logic.
-
Extensibility:
- Adding new types of employees (e.g.,
Supervisor
) would require minimal changes. We could simply extend theEmployee
class and update theCallCenter
dispatch logic.
- Adding new types of employees (e.g.,
-
Separation of Concerns:
- The separation of responsibilities between the
CallCenter
andEmployee
classes ensures that each class has a single responsibility. This follows the Single Responsibility Principle (SRP) and makes the system easier to maintain.
- The separation of responsibilities between the
-
Complexity:
- The simplicity of the design may need to be balanced against the need for more complex features, such as handling priority calls or dynamically adjusting employee availability based on load. However, the current design provides a solid foundation for such extensions.
By carefully considering these design decisions and trade-offs, we can build a robust and maintainable call center system that meets the needs of both current requirements and potential future enhancements.
Posted on July 5, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.