OOD 和 SD的面试准备对象
Object Oriented Design | System Design |
---|---|
应届毕业生,SDEI- | 有经验的面试者,SDE1+ |
OOD常被当做考察面试者综合素质的标准 | 需要处理大量数据,提供Service的部门 |
Amazon, Uber,. | Facebook, Twitter,… |
Viability 设计的系统,支持所有功能 | Scalability 设计是否支持不同规模的访问 |
-例子- | -例子- |
Design Elevator System | Design Twitter |
面试频率
Phone interview 低
Onsite interview 中高频
OOD 面公司
Amazon, Bloomberg, TripAdvisor, EMC, Uber…
SOLID 原则
单一责任原则
三角形面积计算例子
public class AreaCalculator{
private float result;
private float getResult();
public float getResult(){
return this.result;
}
public float calculateArea(Triangle t){
this.result = h * b / 2;
}
}
增加功能让三角形 的面积 以 jason格式打印出来
违反单一原则的错误示范
public class AreaCalculator{
private float result;
private float getResult();
public float getResult(){
return this.result;
}
public float calculateArea(Triangle t){
this.result = h * b / 2;
}
public void printResultInJson(){
jasonPrinter.intialize();
jasonPrinter.print(this.result);
jasonPrinter.close();
}
}
这个类的功能 就是计算三角形面积
这里除了 计算面积
还增加了 第二个责任, 以json形式打印出来
让这个类变得臃肿了。
而且执行了许多 它本身不应该做的事情。
正确做法
分为两个类
一个计算 面积
另一个负责 将面积 以 float形式打印出来
public class AreaCalculator{
private float result;
private float getResult();
public float getResult(){
return this.result;
}
public float calculateArea(Triangle t){
this.result = h * b / 2;
}
}
public class Printer{
public printInJson(float number){
jsonPrinter.intialize();
jsonPrinter.print(this.result);
jsonPrinter.close();
}
}
Open Close Principle 开放封闭原则
对象或实体应该对扩展开放,
对修改封闭 (Open to extension, close to modification).
错误示范
public class AreaCalculator{
private float result;
private float getResult();
public float getResult(){
return this.result;
}
public float calculateArea(Triangle t){
this.result = h * b / 2;
}
public float calculateArea(Rectangle t){
this.result = h * b;
}
}
Liskov substitution principle 里氏替换原则
任何一个子类或派生类应该可以替换它们的基类或父类
Interface segregation principle 接口分离原则
不应该强迫一个类实现它用不上的接口
错误示范
public class Shape{
abstract public float calculateVolumn();
abstract public float calculateArea();
}
public class Rectangle extends Shape{
//...
}
public class Cube extends Shape{
}
三角形 继承 Shape就不合理了,因为三角形没有Volumn
但是Cude可以继承
正确修改
两个类
一个计算平面的Shape
一个计算立体的Shape
Dependency inversion principle 依赖反转原则
抽象不应该依赖于具体实现,
具体实现应该依赖于抽象 High-level的实体不应该依赖于low-level的实体
public class AreaCalculator{
private float result;
private Triangle t;
public float getResult(){
return this.result;
}
public float calculateArea(){
this.result = t.h * t.b / 2;
}
}
public interface Shape {
public float getArea();
}
public class Triangle implements Shape{
public float getArea(){
return b * h / 2;
}
}
public class AreaCalculator {
private float result;
private float getResult(){
return this.result;
}
public float calculateArea(Shape s){
this.result = s.getArea();
}
}
例子
galss of water
首先,消除 疑惑,clarify 确认我们要设计的问题
万能技巧
双W解题法
What
找题目中的关键字
什么样的杯子
什么样的水
杯子的功能
水的特点
另一个例子
针对一个buiding 设计 一个elevator
关键字 Elevator Building
elevator 有什么特点
building 有什么特点
elevator
新手问题:
电梯的载重:电梯能容纳多少人,承载多少重量
老手问题:
电梯有没有承重限制
如果有的话,如何知道当前电梯重量的限制
客梯 airstairs
货梯 goodselevator
是否需要设计两种类,如果需要,他们之间的关系是什么?
airsstairs 和 goodselevator之间称重
airstairs 和 goodselevator 能否上的层数相同
楼有多大
楼有多高
楼能容纳多少人
Buidling
是否有 多处能搭乘的电梯口?
当收到一个搭乘电梯请求时候,有多少电梯响应?
How
针对题目主题 提问,明确自己解题方向
对于电梯运行,有什么规则?
电梯有哪些规则?
如何判断电梯是否超重?
Passenger class 包含重量
电梯能否 自动感应当前重量
当按下按钮,哪一台电梯会相应?
response to the request
同方向 > 静止 > 反向
一半负责 奇数, 一半负责 偶数。
corner case
电梯运行,是否可以按 反方向的楼层?
Core Object
core object: 为了完成设计,需要定义的类
和面试官初步的纸面 contract
承上启下,来自Clarify的结果
为画类图 打下基础。
如何定义Core Object
两个方式
- 以一个Object作为基础,线性思考
- 确定Objects之间的映射关系
第一个写 的是设计的主体
展开线性思考:
输入是什么
输出是什么
输入:
按一下按钮
传入request
输出:
Elevator响应
电梯里面还有button
思考对应关系
1-1对应
1-n对应
n-n对应
request 和 elevatorSystem没有对应
request只是 发送给elevatorSystem
elevatorSystem 对应多个elevator
elevator 和 elevatorbutton对应
小结:
定义 ElevatorSystem
定义输入
定义输出
它们之间关系
UML类图标志
- 表示 access modifier
访问修饰符
package
public
private
protected
package
在同一个package下可以访问,不声明,默认package level水平。
如果什么都不声明,变量和函数都是package level visible的,在同一个package内的其他类都可以访问
在面试当中,不管写代码,还是画类图,都不要用package level
public
如果声明为public,变量和函数都是public level visible的,任何其他的类都可以访问
private
如果声明为private,变量和函数都是class level visible的,这是所有access modifier中限制最多的一个。仅有定义这些变量和函数的类自己可以访问。
OOD当中 实现封装(encapsulation)的重要手段。
protected
如果声明为protected,变量和函数在能被定义他们的类访问的基础上,还能够被该类的子类所访问。
protected 实现继承的 重要手段。
这里,在子类 StreamingAud ioPLayer 是子类,其中没有定义
但是可以调用 speak.open()
说明 子类继承了父类
Use Cases
利用定义的Core Object, 列举出每个Object对应产生的use case。
每个use case只需要先用一句简单的话来描述即可。
ElevatorSystem
实现事情
Handle request
Request
Elevator
take external request 外部传入
take internal request 内部按钮
Open gate 开门
Close gate 关门
ElevatorButton
Press button
Class
阶段总结
- 澄清问题 what how
- 确定设计哪些类
- use cases
Class
类图
为什么画类图
可交付,Minimal Viable Product
节省时间,不容易在Coding上挣扎
建立在Use case上,和之前的步骤层层递进,条例清晰,便于交流和修改
如果时间允许/面试官要求,便于转化成Code
怎么画类图
遍历你所列出的use cases
对于每一个use case,更加详细的描述这个use case在做什么事情 。
(例如:take external request ->
ElevatorSystem takes an external request, and decide to push this request to an appropriate elevator)
针对这个描述,在己有的Core objects里填充进所需要的信息。
Use case: Handle request
ElevatorSystem takes an external request, and decide to push this request to an appropriate elevator