面试题: 设计并实现一个计算器
正常实现: 在控制器中实现大量的 switch 判断不同操作符.
但是计算相关的代码写到控制器里去, 如果计算一复杂, 控制器会多很多代码, 可读性和逻辑容易混乱, 控制器只应该负责把计算结果显示到相关的视图上去就行, 其他的事情应该交给专门的类去实现.
斯坦福教程中, 使用一个 CalculatorBrain 专门负责接收操作数, 执行运算, 返回结果.
定义属性和方法, accumulator 属性用来存储操作数以及计算结果, setOperand:方法用来接收操作数, performOperation:方法执行运算过程, result 只读属性返回计算结果:
class CalculatorBrain {
var accumulator = 0.0
func setOperand(operand: Double) { }
func performOperation(symbol: String) { }
var result: Double {
get {
return accumulator
}
}
}
问题: 每次都要使用根据不同的运算符, 然后在 Switch 中去执行不同的运算.
改进: 使用枚举 Enum 来表示运算类型, 用 Dictionary 来表示和存储不同的运算类型对应的运算.
enum Operation {
case Constrant(Double)
case UnaryOperation((Double) -> Double)
case BinaryOperation((Double, Double) -> Double)
case Equals
}
该枚举将每一种运算类型对应到每一个枚举成员, 并给枚举成员设置关联值类型,常量运算关联了一个 Double 类型, 一元运算关联了一个 (Double)-> Double 类型的闭包,二元操作运算关联一个 (Double, Double) -> Double 类型的闭包, Equals 运算没有设置关联值类型.
定义一个字典存储不同的运算符以及对应的运算操作
var operations: Dictionary<String, Operation> = [
"π": .Constrant(M_PI),
"e": .Constrant(M_E),
"√": Operation.UnaryOperation(sqrt),
"cos": Operation.UnaryOperation(cos)
]
修改 performOperation 中的代码
func performOperation(symbol: String) {
if let operation = operations[symbol] {
switch operation {
case .Constrant(let value): accumulator = value
case .UnaryOperation(let function): accumulator = function(accumulator)
default: break
}
}
}
在 Switch 语句中, 关联值可以被提取出来作为 switch 语句的一部分. 第一个 case: 关联值是一个 Double 类型的常量, 那么提取出来的关联值就是这个常量的值, 第二个 case: 关联值是一个 (Double) -> Double 类型的闭包, 那么提取出来的关联值就是这个闭包.
考察:
1 Swift 中枚举的使用
2 有简化代码逻辑的意识, 能及时优化自己的代码
follow-up
1 使用链式编程思想实现一个计算器
2 使用面向对象的方式实现一个计算器