在smalltalk中重构一个方法
Refactoring a method in smalltalk
我是 Smalltalk (Squeak) 的新用户(实际上是在课程中学习它)。
我有一种方法来检查一个矩形是否等于给定的矩形,如下所示:
isEqual:givenRec
self a = givenRec a
ifTrue: [
self b = givenRec b
ifTrue: [
^true
].
^false
].
self b = givenRec a
ifTrue: [
self a = givenRec b
ifTrue: [
^true
].
^false
].
^false
我的问题是 - 有没有更好的写法?让它更紧凑?
另外 - 为什么我不能引用 a
这是 instanceVariableNames with self inside methods?
感谢您的帮助!
编辑:
class 是这样定义的:
MyShape subclass: #MyTriangle
instanceVariableNames: 'a b c'
classVariableNames: ''
poolDictionaries: ''
category: 'Ex2'
MyShape
只是从 Object
派生而来,什么都没有。
你可以让它更紧凑,是的。
首先,除了#ifTrue:
还有#ifFalse:
和#ifTrue:ifFalse:
,大致就是if-not--then和if--then--else的作用。
而且,我们已经有了逻辑 AND 条件,所以为什么不使用它:
isEqual: givenRec
(self a = givenRec a and: [self b = givenRec b])
ifTrue: [^ true].
(self b = givenRec a and: [self a = givenRec b])
ifTrue: [^ true].
^false
和#ifTrue:ifFalse:
isEqual: givenRec
(self a = givenRec a and: [self b = givenRec b])
ifTrue: [^ true]
ifFalse: [^ (self b = givenRec a and: [self a = givenRec b])]
另外,我们可以return围绕整个语句:
isEqual: givenRec
^ (self a = givenRec a and: [self b = givenRec b])
ifTrue: [true]
ifFalse: [self b = givenRec a and: [self a = givenRec b]]
但是ifTrue: [true]
有点多余,我们用#or:
isEqual: givenRec
^ (self a = givenRec a and: [self b = givenRec b]) or:
[self b = givenRec a and: [self a = givenRec b]]
很不错,我们也很容易看出逻辑结构。
(请注意,我从常见的格式风格出发,指出两个逻辑表达式的异同)。
我们现在只有一个return^
,没有#ifTrue:…
对于实例变量问题:
当您像以前那样在 class 中定义一些实例变量时,您可以在代码中直接使用它们来访问它们:
Object subclass: #Contoso
instanceVariableNames: 'things'
classVariableNames: ''
poolDictionaries: ''
category: 'Examples'
isThingPlusThreeSameAs: anObject
^ thing + 3 = anObject
但通常情况下,最好通过 getters 和 setters 引用实例变量,通常称为 accessors。您必须手动编写它们或使用浏览器中 class 的 第二个 上下文菜单(通过“更多...”)的 'create inst var accessor' 菜单项:
这将生成形式为
的访问器方法
thing
^ thing
thing: anObject
thing := anObject
您还可以像这样在其他方法中使用它们
isThingPlusThreeSameAs: anObject
^ self thing + 3 = anObject
我会把方法写成
equals: aTriangle
a = aTriangle a ifFalse: [^false].
b = aTriangle b ifFalse: [^false].
^c = aTriangle c
顺便说一下,我使用的是选择器 #equals:
而不是 #isEqual:
因为后者在英语中读起来不太好(如果是 #isEqualTo:
,但 #equals:
更短)。
我要在这里补充的另一点是,您可以重新定义 #=
而不是添加新的比较选择器。然而,在那种情况下,您必须注意一些额外的事情:
= aTriangle
self class = aTriangle class ifFalse: [^false].
a = aTriangle a ifFalse: [^false].
b = aTriangle b ifFalse: [^false].
^c = aTriangle c
class 检查的原因是为了确保 #=
是可靠的,即它不会发出 MessageNotUnderstood
异常信号。另一件事是,每次你(重新)定义 #=
时,你也应该(重新)定义 #hash
,使得 t1 hash = t2 hash
每当 t1 = t2
。例如
hash
^(a hash + b hash + c hash) hash
请注意,#hash
的这个提议不是最好的,但这不是讨论编写好的 #hash
函数的问题的地方。
更新
这是我的版本,顺序无关紧要
equals: aTriangle
| sides |
self = aTriangle ifTrue: [^true].
sides := Set with: a with: b with: c.
(sides includes: aTriangle a) ifFalse: [^false].
(sides includes: aTriangle b) ifFalse: [^false].
^(sides includes: aTriangle c)
第一个比较是在顺序相同时避免Set
的加速方法
你可能不应该这样写,但在某些情况下元编程很有用
isEqual: givenRec
#(a b c) do: [ :selector |
(self perform: selector) = (givenRec perform: selector) ifFalse: [
^false]].
^true
我是 Smalltalk (Squeak) 的新用户(实际上是在课程中学习它)。 我有一种方法来检查一个矩形是否等于给定的矩形,如下所示:
isEqual:givenRec
self a = givenRec a
ifTrue: [
self b = givenRec b
ifTrue: [
^true
].
^false
].
self b = givenRec a
ifTrue: [
self a = givenRec b
ifTrue: [
^true
].
^false
].
^false
我的问题是 - 有没有更好的写法?让它更紧凑?
另外 - 为什么我不能引用 a
这是 instanceVariableNames with self inside methods?
感谢您的帮助!
编辑:
class 是这样定义的:
MyShape subclass: #MyTriangle
instanceVariableNames: 'a b c'
classVariableNames: ''
poolDictionaries: ''
category: 'Ex2'
MyShape
只是从 Object
派生而来,什么都没有。
你可以让它更紧凑,是的。
首先,除了#ifTrue:
还有#ifFalse:
和#ifTrue:ifFalse:
,大致就是if-not--then和if--then--else的作用。
而且,我们已经有了逻辑 AND 条件,所以为什么不使用它:
isEqual: givenRec
(self a = givenRec a and: [self b = givenRec b])
ifTrue: [^ true].
(self b = givenRec a and: [self a = givenRec b])
ifTrue: [^ true].
^false
和#ifTrue:ifFalse:
isEqual: givenRec
(self a = givenRec a and: [self b = givenRec b])
ifTrue: [^ true]
ifFalse: [^ (self b = givenRec a and: [self a = givenRec b])]
另外,我们可以return围绕整个语句:
isEqual: givenRec
^ (self a = givenRec a and: [self b = givenRec b])
ifTrue: [true]
ifFalse: [self b = givenRec a and: [self a = givenRec b]]
但是ifTrue: [true]
有点多余,我们用#or:
isEqual: givenRec
^ (self a = givenRec a and: [self b = givenRec b]) or:
[self b = givenRec a and: [self a = givenRec b]]
很不错,我们也很容易看出逻辑结构。 (请注意,我从常见的格式风格出发,指出两个逻辑表达式的异同)。
我们现在只有一个return^
,没有#ifTrue:…
对于实例变量问题:
当您像以前那样在 class 中定义一些实例变量时,您可以在代码中直接使用它们来访问它们:
Object subclass: #Contoso
instanceVariableNames: 'things'
classVariableNames: ''
poolDictionaries: ''
category: 'Examples'
isThingPlusThreeSameAs: anObject
^ thing + 3 = anObject
但通常情况下,最好通过 getters 和 setters 引用实例变量,通常称为 accessors。您必须手动编写它们或使用浏览器中 class 的 第二个 上下文菜单(通过“更多...”)的 'create inst var accessor' 菜单项:
这将生成形式为
的访问器方法thing
^ thing
thing: anObject
thing := anObject
您还可以像这样在其他方法中使用它们
isThingPlusThreeSameAs: anObject
^ self thing + 3 = anObject
我会把方法写成
equals: aTriangle
a = aTriangle a ifFalse: [^false].
b = aTriangle b ifFalse: [^false].
^c = aTriangle c
顺便说一下,我使用的是选择器 #equals:
而不是 #isEqual:
因为后者在英语中读起来不太好(如果是 #isEqualTo:
,但 #equals:
更短)。
我要在这里补充的另一点是,您可以重新定义 #=
而不是添加新的比较选择器。然而,在那种情况下,您必须注意一些额外的事情:
= aTriangle
self class = aTriangle class ifFalse: [^false].
a = aTriangle a ifFalse: [^false].
b = aTriangle b ifFalse: [^false].
^c = aTriangle c
class 检查的原因是为了确保 #=
是可靠的,即它不会发出 MessageNotUnderstood
异常信号。另一件事是,每次你(重新)定义 #=
时,你也应该(重新)定义 #hash
,使得 t1 hash = t2 hash
每当 t1 = t2
。例如
hash
^(a hash + b hash + c hash) hash
请注意,#hash
的这个提议不是最好的,但这不是讨论编写好的 #hash
函数的问题的地方。
更新
这是我的版本,顺序无关紧要
equals: aTriangle
| sides |
self = aTriangle ifTrue: [^true].
sides := Set with: a with: b with: c.
(sides includes: aTriangle a) ifFalse: [^false].
(sides includes: aTriangle b) ifFalse: [^false].
^(sides includes: aTriangle c)
第一个比较是在顺序相同时避免Set
的加速方法
你可能不应该这样写,但在某些情况下元编程很有用
isEqual: givenRec
#(a b c) do: [ :selector |
(self perform: selector) = (givenRec perform: selector) ifFalse: [
^false]].
^true