这是我的要求(来自“破解编码面试”一书)
假设您有一个拥有三个级别员工的呼叫中心:新鲜员工、技术主管(TL)和产品经理(PM)。可以有多个员工,但只有一个TL或PM。一个来电必须分配给一个新的谁是免费的。如果一个新人不能处理这个电话,他或她必须将电话升级为技术主管。如果TL没有空闲或无法处理,则应将调用升级到PM。针对这个问题设计类和数据结构。实现一个方法
getCallHandler()。
这就是我的实现:
public interface CallAllocator {
public Employee getCallHandler() throws NoEmployeeInTheHouseException;
void setTL(TechnicalLead technicalLead);
void setPM(ProductManager productManager);
void addFresher(Fresher fresher);
}接口的实现:
public class CallAllocatorImpl implements CallAllocator {
private TechnicalLead technicalLead;
private ProductManager productManager;
private List<Fresher> freshers = new ArrayList<Fresher>();
@Override
public Employee getCallHandler() throws NoEmployeeInTheHouseException {
if (freshers.isEmpty() && technicalLead == null && productManager == null) {
throw new NoEmployeeInTheHouseException();
}
if (!freshers.isEmpty()) {
Employee fresher = freshers.get(new Random().nextInt(freshers.size()));
if (fresher.getCanHandle()) {
return fresher;
}
}
if (technicalLead != null && technicalLead.getCanHandle()) {
return technicalLead;
}
if (productManager != null && productManager.getCanHandle()) {
return productManager;
}
throw new NoEmployeeInTheHouseException();
}
@Override
public void setTL(TechnicalLead technicalLead) {
this.technicalLead = technicalLead;
}
@Override
public void setPM(ProductManager productManager) {
this.productManager = productManager;
}
@Override
public void addFresher(Fresher fresher) {
if (fresher.isFree()) {
freshers.add(fresher);
}
}
}雇员班:
public class Employee {
private boolean free;
private boolean canHandle;
public boolean isFree() {
return free;
}
public void setFree(boolean free) {
this.free = free;
}
public boolean getCanHandle() {
return canHandle;
}
public void setCanHandle(boolean canHandle) {
this.canHandle = canHandle;
}
}我有三个类的名字:新鲜,TechnicalLead和ProductManager。它们都扩展了员工,但不覆盖任何方法或任何东西。
这是我的TestClass:
public class TestClass {
public static void main(String[] args) throws NoEmployeeInTheHouseException {
CallAllocator callAllocator = new CallAllocatorImpl();
Fresher fresherOne = new Fresher();
fresherOne.setCanHandle(false);
fresherOne.setFree(true);
Fresher fresherTwo = new Fresher();
fresherTwo.setCanHandle(true);
fresherTwo.setFree(true);
Fresher fresherThree = new Fresher();
fresherThree.setCanHandle(false);
fresherThree.setFree(true);
Fresher fresherFour = new Fresher();
fresherFour.setCanHandle(false);
fresherFour.setFree(false);
callAllocator.addFresher(fresherOne);
callAllocator.addFresher(fresherTwo);
callAllocator.addFresher(fresherThree);
callAllocator.addFresher(fresherFour);
TechnicalLead technicalLead = new TechnicalLead();
technicalLead.setCanHandle(false);
technicalLead.setFree(true);
callAllocator.setTL(technicalLead);
ProductManager productManager = new ProductManager();
productManager.setCanHandle(true);
productManager.setFree(true);
callAllocator.setPM(productManager);
Employee callHandler = callAllocator.getCallHandler();
System.out.println(callHandler.getClass().getSimpleName());
}
}那么我如何改进这段代码呢?有什么建议吗?
发布于 2014-02-28 19:30:23
首先,我不确定您是否真正实现了所编写的需求。描述说:
来电必须分配给一个新的谁是自由的。如果一个新人不能处理这个电话,他或她必须升级为技术领导。
这听起来就像如果没有免费的新生,就根本不应该处理调用(抛出的异常?),而不是跳到TL。这种方式作为现实世界的要求是有意义的:如果没有新生,打电话的人可能需要稍晚一点再打电话,而不是浪费技术上的领导时间,让一个新人能够处理。例如,无论调用什么,都可以计划捕获异常并执行addCallToUnhandledCallQueue()或其他任何操作。
这也更多地涉及到这个问题的实质,我相信这是试图让你用责任链的模式来回答。在这种模式中,负责处理命令(在本例中是调用)的每个对象(在本例中是Employee)都包含检查它是否能够处理给定命令的逻辑,如果不能,也知道链中要调用的下一个对象。
这种模式的一个好处是坚持开放/封闭原则。正如您将在下面看到的,做一些事情,比如添加一个新的雇员类型或者稍微改变一下结构,不太可能需要您在getCallHandler()中摆弄if{...} else{...}逻辑。此外,这意味着Employees只需要知道他们的顶头上司,而不是一些大师必须知道并坚持整个员工结构(这很快就会变得不愉快,特别是如果您需要添加其他方法,这些方法也需要了解这种结构)。
一个重要的好处是,考虑到这显然是一个面试问题,如果有人在面试中问我这个问题,我很确定他们会希望我谈论这个模式,所以即使出于任何原因,你最终决定有一个更好的解决方案,理解这个问题也很重要,如果能够明智地描述你拒绝它的原因。
因此,使用此模式,您的getCallHandler(Call call)方法将如下所示:
Employee fresher = getAnyFreeFresher(); //Should throw if there are none
return fresher.handle(call);然后,Employee类看起来如下所示:
public class Employee{
private Employee boss;
private bool canHandle(Call call){
//...
}
public Employee handle(Call call){
if(canHandle(call)){
return this;
}
if(boss == null){
//Nobody in the chain could handle, throw
}
return boss.handle(call);
}
}这是一个非常粗略的大纲,需要详细说明老板是如何设置的,您可能希望员工类型从Employee继承来实现canHandle等等。
发布于 2014-02-28 19:15:30
总的来说,代码很容易读懂。
一些小的挑剔的东西。
不要使用booleans,而要使用enums。
即
public enum EmployeeStatus {
OnCall,
Available
}这允许您在需求更改时添加更多的状态:
public enum EmployeeStatus {
OnCall,
Available,
OutToLunch,
OnVacation,
OnBreak
}我不喜欢getCallHandler()可以填充新生。我会注射这个列表,或者是一家能做到这一点的工厂。这将使代码解耦,并允许将来进行更容易的单元测试。
我也不喜欢这套方法。使用继承应该可以消除这方面的需求。诚然,我目前还没有找到解决办法,但有一个办法。基本上,通过使用不同的方法来处理不同的实例,您将自己绑定到这三种类型中。如果添加了第四种类型,比如ProductExpert,会发生什么?现在您必须更改这个类来处理它。
canHandle方法似乎应该是基于状态枚举的计算。这样,您只需在类中设置一个标志,而不是两个。
public class Employee {
// code
public boolean isFree() {
return status == EmployeeStatus.Available;
}
// code
}
Fresher fresherTwo = new Fresher();
fresherTwo.setStatus(EmployeeStatus.Available);我也不喜欢‘`NoEmployeeInTheHouseException’这个名字,我觉得它有点太因果了。我会做一些'NoEmployeeAvailableException‘之类的事情。这让人觉得更有公事性。
这是一个很好的开始,我很高兴你关心这件事。继续努力吧。
发布于 2014-03-01 08:58:16
首先我将评论你的执行情况,然后我将在最后提出我的版本。
从上到下,我们开始了:
public interface CallAllocator {
public Employee getCallHandler() throws NoEmployeeInTheHouseException;
void setTL(TechnicalLead technicalLead);
void setPM(ProductManager productManager);
void addFresher(Fresher fresher);
}ICallHandler。在讨论处理调用时,实际的employee类是不相关的,它们不应该是模型设计的一部分。setTL、setPM、addFresher方法都使用过于特定于实现的术语和参数。接口定义应该尽可能抽象。下一步:
if (freshers.isEmpty() && technicalLead == null && productManager == null) {
throw new NoEmployeeInTheHouseException();
}我看到在您的实现中,您将新生/领导/管理器的可用性处理为空或空。这是状态管理,而不是建模。最好是使用显式接口方法来捕捉模型是否可用的概念。
if (!freshers.isEmpty()) {
Employee fresher = freshers.get(new Random().nextInt(freshers.size()));
if (fresher.getCanHandle()) {
return fresher;
}
}getCanHandle真的很尴尬,canHandle会更自然一些好的,下面是我的解决方案,可以非常准确地对描述建模:
interface ITicket {}
interface ICallHandler {
boolean isAvailable();
boolean canHandle(ITicket ticket);
}
interface ICallHandlerPicker {
ICallHandler getAvailableCallHandler();
}
interface ICallCenter {
ICallHandler getCallHandler(ITicket ticket);
}
class SingleLeadSingleManagerCallCenter implements ICallCenter {
private final ICallHandlerPicker picker;
private final ICallHandler lead;
private final ICallHandler manager;
SingleLeadSingleManagerCallCenter(ICallHandlerPicker picker, ICallHandler lead, ICallHandler manager) {
this.picker = picker;
this.lead = lead;
this.manager = manager;
}
@Override
public ICallHandler getCallHandler(ITicket ticket) {
ICallHandler handler = picker.getAvailableCallHandler();
if (handler == null) {
// nobody available. perhaps throw new NoSuchElementException() ?
return null;
}
if (handler.canHandle(ticket)) {
return handler;
}
if (lead.isAvailable() && lead.canHandle(ticket)) {
return lead;
}
return manager;
}
}这将附着在描述的定义良好的部分上,并使未定义的部分不被故意实现,例如:
其他要注意的事项:
当然,这是不可伸缩的。描述本身通过指定单个技术主管和单个产品经理排除了可伸缩性。我会通过将多个级别建模为呼叫中心链来解决这个问题:
class MultiLevelCallCenter implements ICallCenter {
private final ICallHandlerPicker picker;
private final ICallCenter nextCallCenter;
MultiLevelCallCenter(ICallHandlerPicker picker, ICallCenter nextCallCenter) {
this.picker = picker;
this.nextCallCenter = nextCallCenter;
}
@Override
public ICallHandler getCallHandler(ITicket ticket) {
ICallHandler handler = picker.getAvailableCallHandler();
if (handler == null) {
// nobody available. perhaps throw new NoSuchElementException() ?
return null;
}
if (handler.canHandle(ticket)) {
return handler;
}
return nextCallCenter.getCallHandler(ticket);
}
}
class UltimateCallCenter implements ICallCenter {
private final ICallHandler handler;
UltimateCallCenter(ICallHandler handler) {
this.handler = handler;
}
@Override
public ICallHandler getCallHandler(ITicket ticket) {
return handler.isAvailable() ? handler : null;
}
}然后,我们可以将这些更可伸缩的类描述中的呼叫中心实现为:
ICallCenter getSingleLeadSingleManagerCallCenter(ICallHandlerPicker picker, final ICallHandler lead, ICallHandler manager) {
ICallCenter managerCallCenter = new UltimateCallCenter(manager);
ICallCenter leadCallCenter = new MultiLevelCallCenter(new ICallHandlerPicker() {
@Override
public ICallHandler getAvailableCallHandler() {
return lead.isAvailable() ? lead : null;
}
}, managerCallCenter);
return new MultiLevelCallCenter(picker, leadCallCenter);
}再一次,我忽略了新生的细节,以及我们如何挑选他们。这些都没有在描述中指定,也没有真正的相关性。这使您可以随意注入任何您喜欢的实现。
https://codereview.stackexchange.com/questions/43087
复制相似问题