在smalltalk中重构方法

在smalltalk中重构方法,smalltalk,squeak,Smalltalk,Squeak,我是Smalltalk(Squeak)的新用户(实际上是在课程中学习)。 我有一个方法来检查一个矩形是否等于一个给定的矩形,如下所示: isEqual:givenRec self a = givenRec a ifTrue: [ self b = givenRec b ifTrue: [ ^true ]. ^false ]. self b = givenRec a if

我是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? 谢谢你的帮助

编辑:

这就是类的定义方式:

MyShape subclass: #MyTriangle
    instanceVariableNames: 'a b c'
    classVariableNames: ''
    poolDictionaries: ''
    category: 'Ex2'

MyShape
只是从
Object
派生而来,没有任何东西。

你可以让它更紧凑,是的

首先,除了
#ifFalse:
之外,还有
#ifFalse:
#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
使用
#如果正确:如果错误:

isEqual: givenRec

    (self a = givenRec a and: [self b = givenRec b])
        ifTrue: [^ true]
        ifFalse: [^ (self b = givenRec a and: [self a = givenRec b])]
此外,我们可以围绕整个语句进行返回:

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]
有点多余,让我们使用
或:

isEqual: givenRec

    ^ (self a = givenRec a and: [self b = givenRec b]) or: 
      [self b = givenRec a and: [self a = givenRec b]]
又好又甜,我们也很容易看到逻辑结构。 (请注意,我与常见的格式样式不同,以指出这两个逻辑表达式的异同)

我们现在只有一个返回
^
,没有
#如果是:…


对于实例变量问题:

当您像以前一样在类中定义一些实例变量时,您可以在代码中使用它们来访问它们:

Object subclass: #Contoso
    instanceVariableNames: 'things'
    classVariableNames: ''
    poolDictionaries: ''
    category: 'Examples'
但通常,最好通过getter和setter(通常称为访问器)引用实例变量。您必须手动编写它们,或使用浏览器中类的第二个上下文菜单(通过“更多…”)的“创建inst var访问器”菜单项:

这将生成表单的访问器方法

thing

    ^ thing
你可以在其他类似的方法中使用它们

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
进行类检查的原因是确保
#=
是健壮的,即它不会发出
消息NotUnderstanding
异常的信号。另一件事是,每次你(重新)定义
=
时,你也应该(重新)定义
#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)

第一个比较是加速该方法,避免在顺序相同时设置

您可能不应该这样编写,但在某些情况下,元编程这是有用的

isEqual: givenRec
  #(a b c) do: [ :selector |
    (self perform: selector) = (givenRec perform: selector) ifFalse: [
      ^false]].
  ^true

谢谢,我编辑了我的问题-添加了类定义另一个问题@Tobias-如何在屏幕上绘制一个矩形,给定其两条边的长度?因此,您的类看起来很好,现在这取决于您用于比较的代码~~请为实例变量问题提出一个新问题~~编辑我的答案为绘制一个矩形,请提出一个新问题,这样更容易回答这是一个链接,当一个顶点不同时,您不能使用提前返回,因为他希望忽略顺序来比较顶点,所以(a,b,c)=(c,b,a)我不喜欢有两个不同的相等概念,一个是在#=中有有序顶点,另一个是在#equalsTo中有集合:…在#equals中,集合是用1而不是a定义的vertex@CarlosE.Ferro固定的。谢谢
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)
isEqual: givenRec
  #(a b c) do: [ :selector |
    (self perform: selector) = (givenRec perform: selector) ifFalse: [
      ^false]].
  ^true