首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >面向对象的呼叫中心设计

面向对象的呼叫中心设计
EN

Code Review用户
提问于 2017-09-06 10:55:47
回答 2查看 5.2K关注 0票数 3

描述:

下面是面试练习问题之一。我认为这样的问题是为了测试候选人编写面向对象代码的能力。我现在是一个候选人,我不知道什么是我应该坚持的要点,而白色的登机问题就像这样。我试着把我的思维过程写下来作为评论。

目标:

目标是模拟一个成本中心。

代码语言:javascript
复制
import java.util.List;
import java.util.ArrayList;

/*

Problem statement

Design a call center such that:

1. There are three kinds of employees: Respondants, Managers and Directors
2. When a call comes then it is allocated to the Respondants first
3. If no Respondant is free or not able to handle it then its escalated to Managers.
4. If no Manager is free or not able to handle it then its escalated to Directors.

*/

/*
  My thought process

  Discovering core objects
  -------------------------
  Employee (Respondant, Manager, Director), Call, Caller, CallManager

  Discovering relationship
  -------------------------
  CallManager has many employees
  There can be only one call per Employee
  There can be only one caller per caller
  An Employee can manage only one caller at a time

  Go for most basic design i.e. CallManager managine everything
*/

public class Main {
  public static void main(String[] args) {
    System.out.println("Hello World");
    CallManager manager = new CallManager();

    manager.addRespondant(new Employee("R1", manager));
    manager.addRespondant(new Employee("R2", manager));
    manager.addRespondant(new Employee("R3", manager));

    manager.addManager(new Employee("M1", manager));
    manager.addManager(new Employee("M2", manager));
    manager.addManager(new Employee("M3", manager));

    manager.addDirector(new Employee("D1", manager));
    manager.addDirector(new Employee("D2", manager));

    Caller foo = new Caller("foo");

    manager.dispatch(foo); // should be R1
    manager.dispatch(new Call()); // should be R2
    manager.dispatch(new Call()); // should be R3
    manager.dispatch(new Call()); // should be M1
  }
}

class CallManager {
  List<List<Employee>> employeeLevels;
  List<List<Call>> waitQueue;

  CallManager() {
    this.employeeLevels = new ArrayList<>();
    this.employeeLevels.add(new ArrayList<>());
    this.employeeLevels.add(new ArrayList<>());
    this.employeeLevels.add(new ArrayList<>());
    this.waitQueue = new ArrayList<List<Call>>();
  }

  private Employee getCallHandler(Call call) {
    /* check all respondants */
    for (Employee respondant: employeeLevels.get(0)) {
      if (respondant.isFree()) return respondant;
    }

    /* check all managers */
    for (Employee manager: employeeLevels.get(1)) {
      if (manager.isFree()) return manager;
    }

    /* check all directors */
    for (Employee director: employeeLevels.get(2)) {
      if (director.isFree()) return director;
    }

    // No one is free
    return null; // returning null is not a good idea
  }

  public void addRespondant(Employee emp) {
    employeeLevels.get(0).add(emp);
  }

  public void addManager(Employee emp) {
    employeeLevels.get(1).add(emp);
  }

  public void addDirector(Employee emp) {
    employeeLevels.get(2).add(emp);
  }

  public void dispatch(Caller caller) {
    dispatch(new Call(caller));
  }

  public void dispatch(Call call) {
    /* check if any respondant is free */
    Employee handler = getCallHandler(call);
    if (handler == null) {
      System.out.println("Sorry, the line is busy, your call is going in wait queue");
      putCallInWaitQueue(call);
      return;
    }
    handler.assignCall(call);
    call.setEmployee(handler);
  }

  public void putCallInWaitQueue(Call call) {
    waitQueue.get(call.getRank()).add(call);
  }
}


class Employee {
  private String name;
  private Call currentCall;
  private CallManager callManager;

  Employee(String name, CallManager callManager) {
    this.name = name;
    this.callManager = callManager; // this is the required depedency hence must be there in constructor
  }

  public boolean isFree() {
    return this.currentCall == null;
  }

  private void escalateCall() {
    if (!isFree()) {
      currentCall.incrementRank();
      callManager.putCallInWaitQueue(currentCall);
    }
  }

  public void assignCall(Call call) {
    System.out.println(name + " Received call!");
    currentCall = call;
  }
}

final class Call { // final by default
  private int rank;
  private Caller caller;
  private Employee employee;

  Call() {
    this.rank = 0;
  }

  Call(Caller caller) {
    super();
    this.caller = caller;
  }

  public int getRank() {
    return rank;
  }

  public void incrementRank() {
    this.rank += 1;
  }

  public void setCaller(Caller caller) {
    this.caller = caller;
  }

  public void setEmployee(Employee employee) {
    this.employee = employee;
  }
}

final class Caller {
  private String name;

  Caller(String name) {
    this.name = name;
  }
}

问题:

除了评论目前的设计之外,我还想就如何找到要建模的类和它们的行为提出一些建议。

EN

回答 2

Code Review用户

发布于 2017-09-07 17:25:41

谢谢你分享你的代码,它看起来不错,但我认为我们可以做一些改进。

目前,getCallHandler(Call call)方法实际上并没有使用call!我们可以删除它,所以我们最终得到了getCallHandler()。然后,当我们稍后调用它时,我们不需要传入一个Call对象。

您说返回null是个坏主意。我同意您的意见,并建议您使用Optional。这是在Java 8中引入的,其目的是在实际返回值可能不存在时充当返回值。对我来说,你的例子看起来像一个很好的用例!

如果我们使用可选选项,让我们看看您的方法是什么样子的。

代码语言:javascript
复制
private Optional<Employee> getCallHandler() {
    /* check all respondants */
        for (Employee respondant: employeeLevels.get(0)) {
            if (respondant.isFree()) return Optional.of(respondant);
        }

    /* check all managers */
        for (Employee manager: employeeLevels.get(1)) {
            if (manager.isFree()) return Optional.of(manager);
        }

    /* check all directors */
        for (Employee director: employeeLevels.get(2)) {
            if (director.isFree()) return Optional.of(director);
        }

        // No one is free
        return Optional.empty(); // returning null is not a good idea - I agree :)
    }

然而,这确实意味着我们现在需要更改调用代码。

代码语言:javascript
复制
public void dispatch(Call call) {
/* check if any respondant is free */
    Optional<Employee> handler = getCallHandler();
    if (!handler.isPresent()) {
        System.out.println("Sorry, the line is busy, your call is going in wait queue");
        putCallInWaitQueue(call);
        return;
    }
    handler.get().assignCall(call);
    call.setEmployee(handler.get());
}

现在,我们不需要担心传递null来表示“无值”。

  1. 有三种类型的雇员:响应者,经理和董事

对我来说,这是一个尖叫,他们想让你以某种形式使用继承。

在当前代码中,您有一个Employee类,在3个不同的列表中维护不同级别的Employee,然后通过索引、0、1和2跟踪它们。我认为这里可以使Employee成为一个抽象基类,并创建3个具体的实现。这些将是RespondantManagerDirector

这些看起来就像

代码语言:javascript
复制
class Respondant extends Employee {

    Respondant(String name, CallManager callManager) {
        super(name, callManager);
        this.priority = 1;
    }
}

class Manager extends Employee {

    Manager(String name, CallManager callManager) {
        super(name, callManager);
        this.priority = 2;
    }
}

class Director extends Employee {

    Director(String name, CallManager callManager) {
        super(name, callManager);
        this.priority = 3;
    }
}

这些都是非常相似的,这里唯一不同的地方是优先级。我把它们设置成与问题描述相匹配。目前,他们都是赤裸裸的,但你可以继续添加不同的方式,他们可能会回应电话。甚至只是不同的打印声明。

现在我们有了这3个子类,我们不需要3个列表,我们需要1个。让我们更新一下。

代码语言:javascript
复制
 CallManager() {
        this.employees = new ArrayList<>();
        this.waitQueue = new ArrayList<>();
    }

现在我们把所有的Employees都放在那里。

目前,我们有3种不同的增加员工的方法,同样,我们只需要1。

代码语言:javascript
复制
public void addEmployee(Employee emp) {
    employees.add(emp);
}

现在,我们有3个子类,这很好,但是我们需要能够比较它们,我们需要为员工添加一种方法来赋予它的优先级,所以我们只需要使用getter。(我们不需要一个策划人)

代码语言:javascript
复制
class Employee {
    ...
    protected int priority;
    ...
    public int getPriority() {
        return priority;
    }
}

好的,现在我们有3个子类,可以得到它们的优先级,所以我们现在要做的就是对列表进行排序,然后删除所有不自由的子类。那么,在0位置,我们将始终把Employee放在最高优先级!

代码语言:javascript
复制
private Optional<Employee> getCallHandler() {
        List<Employee> activeEmployees = employees.stream()
                .filter(Employee::isFree)
                .sorted(Comparator.comparingInt(Employee::getPriority))
                .collect(Collectors.toList());

        if(!activeEmployees.isEmpty()){
            return Optional.of(activeEmployees.get(0)); // the sorted list, putting the top priority at position 0.
        }
        return Optional.empty(); // returning null is not a good idea
    }

如果您不习惯使用Java,这个更改可能看起来有点复杂,但实际上我们在这里所做的一切就是获取就业列表的流、基于isFree方法进行筛选,并根据getPriority值进行排序。

因此,现在我们已经删除了一些不需要的方法,其形式是addX addY。我们现在只处理一个列表,而不是3个列表。这是开放的修改,因为我们可以添加任何其他雇员类型在未来和这个代码将仍然工作!

有了这段新代码,这就是您的主要方法的样子。

代码语言:javascript
复制
public class Main {
    public static void main(String[] args) {
        System.out.println("Hello World");
        CallManager manager = new CallManager();

        manager.addEmployee(new Respondant("R1", manager));
        manager.addEmployee(new Respondant("R2", manager));
        manager.addEmployee(new Respondant("R3", manager));

        manager.addEmployee(new Manager("M1", manager));
        manager.addEmployee(new Manager("M2", manager));
        manager.addEmployee(new Manager("M3", manager));

        manager.addEmployee(new Director("D1", manager));
        manager.addEmployee(new Director("D2", manager));

        Caller foo = new Caller("foo");

        manager.dispatch(foo); // should be R1
        manager.dispatch(new Call()); // should be R2
        manager.dispatch(new Call()); // should be R3
        manager.dispatch(new Call()); // should be M1
    }
}

输出是

代码语言:javascript
复制
Hello World
R1 Received call!
R2 Received call!
R3 Received call!
M1 Received call!

希望这篇评论对你有帮助!

票数 5
EN

Code Review用户

发布于 2018-06-05 05:22:20

代码语言:javascript
复制
 List<Employee> activeEmployees = employees.stream()
                .filter(Employee::isFree)
                .sorted(Comparator.comparingInt(Employee::getPriority))
                .collect(Collectors.toList());

为什么不能使用findAny与并行流?,而不是收集列表?

票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/174942

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档