Обзор объектно-ориентированный дизайн для выборочного опроса вопрос


Я иду на интервью на вопросы "трескать кодирование интервью" и одна из глав (Глава 7) посвящена объектно-ориентированного дизайна (ООД). Требования к одной из проблем являются следующие:

Представьте, у вас есть колл-центр с три уровня сотрудников: свежее, технический руководитель (Каховская), менеджер по продукции (ТЧ). Может быть несколько сотрудников, но только одно ПЯ или ПМ. Входящий телефонный звонок должен быть выделен посвежее, кто свободен. Если свежее не могу справиться со звонка, он или она должны нагнетать призыв к технический руководитель. Если ТЛ не свободен или не в состоянии справиться с этим, то вызов должен быть накалилась до вечера. Дизайн классов и структуры данных для этой проблемы. Реализовать метод getCallHandler().

Я написал решение на C#, он не был составлен (по предложению автора книги), и он не многопоточный. Мое решение немного долго, но оно должно быть очень простым для понимания.

Основываясь на моем понимании проблемы, я установил цепь "команды", где каждый сотрудник знает, кто его/ее вышестоящим руководством. Звонки обрабатываются следующим образом:

  • Если свежее присваивается позывной, который он/она не может справиться, то свежее обостряется вызов своим начальником: техническое.
  • Если нет новичков, то техника автоматически получает вызов.
  • Если технический руководитель занят и не может обслужить вызов, тогда он/она обостряет позвонить менеджеру продукта.
  • Если менеджер продукта (ПМ) занят, то вызов становится в очередь на очередь в ПМ и удалены из очереди после того, как их текущий вызов обслуживается.

Примечание: многопоточность будут добавлены в более поздней версии, поэтому, Пожалуйста, игнорируйте любые проблемы многопоточности.

enum EStatus{ HANDLING, ESCALATED, QUEUED }
enum ERank{ FRESHER, TECH_LEAD, PROD_MANAGER }

abstract class Employee
{
    public ERank Rank{get; private set;}

    public Employee(ERank rank)
    {
        Rank = rank;
    }

    public abstract EStatus ServiceCall(Call call);
}

class Fresher:Employee
{
    private TechLead _superior;

    public Fresher(TechLead superior):base(ERank.FRESHER)
    {
        _superior = superior;
    }

    public override EStatus ServiceCall(Call call)
    {
        if(CanService(call))
        {
            call.Service(this);
            return EStatus.HANDLING;
        }
        else
        {
            _superior.ServiceCall(call);
            return EStatus.ESCALATED;
        }
    }

    private bool CanService(Call call)
    {
        // some logic to determine if the call can be serviced 
    }
}

class TechLead:Employee
{
    private Call _activeCall;
    private ProductManager _superior;

    public TechLead(ProductManager superior):base(ERank.TECH_LEAD)
    {
        _activeCall = null;
        _superior = superior;
    }

    public override EStatus ServiceCall(Call call)
    {
        if(_activeCall == null && CanService(call))
        {
            HandleCall(call);
            return EStatus.HANDLING;
        }
        else
        {
            _superior.ServiceCall(call);
            return EStatus.ESCALATED;
        }
    }

    private bool CanService(Call call)
    {
        // some logic to determine if the call can be serviced 
    }

    private HandleCall(Call call)
    {
        _activeCall = call;
        call.OnCallServiced += new Call.CallServicedDelegate(OnCallServiced);
        call.Service(this);
    }

    private void OnCallServiced(Call call)
    {
        if(call!=_activeCall)
        {
            // TODO: this call was never accepted
        }
        else
        {
            _activeCall = null;
        }
    }
}

class ProductManager:Employee
{
    private Call _activeCall;
    private Queue<Call> _callQueue;

    public ProductManager():base(ERank.PROD_MANAGER)
    {
        _activeCall = null;
        _callQueue = new Queue<Call>();
    }

    public override EStatus ServiceCall(Call call)
    {
        if(_activeCall == null)
        {
            HandleCall(call);
            return EStatus.HANDLING;
        }
        else
        {
            _callQueue.Enqueue(call);
            return EStatus.QUEUED;
        }
    }

    private HandleCall(Call call)
    {
        _activeCall = call;
        call.OnCallServiced += new Call.CallServicedDelegate(OnCallServiced);
        call.Service(this);
    }

    private void OnCallServiced(Call call)
    {
        if(call!=_activeCall)
        {
            // TODO: this call was never accepted
        }
        else
        {
            if(_callQueue.Count==0)
            {
                _activeCall = null;
            }
            else
            {
                HandleCall(_callQueue.Dequeue());
            }
        }
    }
}

class Call
{
    public delegate void CallServicedDelegate(Call call);
    public event CallServicedDelegate OnCallServiced;

    public Employee CallHandler{get; private set;}

    public Call()
    {
        CallHandler = null;
    }

    public void Service(Employee callHandler)
    {
        CallHandler = callHandler;

        // Invoke the notification when the call is complete
        OnCallServiced(this);
    }

    public void Disconnect()
    {
        // Disconnect the call
    }
}

class CallCenter
{
    private bool _running;
    private BlockingQueue<Call> _callQueue;
    private Queue<Employee> _freeFreshers;
    private List<Employee> _busyFreshers;
    private readonly TechLead _techLead;

    public CallCenter(int numEmployees)
    {
        // create the tech lead and a supervising product manager
        _techLead = TechLead(new ProductManager());

        _callQueue = new BlockingQueue<Call>();
        _freeFreshers = new Queue<Employee>();
        _busyFreshers = new List<Employee>();

        for(int i = 0; i < numEmployees; i++)
        {
            _freeFreshers.Enqueue(new Fresher(_techLead));
        }
    }

    public void AcceptCall(Call call)
    {
        _callQueue.Enqueue(call);
    }

    // Assuming that threading will be handled
    public void StartCallService()
    {
        _running = true;

        while(_running)
        {
            Call call = _callQueue.Dequeue();

            // Subscribe for the on call serviced delegate
            call.OnCallServiced += new Call.CallServicedDelegate(OnCallServiced);

            if(_freeFreshers.Count>0)
            {
                // Block until a fresher is available
                 Employee e = _freeFreshers.Dequeue();

                 // Assign the call to a free fresher
                switch(e.ServiceCall(call))
                {
                    case EStatus.HANDLING:
                        _busyFreshers.Add(e);
                        break;
                    case EStatus.ESCALATED:
                        _freeFreshers.Enqueue(e);
                        break;
                    case EStatus.QUEUED:
                    default:
                        break;
                }
            }
            else
            {
                _techLead.ServiceCall(call);
            }
        }
    }

    // Assuming that threading will be handled
    public void StopCallService()
    {
        _running = false;
    }

    private void OnCallServiced(Call call)
    {
        switch(call.CallHandler.Rank)
        {
            case ERank.FRESHER:
                _busyFreshers.Remove(call.CallHandler);
                _freeFreshers.Enqueue(call.CallHandler);
                break;
            case ERank.TECH_LEAD:
            case ERank.PROD_MANAGER:
            default:
                // nothing to do if it's a tech lead or prod manager
                break;
        }
    }
}

Я надеялся получить некоторую обратную связь, поскольку этот вопрос пришел от книги, есть много возможных паводков решений и не имеют реальных интервьюера прокомментировать решение. Кроме того, у меня есть авторское решение и я могу разместить его в случае необходимости.

- Это похоже на достойное решение проблемы, как говорится? Есть что-то, что мне определенно надо улучшить?



6734
9
задан 10 февраля 2011 в 10:02 Источник Поделиться
Комментарии
4 ответа

Короче, на мой взгляд, я думаю, вы сделали слишком много.

Кажется, что ваш дизайн вышел за дизайн для первоначального интервью вопрос и в упражнение самостоятельно.

Однако, у меня нет книги, которую Вы читаете, так что вы можете ходить больше, чем просто фрагмент кода в ваш вопрос.

Исходя из принципа:


Сделать самое простое, что может
возможно работать.

Основы я тянул с этим вопросом было:


  • Нам нужен способ представлять три разных вида сотрудников.

  • Нам нужен способ представления вызова.

  • Нам нужен способ выбора сотрудников для обработки звонков (метод getCallHandler).

т. е. я думаю, что вопрос только после реализации GetCallHandler и некоторые базовые определения типов, участвующих.

Я придумал следующее:

public class Call
{
// TODO: Implement Call.
}

public enum EmployeePosition
{
Fresher,
TechnicalLead,
ProductManager
}

public class CallCentre
{
private IList<IEmployee> employees;

public IEmployee GetCallHandler(Call call)
{
return employees.Where(e => e.CanHandle(call))
.OrderBy(e => e.Position)
.FirstOrDefault();
}

public CallCentre(IList<IEmployee> employees)
{
if (employees == null)
{
throw new ArgumentNullException("employees");
}

this.employees = employees;
}
}

public interface IEmployee
{
EmployeePosition Position { get; }
Boolean CanHandle(Call call);
}

public class Fresher : IEmployee
{
public EmployeePosition Position { get { return EmployeePosition.Fresher; } }

public Boolean CanHandle(Call call)
{
// TODO: Logic for handling a call.
}
}

// Similar implementations of IEmployee for TechnicalLead and ProductManager.

Это будет отправная точка, основываясь на том, что вопрос говорит. После этого, как правило, в период взаимодействия между интервьюируемого и интервьюера(ов), в которых предположения могут быть рассмотрены и дизайн обсуждается/обновлено с большим количеством деталей, таких как человек, находясь в состоянии "занят" и т. д.

Я думаю, что ваша конструкция-это шаг вперед.

Сочетание класса и перечисления для разных типов сотрудников-это то, что мы оба отправились. Однако, я выбрал интерфейс базового класса. Я не думаю, что ваш базовый класс предоставляет большое значение - просто код, чтобы присвоить звание собственность. Мое предложение имеет то преимущество, что никто не собирается нарушать реализации для некоторых сотрудников ненароком путем изменения базового класса. Интерфейс не содержит реализации. Используя интерфейс также позволяет классам работника использовать другой базовый класс в будущем, если потребности быть.

Одна точка для дальнейшего обсуждения может быть где говорится:


Если свежее не могу справиться с ним
или она должна нагнетать вызов
технический руководитель.

Это позволяет предположить, что они хотят посвежее объект непосредственно общаться со своим руководителем, как в вашей конструкции (в отличие от моей реализации, где сотрудники не должны знать друг друга, даже существовать - похож на многие рабочие места!). Это может быть разъяснено в интервью. Однако, я предпочитаю мой дизайн, потому что ослабленные связи между сотрудниками.

Насколько многопоточность идет, я ожидала совсем немного обсуждение


  • как это может быть реализовано

  • потенциальные проблемы с различных потоковых моделей

  • потенциальные проблемы

  • типичного громкость звонка

прежде чем он будет добавлен в какой-либо код. Но, как я говорю, похоже на то, что ваш дизайн-это больше, чем просто ответ на вопрос интервью.

8
ответ дан 10 февраля 2011 в 09:02 Источник Поделиться

Извините, если отсутствует любезностей, но я собираюсь спуститься к нему.

Источник во время обзора:

enum EStatus{ HANDLING, ESCALATED, QUEUED }
enum ERank{ FRESHER, TECH_LEAD, PROD_MANAGER }

abstract class Employee
{
public ERank Rank{get; private set;}

public Employee(ERank rank)
{
Rank = rank;
}

public abstract EStatus ServiceCall(Call call);
}

class Fresher:Employee
{
private TechLead _superior;

public Fresher(TechLead superior):base(ERank.FRESHER)
{
_superior = superior;
}

public override EStatus ServiceCall(Call call)
{
if(CanService(call))
{
call.Service(this);
return EStatus.HANDLING;
}
else
{
_superior.ServiceCall(call);
return EStatus.ESCALATED;
}
}

private bool CanService(Call call)
{
// some logic to determine if the call can be serviced
}
}

class TechLead:Employee
{
private Call _activeCall;
private ProductManager _superior;

public TechLead(ProductManager superior):base(ERank.TECH_LEAD)
{
_activeCall = null;
_superior = superior;
}

public override EStatus ServiceCall(Call call)
{
if(_activeCall == null && CanService(call))
{
HandleCall(call);
return EStatus.HANDLING;
}
else
{
_superior.ServiceCall(call);
return EStatus.ESCALATED;
}
}

private bool CanService(Call call)
{
// some logic to determine if the call can be serviced
}

private HandleCall(Call call)
{
_activeCall = call;
call.OnCallServiced += new Call.CallServicedDelegate(OnCallServiced);
call.Service(this);
}

private void OnCallServiced(Call call)
{
if(call!=_activeCall)
{
// TODO: this call was never accepted
}
else
{
_activeCall = null;
}
}
}

class ProductManager:Employee
{
private Call _activeCall;
private Queue<Call> _callQueue;

public ProductManager():base(ERank.PROD_MANAGER)
{
_activeCall = null;
_callQueue = new Queue<Call>();
}

public override EStatus ServiceCall(Call call)
{
if(_activeCall == null)
{
HandleCall(call);
return EStatus.HANDLING;
}
else
{
_callQueue.Enqueue(call);
return EStatus.QUEUED;
}
}

private HandleCall(Call call)
{
_activeCall = call;
call.OnCallServiced += new Call.CallServicedDelegate(OnCallServiced);
call.Service(this);
}

private void OnCallServiced(Call call)
{
if(call!=_activeCall)
{
// TODO: this call was never accepted
}
else
{
if(_callQueue.Count==0)
{
_activeCall = null;
}
else
{
HandleCall(_callQueue.Dequeue());
}
}
}
}

class Call
{
public delegate void CallServicedDelegate(Call call);
public event CallServicedDelegate OnCallServiced;

public Employee CallHandler{get; private set;}

public Call()
{
CallHandler = null;
}

public void Service(Employee callHandler)
{
CallHandler = callHandler;

// Invoke the notification when the call is complete
OnCallServiced(this);
}

public void Disconnect()
{
// Disconnect the call
}
}

class CallCenter
{
private bool _running;
private BlockingQueue<Call> _callQueue;
private Queue<Employee> _freeFreshers;
private List<Employee> _busyFreshers;
private readonly TechLead _techLead;

public CallCenter(int numEmployees)
{
// create the tech lead and a supervising product manager
_techLead = TechLead(new ProductManager());

_callQueue = new BlockingQueue<Call>();
_freeFreshers = new Queue<Employee>();
_busyFreshers = new List<Employee>();

for(int i = 0; i < numEmployees; i++)
{
_freeFreshers.Enqueue(new Fresher(_techLead));
}
}

public void AcceptCall(Call call)
{
_callQueue.Enqueue(call);
}

// Assuming that threading will be handled
public void StartCallService()
{
_running = true;

while(_running)
{
Call call = _callQueue.Dequeue();

// Subscribe for the on call serviced delegate
call.OnCallServiced += new Call.CallServicedDelegate(OnCallServiced);

if(_freeFreshers.Count>0)
{
// Block until a fresher is available
Employee e = _freeFreshers.Dequeue();

// Assign the call to a free fresher
switch(e.ServiceCall(call))
{
case EStatus.HANDLING:
_busyFreshers.Add(e);
break;
case EStatus.ESCALATED:
_freeFreshers.Enqueue(e);
break;
case EStatus.QUEUED:
default:
break;
}
}
else
{
_techLead.ServiceCall(call);
}
}
}

// Assuming that threading will be handled
public void StopCallService()
{
_running = false;
}

private void OnCallServiced(Call call)
{
switch(call.CallHandler.Rank)
{
case ERank.FRESHER:
_busyFreshers.Remove(call.CallHandler);
_freeFreshers.Enqueue(call.CallHandler);
break;
case ERank.TECH_LEAD:
case ERank.PROD_MANAGER:
default:
// nothing to do if it's a tech lead or prod manager
break;
}
}
}

Наблюдения по инстанциям.

Первая вещь-это "субординацию" ведет себя таким образом, другие то, что указано в задаче. Проблема государства:


Входящий телефонный звонок должен быть выделен посвежее, кто свободен.

Определенные цепочки "из команд" поведение...


Если нет новичков, то техника автоматически получает вызов.

...стоит в оппозиции к этому. Кроме того, в том, как решение реализовано, то из этого следует, что если технический руководитель занят, то вызов получает bumped до менеджера продукта. Если менеджер занят, то вызов ставится в очередь личный звонок, подождать, пока он свободен.

Такое поведение есть следствие заполнения ПМС очередь личный разговор с несортированной звонки, когда всех новичков и технологий занят. Это делает ТЧ отвечает за показ всех этих разговоров, даже если в какой-то момент посвежее должны были стать бесплатными.

Предложил улучшение.

Реализовать базовый класс абонента таким образом, что он должен присвоении очереди^ звонков на часы. Это позволило бы за 2 вещи:


  1. Все освежители могли наблюдать одну и ту же очередь^ новых вызовов. При поступлении нового вызова, то он может быть добавлен к общей свежее очереди^.

  2. Техническим руководителем и менеджером проекта могут быть назначены свои собственные очереди^, где обострилась звонки могло ждать их, чтобы стать доступным.

Это позволяет для всех новых вызовов ждать посвежее будет бесплатным, а для TL и PM имеют только звонки в очереди к ним, что они должны ответить.

Очереди ^могут быть перечислены. Читать далее...

Наблюдения на поведение очереди.

В реализации я насчитал 3 очереди и список бонусов.


  1. _callQueue в коллцентре. Намерена содержать новые вызовы.

  2. _freeFreshers в коллцентре. Держит новичков без звонков.

  3. _callQueue в ProductManager. Проводит ПМС звонки или без обслуживания вызовов (см. субординацию).

  4. _busyFreshers* в коллцентре. Это не совсем очереди, но он удерживает новичков от _freeFreshers очереди, когда они не свободны.

1 квартал:

В конструкции, в этой очереди стоят входящие звонки. На практике, 1 квартал держит пока(_running) цикл в методе StartCallService съел столько процессор, как это возможно. Как только звонок будет добавлен в 1 квартале он незамедлительно удалены из очереди и обрабатывается (а)абонент бесплатно, (Б) техническим руководителем, (в) менеджер проекта, или (D) бросается в ПМС очереди. Ничего никогда не остается в Q1.

Q2 И Q4:Есть

К2 и К4 просто отслеживать новичков напряженного состояния. К4 существует только поэтому, что занят новичков не потеряли? Это также обеспечивает набор поведения, выбирая которые бесплатно свежее принимает вызов.

В3:

Эта очередь ведет себя как очереди. Абоненты здесь ждать внимания со стороны менеджера проекта.

Предложил улучшение.

Я борюсь с тем, что писать здесь, потому что минус несколько конструктивных недостатков в других местах, эти очереди очень функциональный. Но, что на самом деле должны быть заданы, функция очереди действительно хотел?

В случае Q2 и Q4, который проводим свободное государство и диктовать, кто получает следующий вызов. Это делает больше смысла, чтобы добавить свойство IsBusy к работнику. Этак коллцентра можно просто иметь список _freshers. Чтобы определить, свежее занят, вы просто спросите. Чтобы получить бесплатную свежее, Вы больше не застряли с теми, кто высовывается из Q2. Вы можете просто попросить сотрудника, который соответствует каким-либо критериям вам нравится.

Точно так же в 1 квартале может быть заменена на список. Когда новый призыв приходит в назначенный Лучшие бесплатные посвежее. Если нет свежей доступен он будет добавлен в список неотвеченных вызовов. Когда очухался заканчивает называть чека без ответа вызовов производится. Это позволяет для ликвидации время(_running) петли, а я люблю исключения петель, очень люблю, правда.

Наблюдение на использование перечислений

Определенные перечисления...

enum EStatus{ HANDLING, ESCALATED, QUEUED }
enum ERank{ FRESHER, TECH_LEAD, PROD_MANAGER }

...на самом деле в конечном итоге просто скопировал информацию. ERank можно определить по классу, и EStatus может быть определена по очереди или сотрудник звонка.

Предложил улучшение.

Просто избавиться от них.

О, getCallHandler()

На данный момент это кажется глупо (и немного таинственно определенными) дело осуществить. Может быть, другой пользователь, почему это разумнее всего реализовать?

Но требование-это требование. И, твой звонок.CallHandler, собственность имеет право идея. Я бы просто сделать это так называемый метод getCallHandler() играть по букве закона, и нуль разумный значение, возвращаемое, если никто не обслужил вызов еще.

Как всегда, я приветствую комментарии и критику. Ура!

4
ответ дан 10 февраля 2011 в 11:02 Источник Поделиться

Похоже, это может быть хорошей идеей, чтобы обеспечить решение из книги просто установить рамки:

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); }
}

Решение книги схожие по конструкции и степени... нет никаких дополнительных, кроме того, что обеспечивается цитата вышеуказанной информации. Моя оценка заключается в том, что:


  1. Я на самом деле не сделать так называемый метод GetCallHandler(), я преобразовал его в собственность звоните.

  2. Была конкретная причина, почему GetCallHandler() метод необходим, т. е. необходимо для другого компонента системы.

У меня есть обоснование моей конструкции по сравнению с авторским дизайном:


  1. Это более эффективно для поддержания очереди свободных сотрудников, чем действительно пройти через список сотрудников и проверить, какой из них свободен, каждый раз, когда мне позвонили.

  2. Поведение в ответ на вызов определяется конкретной реализации работником, а не сотрудником "эксперта" (т. е. каждый сотрудник знает, что они должны делать, а не система рассказывая друг сотрудника, что делать).

  3. Если GetCallHandler метод необходим в другую систему, тогда вопрос был бы более конкретную сигнатуру метода.

  4. Судя по решению, GetCallHandler только называется CallHandler класса сама, так что поддерживает #3. Никто за пределами CallHandler , кажется, чтобы использовать его.

0
ответ дан 10 февраля 2011 в 10:02 Источник Поделиться

Здесь работает редакция моего решения (на основе всех ответов):

enum ERank{ FRESHER = 0, TECH_LEAD = 1, PROD_MANAGER = 2 }

abstract class Employee
{
// Properties
public ERank Rank{get; private set;}
public bool Busy{get; priavte set;}

// Fields
protected static readonly CallService _callService = new CallService(SOME_GLOBAL_VALUE);

// Constructors
public Employee(ERank rank)
{
Rank = rank;
Busy = false;
}

public void ServiceCall(Call call)
{
if(CanService(call))
{
Busy = true;
call.Service(this);
}
else
{
// Only escalate if there is a superior rank available
if(!Busy && (Rank != ERank.PROD_MANAGER))
{
call.Escalate();
}

// Put the call back onto the queue
_callService.AcceptCall(call);
}
}

public void OnCallServiced(Call call)
{
if(this != call.CallHandler)
{
// TODO: this call was never accepted
// somebody is innapropriately calling this method
}
else
{
Call nextCall = _callService.GetNextCall();
if(nextCall == null)
{
Busy = false;
}
}
}

protected abstract bool CanService(Call call);
}

class Fresher:Employee
{
public Fresher():base(ERank.FRESHER){}
private override bool CanService(Call call){ /*...*/ }
}

class TechLead:Employee
{
public TechLead():base(ERank.TECH_LEAD){}
private override bool CanService(Call call){ /*...*/ }
}

class ProductManager:Employee
{
public ProductManager():base(ERank.PROD_MANAGER){}
private override bool CanService(Call call){ /*...*/ }
}

class Call
{
public Employee CallHandler{get; private set;}
public ERank AcceptableRank{get; private set;}

public Call()
{
CallHandler = null;
AcceptableRank = ERank.FRESHER;
}

public void Service(Employee callHandler)
{
CallHandler = callHandler;

// notify the call handler when the call is serviced
CallHandler.OnCallServiced(this);
}

public void Escalate()
{
if((in)AcceptableRank < (int)ERank.PROD_MANAGER)
{
AcceptableRank = ERank(((int)AcceptableRank)+1);
}
}
}

class CallCenter
{
private Employee[][] _employees;
private Queue<Call>[] _rankedCallQueue;

public CallCenter(int numEmployees)
{
_prodManager = new ProductManager();
_techLead = new TechLead();

_rankedCallQueue = new Queue<Call>[((int)ERank.PROD_MANAGER)+1];
_employees = new Employee[((int)ERank.PROD_MANAGER)+1][];

for(int rank = 0; rank <= (int)ERank.PROD_MANAGER; rank++)
{
_rankedCallQueue[rank] = new Queue<Call>();
switch((ERank)rank)
{
case ERank.Fresher:
_employees[rank] = new Employee[numEmployees];
for(int i = 0; i < numEmployees; i++)
{
_employees[rank][i] = new Fresher();
}
break;
case ERank.TECH_LEAD:
_emlpoyees[rank] = new Employee[]{new TechLead()};
break;
case ERank.PROD_MANAGER:
_employees[rank] = new Employee[]{new ProductManager()};
break;
default:
break;
}
}
}

public Employee GetCallHandler(Call call)
{
int rank = (int)call.AcceptableRank;
for(int i = 0; i < _employees[rank].Length; i++)
{
if(!_employees[rank][i].Busy)
{
return _employees[rank][i];
}
}
return null;
}

public void AcceptCall(Call call)
{
Employee employe = GetCallHandler(call);
if(employee == null)
{
_rankedQueue[(int)call.AcceptableRank].Enqueue(call);
}
else
{
employee.ServiceCall(call);
}
}

public Call GetNextCall(ERank rank)
{
if(_callQueue[(int)rank].Count == 0)
{
return null;
}
else
{
return _callQueue[(int)rank].Dequeue();
}
}
}

0
ответ дан 11 февраля 2011 в 01:02 Источник Поделиться