Call center escalation excercise, using Chain of Responsibility pattern This is a coding interview I found online, to be solved within 30 minutes:


  Design an OOP concept for an application where employee can dispatch their incoming phone call according to their seniority level if they are not able to solve.


I chose to use Chain of Responsibility pattern, I also used Null pattern.
Please review OOP principles and the specific design pattern implementation.

The unit test is just a code sample.

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace DesignPatternsQuestions
{
    [TestClass]
    public class ChainOfResponsibilityTest
    {
        [TestMethod]
        public void PhoneCallDispatchCORTest()
        {
            PhoneCallHandler gilad = new PhoneCallHandler(new LowLevel("Gilad"));
            PhoneCallHandler bonno = new PhoneCallHandler(new LowLevel("Bonno"));
            PhoneCallHandler batel = new PhoneCallHandler(new MediumLevel("Batel"));
            PhoneCallHandler daniel = new PhoneCallHandler(new HighLevel("Daniel"));
            gilad.RegisterNext(batel);
            bonno.RegisterNext(batel);
            batel.RegisterNext(daniel);

            BankPhoneCall call1 = new BankPhoneCall(1500);
            bool res = gilad.GetPhoneCall(call1);
            Assert.IsTrue(res);
        }
    }

    public interface IPhoneCall
    {
        int Budget { get; set; }
    }

    public class BankPhoneCall :IPhoneCall
    {

        public BankPhoneCall(int budget)
        {
            Budget = budget;
        }

        public int Budget { get; set; }
    }

    public interface IPhoneCallHandler
    {
        bool GetPhoneCall(IPhoneCall phoneCall);
        //the same item is able to call next Item in the chain of command
        void RegisterNext(IPhoneCallHandler employee);
    }

    public class PhoneCallHandler : IPhoneCallHandler
    {
        private IPhoneCallHandler _nextCallHandler = EndOfChainHandler.Instance;
        private Employee _employee;

        public PhoneCallHandler(Employee employee)
        {
            _employee = employee;
        }
        public bool GetPhoneCall(IPhoneCall phoneCall)
        {
            bool res = _employee.Resolve(phoneCall);
            if (!res)
            {
                //handle _nextcallHandler == null with null pattern
                return _nextCallHandler.GetPhoneCall(phoneCall);
            }
            return res;
        }

        public void RegisterNext(IPhoneCallHandler nextCallHandler)
        {
            _nextCallHandler = nextCallHandler;
        }
    }


    /// &lt;summary&gt;
    /// null pattern
    /// &lt;/summary&gt;
    public sealed class EndOfChainHandler : IPhoneCallHandler
    {
        private static readonly Lazy&lt;EndOfChainHandler&gt; lazy = new Lazy&lt;EndOfChainHandler&gt;(()=&gt; new EndOfChainHandler());
        public static EndOfChainHandler Instance { get { return lazy.Value; } }
        public bool GetPhoneCall(IPhoneCall phoneCall)
        {
            return false;
        }

        public void RegisterNext(IPhoneCallHandler employee)
        {
            throw  new InvalidOperationException("can't register next to null");
        }
    }

    public abstract class Employee
    {
        protected int _maxBudget;

        public string Name { get;set; }
        public Employee(string name, int maxBudget)
        {
            _maxBudget = maxBudget;
            Name = name;
        }

        public bool Resolve(IPhoneCall phoneCall)
        {
            return phoneCall.Budget &lt;= _maxBudget;
        }
    }
    public class LowLevel : Employee
    {
        public LowLevel(string name)
            : base(name, 1000)
        {
        }
    }

    public class MediumLevel : Employee
    {
        public MediumLevel(string name)
            : base(name, 5000)
        {
        }
    }

    public class HighLevel : Employee
    {
        public HighLevel(string name)
            : base(name, 10000)
        {
        }
    }
}
Review

Well done, there isn't much I would change in this implementation. I would grant you the humble badge for putting yourself as LowLevel employee :) 

The lazy and null pattern are well implemented (except a small issue that could possibly introduce null -> see minor issues). The chain of responsibility looks to be by the book. You've even used a visitor pattern between the handler and the employee. Each specific employee yields different behavior handling a phone call.

Minor issues


IPhoneCall as interface seems overkill to me. This is just a POCO.
Check arguments against null in public entry points such as public PhoneCallHandler(Employee employee).
GetPhoneCall could be written more compact: return _employee.Resolve(phoneCall) || _nextCallHandler.GetPhoneCall(phoneCall);.
RegisterNext should not allow null, as this bypasses your EndOfChainHandler.Instance pattern to avoid null handling.