Smalltalk 语言 访问者模式实战 计算表达式值的访问者

Smalltalk阿木 发布于 2025-05-29 6 次阅读


阿木博主一句话概括:Smalltalk【1】 语言中访问者模式【2】在表达式求值【4】中的应用实战

阿木博主为你简单介绍:
访问者模式是一种行为设计模式【5】,它允许在不改变对象结构【6】的情况下,对对象结构中的元素进行操作。本文将围绕Smalltalk语言【7】,通过一个表达式求值的实例,展示如何使用访问者模式来实现对表达式求值的扩展和增强。

关键词:Smalltalk,访问者模式,表达式求值,设计模式

一、
在编程中,我们经常需要对表达式进行求值,例如数学表达式、逻辑表达式等。随着需求的增加,表达式可能变得复杂,包含多种操作符【8】和操作数。在这种情况下,直接修改表达式求值逻辑可能会导致代码难以维护和扩展。访问者模式提供了一种优雅的解决方案,通过将操作与对象结构分离,使得对表达式求值的扩展变得简单。

二、Smalltalk 语言简介
Smalltalk 是一种面向对象编程语言,以其简洁、直观和动态性著称。在Smalltalk中,所有对象都是类的实例,而类则由方法定义。Smalltalk 的这种设计使得它非常适合用于演示设计模式。

三、访问者模式概述
访问者模式定义了一个表示作用于某对象结构中的各元素的操作,它使得算法可以在不改变各元素类的前提下定义作用于这些元素的新操作。

访问者模式包含以下角色:
- 抽象访问者【9】(Visitor):定义一个访问者接口,它包含一个访问操作,用于访问每个具体元素【10】
- 具体访问者【11】(ConcreteVisitor):实现抽象访问者定义的访问操作,针对不同的元素类型执行不同的操作。
- 抽象元素【12】(Element):定义一个接受操作的接口,在具体元素中实现这个接口。
- 具体元素(ConcreteElement):实现抽象元素接口,存储要被访问的数据。
- 对象结构(Object Structure):存储和管理元素对象,它提供一个方法,允许访问者访问每个元素。

四、表达式求值实例
以下是一个使用Smalltalk语言实现的简单表达式求值系统【13】,其中包含加法【14】和减法【15】操作符。

smalltalk
| expressionTree |
expressionTree := ExpressionTree new
expressionTree addSubExpression: (ExpressionTree new value: 5)
addSubExpression: (ExpressionTree new value: 3)
addSubExpression: (ExpressionTree new value: 2)
addSubExpression: (ExpressionTree new value: 1)

expressionTree accept: [ :element |
element isANumber ifTrue: [ Transcript show: element value ].
element isAnAddition ifTrue: [ Transcript show: 'Addition' ].
element isASubtraction ifTrue: [ Transcript show: 'Subtraction' ] ].

在这个例子中,`ExpressionTree` 是一个抽象元素,它可以是数字或操作符。`addSubExpression:` 方法用于构建表达式树【16】。`accept:` 方法用于接受访问者【3】

smalltalk
Class category: ExpressionTree

Class variable: 'value' instance variable: 'left' instance variable: 'right'

Class method: new
^ self new: 0 withLeft: nil withRight: nil

Method: initialize: aNumber withLeft: aLeft withRight: aRight
"Initialize an expression tree node."
self value: aNumber.
self left: aLeft.
self right: aRight.

Method: isANumber
"Check if this is a number."
^ self value isNumber.

Method: isAnAddition
"Check if this is an addition operation."
^ self isANumber and: [ self left isANumber and: [ self right isANumber ] ].

Method: isASubtraction
"Check if this is a subtraction operation."
^ self isANumber and: [ self left isANumber and: [ self right isANumber ] ].

Method: accept: aVisitor
"Accept a visitor to perform an operation."
aVisitor visit: self.
self left accept: aVisitor ifNotNilDo: [ :left |
left isANumber ifFalse: [ left accept: aVisitor ] ].
self right accept: aVisitor ifNotNilDo: [ :right |
right isANumber ifFalse: [ right accept: aVisitor ] ].

五、扩展表达式求值
现在,我们想要扩展表达式求值系统以支持乘法【17】和除法【18】操作符。使用访问者模式,我们可以轻松地实现这一点。

smalltalk
Class category: ExpressionTree

Class method: new
^ self new: 0 withLeft: nil withRight: nil

Method: initialize: aNumber withLeft: aLeft withRight: aRight
"Initialize an expression tree node."
self value: aNumber.
self left: aLeft.
self right: aRight.

Method: isANumber
"Check if this is a number."
^ self value isNumber.

Method: isAnAddition
"Check if this is an addition operation."
^ self isANumber and: [ self left isANumber and: [ self right isANumber ] ].

Method: isASubtraction
"Check if this is a subtraction operation."
^ self isANumber and: [ self left isANumber and: [ self right isANumber ] ].

Method: isAMultiplication
"Check if this is a multiplication operation."
^ self isANumber and: [ self left isANumber and: [ self right isANumber ] ].

Method: isADivision
"Check if this is a division operation."
^ self isANumber and: [ self left isANumber and: [ self right isANumber ] ].

Method: accept: aVisitor
"Accept a visitor to perform an operation."
aVisitor visit: self.
self left accept: aVisitor ifNotNilDo: [ :left |
left isANumber ifFalse: [ left accept: aVisitor ] ].
self right accept: aVisitor ifNotNilDo: [ :right |
right isANumber ifFalse: [ right accept: aVisitor ] ].

现在,我们可以创建一个新的访问者来处理乘法和除法操作。

smalltalk
Class category: ExpressionTreeVisitor

Class method: new
^ self new

Method: visit: anElement
"Perform an operation on the element."
"This method should be overridden by concrete visitors."
^ self.

Method: visitNumber: aNumber
"Visit a number element."
"This method should be overridden by concrete visitors."
^ self.

Method: visitAddition: anAddition
"Visit an addition element."
"This method should be overridden by concrete visitors."
^ self.

Method: visitSubtraction: aSubtraction
"Visit a subtraction element."
"This method should be overridden by concrete visitors."
^ self.

Method: visitMultiplication: aMultiplication
"Visit a multiplication element."
"This method should be overridden by concrete visitors."
^ self.

Method: visitDivision: aDivision
"Visit a division element."
"This method should be overridden by concrete visitors."
^ self.

我们创建一个具体的访问者来计算表达式的值。

smalltalk
Class category: ExpressionTreeValueCalculator

InheritsFrom: ExpressionTreeVisitor

Method: visitNumber: aNumber
"Calculate the value of a number element."
^ aNumber value.

Method: visitAddition: anAddition
"Calculate the value of an addition element."
^ anAddition left accept: self value + anAddition right accept: self value.

Method: visitSubtraction: aSubtraction
"Calculate the value of a subtraction element."
^ aSubtraction left accept: self value - aSubtraction right accept: self value.

Method: visitMultiplication: aMultiplication
"Calculate the value of a multiplication element."
^ aMultiplication left accept: self value aMultiplication right accept: self value.

Method: visitDivision: aDivision
"Calculate the value of a division element."
^ aDivision left accept: self value / aDivision right accept: self value.

通过这种方式,我们可以轻松地扩展表达式求值系统以支持新的操作符,而不需要修改现有的表达式树结构【19】

六、结论
本文通过一个Smalltalk语言中的表达式求值实例,展示了如何使用访问者模式来扩展和增强表达式求值系统的功能。访问者模式使得在不改变对象结构的情况下,对对象结构中的元素进行操作成为可能,从而提高了代码的可维护性【20】和可扩展性【21】

通过本文的实例,我们可以看到访问者模式在Smalltalk语言中的强大应用,它为设计灵活、可扩展的软件系统提供了一种有力的工具。