基本套路
- 题目描述
- 往往非常简单,如:设计一个XX系统。或者:你有没有用过XXX,给你看一下它的界面和功能,你来设计一个。
- 阐述题意
- 面试者需向面试官询问系统的具体要求。如,需要什么功能,需要承受的流量大小,是否需要考虑可靠性,容错性等等。
- 面试者提供一个初步的系统设计
- 面试官这对初步的系统中提出一些后续的问题:如果要加某个功能怎么办,如果流量大了怎么办,如何考虑一致性,如果机器挂了怎么办。
- 面试者根据面试官的后续问题逐步完善系统设计
- 完成面试
总体特点是以交流为主,画图和代码为辅。
根据我们面试别人和参与面试的经验,先从面试官的角度给出一些考量标准:
- 适应变化的需求(Adapt to the changing requirements )
- 设计干净,优美,考虑周到的系统(Produce a system that is clean, elegant, well thought )
- 解释为何这么实现(Explain why you choose this implementation )
- 对自己的能力水平很熟练(Be familiar with your experience level to make decisions )
- 在一些高层结构和复杂性方面有设计(Answer in high level of scale and complexity )
按照评分体系的化,分成下面4个等级
其实大家大可不必追求完美,在真正的面试中,没有人能对答如流,往往面试官也会给出善意的提示,就算你没回答某个子问题,在面试后的评价中也会综合衡量,跟其他的面试者比较,最终打出一个分数
解题策略
Abstractions, Object and Decoupling
通常,关于OOP,面试官会让面试者设计一个程序框架,该程序能够实现一些特定的功能。比如,如何实现一个音乐播放器,如何设计一个车库管理程序等等。对于此类问题,设计的关键过程一般包括抽象(abstraction),设计对象(object)和设计合理的层次/接口(decoupling)。这里,我们举一个例子简单说明这些过程分别需要做些什么,在“模式识别”给出更为具体和完整的实例。
继承/组合/参数化类型
在面向对象中最常用的两种代码复用技术就是继承和组合。在设计对象的时候,“Is-A”表示一种继承关系。比如,班长“Is-A”学生,那么,学生就是基类,班长就是派生类。在确定了派生关系之后,我们需要分析什么是基类变量(base class variables)什么是子类变量(sub class variables),并由此确定基类和派生类之间的联系。而“Has-A”表示一种从属关系,这就是组合。比如,班长“Has-A”眼镜,那就可以解释为班长实例中拥有一个眼镜实例变量(instance variable)。在具体实现的时候,班长类中定义一个眼镜的基类指针。“在生成班长实例的时候,同时生成一个眼镜实例,利用眼镜的基类指针指向这个实例。任何关于眼镜的操作函数都可以利用这个基类指针实现多态(polymorphism)。注意,多态是OOP相关的一个重要概念,也是面试常考的概念之一。关于多态的解释请见“工具箱”。
在通常情况下,我们更偏向于“Has-A”的设计模式。因为该模式减少了两个实例之间的相关性。对于继承的使用,通常情况下我们会定义一个虚基类,由此派生出多个不同的实例类。在业界的程序开发中,多重继承并不常见,Java甚至不允许从多个父类同时继承,产生一个子类。
此外,我们还要提及参数化类型。参数化类型,或者说模版类也是一种有效的代码复用技术。在C++的标准模版库中大量应用了这种方式。例如,在定义一个List的变量时,List被另一个类型String所参数化。
设计模式着重于代码的复用,所以在选择复用技术上,有必要看看上述三种复用技术优劣。
继承
- 通过继承方式,子类能够非常方便地改写父类方法,同时
- 保留部分父类方法,可以说是能够最快速地达到代码复用。
- 继承是在静态编译时候就定义了,所以无法再运行时刻改写父类方法。
- 因为子类没有改写父类方法的话,就相当于依赖了父类这个方法的实现细节,被认为破坏封装性。<