设计模式专题-解析器设计模式

问题 顾客们在你的机器App或者网页上进行报价。最终的报价包含了2个部分,零件成本和劳动成本。我们希望能够对劳动力部分或者零件成本部分都可以进行适当的调整。这些调整可能来自优惠券,保修索赔或讲价。我们需要一种简单的语言来表达这些定制的调整。例如,我们应该能够定义一个表达式,可以为部分添加20%的折扣。或者是另一个减少10美元劳动力的,或者两者都有。

解决方案 我们会定义一种简单语言来做调整。我们将会有一套规则来涵盖加法,减法,乘法和除法。我们还将定义两个变量,他们将映射到零件和劳动力价格。一旦定义好了语言,我们的用户可以通过遵守语法的字符串来表达调整。例如,从我们将要使用的部分中减去10美元:

var expression = "l + p - 10.00"
//adjustment: total price is labor + partsPrice - $10
复制代码

在开始之前,让我们先了解一下我们需要构建什么。为了能够解析字符串并将其解释为一个数学函数,我们需要定义一组规则和对象。通过观察我们的问题,我们发现了一些问题:

  • 数字:比如$10或者15%。
  • 变量:零件和劳动成本的占位符或者替代符。
  • 操作: +, -, * , / 这些操作符会定义好2个变量之间的操作运算。

虽然这些对象都有他们自己的含义和解析,但是他们都有一个共同的特性,他们都需要用我们定义的语言来解析。因此,我们定义一个协议,所有表达式都需要实现我们这个协议。

protocol Expression {
    
    func interpret(variables:[String: Expression]) -> Double
}
复制代码

这个协议有一个函数方法叫作interpret。他有一个参数字典,和一个Double返回值。之前我们提到,没一个表达式都需要一个函数去解析它,而且,我们希望,从表达式得到的最终返回值是一个Double

有了基础的表达式协议之后,我们可以看看var expression = "l + p - 10.00"这个代码,他有3部分组成(我们上面提到的)。 那么,我们先来定义第一部分,值(数字)

class Number: Expression {
    
    var value: Double
    
    init(value: Double) {
        
        self.value = value
    }
    
    func interpret(variables:[String: Expression]) -> Double {
        
        return value
    }
}
复制代码

Number类是一个值类型,有一个属性value,代表着这个对象的值,有初始化方法,还实现了表达式协议interpret方法,解析出变量的值。

上面我们也提到,变量,变量是一个很重要的东西,我们这个场景下的变量就是l 和 p。那么,我们来定义一个这样的类:

/// 变量类
class Variable: Expression {
    private var name: String
    
    init(name: String) {
        self.name = name
    }
    
    func interpret(variables: [String : Expression]) -> Double {
        
        if let expression = variables[name] {
            return expression.interpret(variables: variables)
        }else {
            return 0.00
        }
    }
}

复制代码

这个变量类有一个属性name,还实现了表达式协议interpret方法,解析出变量的值。

有了变量和值类型之后,我们需要为他们提供一些加减乘除的基础API

class Add: Expression {
    
    var leftOperand: Expression
    var rightOperand: Expression
    
    init(leftOperand: Expression, rightOperand: Expression) {
        
        self.leftOperand = leftOperand
        self.rightOperand = rightOperand
    }
    
    func interpret(variables:[String: Expression]) -> Double {
        return leftOperand.interpret(variables: variables) + rightOperand.interpret(variables: variables)
    }
}

class Subtract: Expression {
    
    var leftOperand: Expression
    var rightOperand: Expression
    
    init(leftOperand: Expression, rightOperand: Expression) {
        
        self.leftOperand = leftOperand
        self.rightOperand = rightOperand
    }

    func interpret(variables: [String : Expression]) -> Double {
        
        return leftOperand.interpret(variables: variables) - rightOperand.interpret(variables: variables)
    }
}

class Multiply: Expression {
    
    var leftOperand: Expression
    var rightOperand: Expression
    
    init(leftOperand: Expression, rightOperand: Expression) {
        
        self.leftOperand = leftOperand
        self.rightOperand = rightOperand
    }
    
    func interpret(variables: [String : Expression]) -> Double {
        
        return leftOperand.interpret(variables: variables) * rightOperand.interpret(variables: variables)
    }
}

class Divide: Expression {
    
    var leftOperand: Expression
    var rightOperand: Expression
    
    init(leftOperand: Expression, rightOperand: Expression) {
        
        self.leftOperand = leftOperand
        self.rightOperand = rightOperand
    }
    
    func interpret(variables: [String : Expression]) -> Double {
        
        return leftOperand.interpret(variables: variables) / rightOperand.interpret(variables: variables)
    }
}
复制代码

他们的写法都是一样的意思,有左右两边的表达式,可以是Variable 也可能是Number类型,然后可以通过interpret方法解析出值来。

接下来就是整个运算的核心了,他实现了如何报价具体,可以看我们写的注释,我就不详细说明了:

/// 评估价格类
class Evaluator: Expression {
    
    /// 默认的语法树
    var syntaxTree: Expression = Number(value: 0.00)
    
    /// 表达式
    var expression: String
    
    
    /// 自定义的一个堆栈容器
    struct Stack<T> {
        
        var items = [T]()
        
        /// 增加一个元素,入栈
        ///
        /// - Parameter item: 元素
        mutating func push(item: T) {
            items.append(item)
        }
        
        /// 移除最后的元素,出栈
        ///
        /// - Returns: 元素
        mutating func pop() -> T {
            return items.removeLast()
        }
    }
    
    
    /// 初始化
    ///
    /// - Parameter expression: 表达式
    init(expression: String) {
        
        self.expression = expression
    }
    
    /// 建立语法树
    private func buildSyntaxTree() {
        var expressionStack = Stack<Expression>()
        
        /// 把所有元素按空格切割
        var items = expression.components(separatedBy: " ")
        
        var index = 0
        
        while index < items.count {
            
            switch items[index] {
            case "*":
                let nextExpression = getNextExpression(items: items, index: index)
                expressionStack.push(item: Multiply(leftOperand: expressionStack.pop(), rightOperand: nextExpression))
                index += 2
            case "/":
                let nextExpression = getNextExpression(items: items, index: index)
                expressionStack.push(item: Divide(leftOperand: expressionStack.pop(), rightOperand: nextExpression))
                index += 2
            case "+":
                let nextExpression = getNextExpression(items: items, index: index)
                expressionStack.push(item: Add(leftOperand: expressionStack.pop(), rightOperand: nextExpression))
                index += 2
                
            case "-":
                let nextExpression = getNextExpression(items: items, index: index)
                expressionStack.push(item: Subtract(leftOperand: expressionStack.pop(), rightOperand: nextExpression))
                index += 2
                
            default:
                
                /// 如果是值就初始化一个Number变量
                if let doubleValue = items[index].doubleValue {
                    expressionStack.push(item: Number(value: doubleValue))
                    index += 1
                } else {
                    //如果是l,p这样的变量,就初始化一个Variable变量
                    expressionStack.push(item: Variable(name: items[index]))
                    index += 1
                }
            }
            
        }
        syntaxTree = expressionStack.pop()
    }
    
    /// 获取下一个表达式
    ///
    /// - Parameters:
    ///   - items: 元素
    ///   - index: 位置
    /// - Returns: 表达式
    private func getNextExpression(items: [String], index: Int) -> Expression {

        /// 如果
        let next = items[index + 1]
        var nextExpression: Expression
        
        /// 能解析成值
        if let doubleValue = next.doubleValue {
            nextExpression = Number(value: doubleValue)
        } else {
            //不能解析成值,则认为是一个变量,l,p,零件或者劳动力
            nextExpression = Variable(name: next)
        }
        return nextExpression
    }
    
    
    /// 解析
    ///
    /// - Parameter variables: 变量
    /// - Returns: 返回值
    func interpret(variables: [String : Expression]) -> Double {
        
        //如果有2种运算
        if (expression.contains("/") || expression.contains("*")) &&
            (expression.contains("+") || expression.contains("-")) {
            
            let expressions = parseoutAdditionsAndSubtractions(input: expression)
            var newExpression = ""
            var index = 0
            for expression in expressions {
                if expression == "+" || expression == "-" {
                    newExpression += expression
                } else {
                    let eval = Evaluator(expression: expression)
                    let result = eval.interpret(variables: variables)
                    newExpression += String(result)
                }
                
                if index != expressions.count - 1 {
                    newExpression += " "
                }
                index += 1
            }
            let evaluator = Evaluator(expression: newExpression)
            return evaluator.interpret(variables: variables)
        } else {
            //只有一种运算
            
            //建立语法树
            buildSyntaxTree()
            return syntaxTree.interpret(variables: variables)//返回解析值
        }
    }
    
    private func parseoutAdditionsAndSubtractions(input: String) -> [String] {
        var result = [String]()
        
        let items = input.components(separatedBy: " ")
        
        var sentence = ""
        var index = 0
        for item in items {
            if item == "+" || item == "-" {
                result.append(sentence.trim())
                result.append(item)
                sentence = ""
            } else {
                sentence += item
                if index != items.count - 1 {
                    sentence += " "
                }
            }
            index += 1
        }
        result.append(sentence)
        return result
    }
}
复制代码

然后我们再提供一个用户操作的封装类:

/// 报价类
class Quote {
    
    /// 零件价格
    var partsPrice: Double
    
    /// 劳动力价格
    var laborPrice: Double
    
    /// 调整价格
    var adjustments: String?
    
    
    /// 初始化报价
    ///
    /// - Parameters:
    ///   - partsPrice: 零件价格
    ///   - laborPrice: 劳动力价格
    ///   - adjustments: 调整价格
    init(partsPrice: Double, laborPrice: Double, adjustments: String?) {
        self.partsPrice = partsPrice
        self.laborPrice = laborPrice
        self.adjustments = adjustments
    }
    
    /// 初始化报价
    ///
    /// - Parameters:
    ///   - partsPrice: 零件报价
    ///   - laborPrice: 劳动力报价
    convenience init(partsPrice: Double, laborPrice: Double) {
        self.init(partsPrice: partsPrice, laborPrice: laborPrice, adjustments: nil)
    }
    
    
    /// 总价格
    var totalPrice: Double {
        
        /// 需要调整价格
        if let adjustments = adjustments {
            var variables = [String: Expression] ()
            variables["l"] = Number(value: laborPrice)
            variables["p"] = Number(value: partsPrice)
            
            /// 评估价格
            let evaluator = Evaluator(expression: adjustments)
            return evaluator.interpret(variables: variables)
        } else {
            return partsPrice + laborPrice
        }
    }
}
复制代码

最后,我们写一个测试方法:

func interpreterTest() {
    let quote = Quote(partsPrice: 145.00, laborPrice: 45.00)
    
    //adjustment: total price is only labor plus $20
    quote.adjustments = "l + 20.00"
    print(quote.totalPrice)

    //adjustment: total price is partsPrice - $10
    quote.adjustments = "p - 10.00"
    print(quote.totalPrice)

    //adjustment: total price is labor and part - $10
    quote.adjustments = "l + p - 10.00"
    print(quote.totalPrice)
    
    //adjustment: total price is labor + 10% off parts
    quote.adjustments = "l + p - p * 0.1"
    print(quote.totalPrice)
    
    //adjustment: total price is 20% off labor + 10% off parts
    quote.adjustments = "l - l * 0.2 + p - p * 0.1"
    print(quote.totalPrice)
    
    //adjustment: total price is %20 off total price
    quote.adjustments = "l - l * 0.2 + p - p * 0.2"
    print(quote.totalPrice)
    
    //adjustment total price is parts * labor :|
    quote.adjustments = "p * l"
    print(quote.totalPrice)
}
复制代码

这样,我们就可以自定义我们的表达式来做一些运算了。有兴趣可以查看具体的英文链接 打印的结果:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值