Callcenter

Опубликовано Sep 30, 2011 в ООП | 6 коммент.

, ,

Callcenter

Некоторая компания занимается предоставлением услуг Callcenter. Есть три уровня иерархии операторов: оператор (O), руководители групп (TM) и менеджеры (M).
При попадании звонка в CallCenter – система должна выбрать первого свободного оператора, если все операторы заняты – то первого свободного руководителя групп, если же все руководители групп заняты – то первого свободного менеджера.
Необходимо разработать набор классов и реализовать функцию getCallHandler().


Первое приемлемое решение появилось. Будем считать, что его внедрили и наш Call Center заработал.  И что из этого вышло? Оказалось, что некоторые из операторов загружены работой существенно сильнее, чем другие. Это привело к нездоровой атмосфере в коллективе заказчика и он выставил требование разобраться в ситуации и устранить проблему.

Действительно, оказалось, что когда сотрудник заступает на смену и регистрируется в системе, его позиция в списке определяется его Id, который жестко привязан к идентификатору записи о сотруднике в базе данных. В результате чем меньше Id сотрудника, тем чаще на него переводят звонок. Предположим, что изменить функциональность регистрации оператора в системе при начале его смены не возможно.

Вопрос. Что можно изменить в коде классов CallCenter и Operator, чтобы решить описанную проблему?


А теперь


6 Коммент. : “Callcenter”

  1. Евгений says:

    Что-то тихо здесь… Имеет смысл бросать сюда исходники?

    • Галина says:

      Тихо, да. У нас стали теряться письма, уведомляющие о комментариях. Проблему обнаружили с опозданием, но сейчас ею занимаемся.
      По поводу исходников – вам решать, обычно мы всегда отвечаем по делу.

      • Евгений says:

        Вот класс Operator:

        class Operator
        {
                int Id;
                int rank;
                bool isFree;
         
                public Operator(int Id, int rank, bool isFree)
                {
                    this.Id = Id;
                    this.rank = rank;
                    this.isFree = isFree;
                }
                public bool GetStatus()
                {
                    return isFree;
                }
                public void SetStatus(bool setStatus)
                {
                    isFree = setStatus;
                }
                public int GetRank()
                {
                    return rank;
                }
                public void Info()
                {
                    Console.WriteLine("ID - {0}, qualification - {1}, free - {2}.", Id, GetQualification(), isFree);            
                }        
                public void Call()
                {
                    Console.ForegroundColor = ConsoleColor.Green;
                    isFree = false;
         
                    Console.WriteLine("Hello! I’m {0} {1}.", GetQualification(), Id );
                    bool call = true;
                    while (call)
                    {
                        Console.WriteLine("To end call press any key...");
                        Console.ReadKey();
                        call = false;
                        Console.WriteLine("{0} {1} ended a call.", GetQualification(), Id);
                        isFree = true;
                    }
                    Console.ForegroundColor = ConsoleColor.Cyan;
         
                }
                string GetQualification()
                {
                    switch (rank)
                    {
                        case 0: { return "operator"; }
                        case 1: { return "team manager"; }
                        case 2: { return "manager"; }
                    }
                    throw new Exception("Unknown rank"); // Should never get here
                }
        }

        Класс CallCenter:

        class CallCenter
        {
                int nextOperatorId = 0;
                List<Operator> _operators = new List<Operator>();
         
                public void Start()
                {
                    // Operating mode selection.
                    Console.WriteLine("********************************");
                    Console.WriteLine("Select one of following actions.");
                    Console.WriteLine("1 - add a new operator");
                    Console.WriteLine("2 - call a free operator");
                    Console.WriteLine("3 - view a list of operators");
                    Console.WriteLine("4 - clear a list of operators");
                    Console.WriteLine("================================");
                    int mode;
                    //Input validation.
                    if (!Int32.TryParse(Console.ReadLine(), out mode) | mode > 4 | mode < 0)    
                    {
                        Console.WriteLine("Please input one of digits 1, 2, 3 or 4!");
                    }
                    switch (mode)
                    {
                        case 1: AddOperator(); break;
                        case 2: SeachFreeOperator(); break;
                        case 3: ViewOperators(); break;
                        case 4: ClearOperators(); break;
                    }
                }
                public void AddOperator()
                {
                    //Inputting status of operator.
                    bool status = true;
                    Console.WriteLine("Input 1 for setting status of the operator as busy (default - free)");
                    Console.WriteLine("To skip - press Enter");
                    int st;
                    if (!Int32.TryParse(Console.ReadLine(), out st) | st !=1 )
                    {
                        Console.WriteLine("Status of operator was set as free!");
                    }
                    if (st == 1) { status = false; Console.WriteLine("Status of operator was set as busy!"); }
         
                    //Inputting qualification of operator.
                    Console.WriteLine("Input rank of operator: 0 - operator; 1 - meneger; 2 - senior");
                    int setRank = 0;
                    bool repeatInput = true;
                    //Input validation.
                    while (repeatInput)
                    {
                        if (Int32.TryParse(Console.ReadLine(), out setRank) && setRank < 3 && setRank >= 0)
                        {
                            repeatInput = false;
                        }
                        else { Console.WriteLine("Please input correct number(0 - operator; 1 - team manager; 2 - manager)"); }
                    }
                    //Sending data about new operator to constructor and addition new operator to list.
                    _operators.Add(new Operator(nextOperatorId, setRank, status));
                    nextOperatorId++;
                }
                public void SeachFreeOperator()
                {
                    if (_operators.Count == 0) 
                    {
                        Console.ForegroundColor = ConsoleColor.Red;
                        Console.WriteLine("Sorry, the list of operators is empty...");
                        Console.ForegroundColor = ConsoleColor.Cyan;
                        return;
                    }
         
                    //A search of the free operator with the lowest rank.
                    int indexFreeOperator = -1;
                    int rankOperator = 3;
                    for (int i = 0; i < _operators.Count; i++)
                    {
                        // Search a free operator, if any, we save his rank and position in list. 
                        // Next if rank of the next free operator is lower rank of previous free operator
                        // we save the position in list and rank of the next operator.
                        if (_operators[i].GetStatus() && _operators[i].GetRank() < rankOperator)
                        {
                            // In case when was detected free operator with the lowest rank(0 - operator) we will make call.
                            if (_operators[i].GetRank() == 0) { _operators[i].Call(); return; }
                            indexFreeOperator = i;
                            rankOperator = _operators[i].GetRank();
                        }
                    }
                    // Making a call.
                    if (indexFreeOperator >= 0) { _operators[indexFreeOperator].Call(); }
                    else
                    {
                        Console.ForegroundColor = ConsoleColor.Red;
                        Console.WriteLine("Sorry! All operators are busy. Try again later.");
                        Console.ForegroundColor = ConsoleColor.Cyan;
                    }
         
                }
                public void ViewOperators()
                {
                    if (_operators.Count == 0)
                    {
                        Console.ForegroundColor = ConsoleColor.Red;
                        Console.WriteLine("Sorry, the list of operators is empty...");
                        Console.ForegroundColor = ConsoleColor.Cyan;
                        return;
                    }
                    foreach (Operator Op in _operators)
                    {
                        if (Op.GetStatus())
                        {
                            Console.ForegroundColor = ConsoleColor.Yellow;
                            Op.Info();
                        }
                        else 
                        {
                            Console.ForegroundColor = ConsoleColor.DarkYellow;
                            Op.Info();
                        }
                    }
                    Console.ForegroundColor = ConsoleColor.Cyan;
                }
                public void ClearOperators()
                {
                    _operators.Clear();
                    Console.WriteLine("All operators were deleted.");
                }
         
        }

        Жду ответа :-)

        • Галина says:

          Спасибо за решение. Думаю, что завтра-послезавтра проверю подробнее и отвечу по существу.

        • Галина says:

          Вот первые комментарии. В том смысле, что дальше я со временем создам пост с примерами кода из замечаний.

          Классы в целом спроектированы приемлемым образом, и требование условия достигнуто. Дальнейшие замечания я сделала, исходя из того, на что бы обращалось внимание, если эту реализацию рассматривать как тестовое задание на позицию Junior Net Developer.

          Ввод-вывод описывать в подобных задачах не нужно. И вот почему. Абсолютно неизвестно, как будут использоваться созданные классы. Консольного приложения, скорее всего, не будет и куда девать в этом случае все из Console? Если же это тестовое задание, то, как раз время и убито на написание витиеватого ввода-вывода. Речь в подобных заданиях идет о написании классов уровня бизнес-логики, а о вводе выводе ничего не сказано и его быть не должно.

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

          Итак, интерес представляют классы CallCenter и Operator. В классе Operator основное место – метод Call. Задача этого метода изменить поле isFree на false. И это все, что еще в нем делать в условии (спецификации) не сказано. В классе CallCenter интересен только метод SeachFreeOperator (соответствует getCallHandler() из условия). Метод SeachFreeOperator (getCallHandler()) должен возвращать ID оператора (oн начинался с get…, значит должен что-то вернуть), это значение и будет в простейшем случае тем, что в условии названо CallHandler.

          Логика поиска подходящего оператора в методе SeachFreeOperator – лобовая, то есть просто написан алгоритм поиска, подходящий под условие. Вполне приемлемо, но тут как раз и могут начаться вопросы, связанные с выяснением уровня владения языком. Ведь поиск в массиве (списке) реализован в C# разнообразными готовыми средствами. Удастся ли переписать этот алгоритм, продемонстрировав их использование?

          Речь идет, например, об этом.

  2. Евгений says:

    За мишуру – простите :-)
    Я тут почистил классы. И поиск исправил (реализован средствами С#).

    class Operator
        {
            int Id;
            int rank;
            bool isFree;
     
            public Operator(int Id, int rank, bool isFree)
            {
                this.Id = Id;
                this.rank = rank;
                this.isFree = isFree;
            }
     
            public int GetID()
            {
                return this.Id;
            }
            public bool GetStatus()
            {
                return isFree;
            }
            public int GetRank()
            {
                return rank;
            }
     
            public void Call()
            {
                this.isFree = false;           
            }       
        }
     
    	class CallCenter
        {
            List<Operator> _operators = new List<Operator>();
     
            public int SeachFreeOperator()
            {
                if (_operators == null) return -1;
                Operator freeOperator = _operators.Where(x => x.GetStatus()).OrderBy(x => x.GetRank()).FirstOrDefault();
                if (freeOperator == null) return -2;
                freeOperator.Call();
                return freeOperator.GetID();
            }
        }

Оставить комментарий

Ваш адрес email не будет опубликован.


*