用smalltalk重构一个方法

时间:2016-04-09 08:30:49

标签: smalltalk squeak

我是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

我的问题是 - 有没有办法更好地写这个?为了更紧凑?

同样 - 为什么我不能使用self inside方法引用a哪个是instanceVariableNames? 谢谢你的帮助!

编辑:

这是课程的定义方式:

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

MyShape只是源自Object而没有任何内容。

3 个答案:

答案 0 :(得分:4)

你可以把它变得更紧凑,是的。

首先,除了#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])]

此外,我们可以围绕整个声明做出回报:

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]]

美好而甜蜜,我们也很容易看到逻辑结构。 (请注意,我偏离常见的格式样式,以指出两个逻辑表达式中的相似点和不同点。)

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

对于实例变量问题:

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

Object subclass: #Contoso
    instanceVariableNames: 'things'
    classVariableNames: ''
    poolDictionaries: ''
    category: 'Examples'
isThingPlusThreeSameAs: anObject

    ^ thing + 3 = anObject

但通常情况下,最好通过 getters setters 引用实例变量,通常称为访问者。您必须手动编写它们或使用浏览器中类的第二个上下文菜单(通过“more ...”)的“create inst var accessor”菜单项:

这将生成表单

的访问器方法
thing

    ^ thing
thing: anObject

    thing := anObject

你可以在其他方法中使用它们

isThingPlusThreeSameAs: anObject

    ^ self thing + 3 = anObject

答案 1 :(得分:0)

我会把方法写成

equals: aTriangle
  a = aTriangle a ifFalse: [^false].
  b = aTriangle b ifFalse: [^false].
  ^c = aTriangle c

请注意我使用选择器#equals:而不是#isEqual:的方式,因为后者在英语中读得不好(如果它已经#isEqualTo: 1}},但#equals:更短。)

我在此添加的另一个评论是,您可以重新定义#=,而不是添加新的比较选择器。但是,在这种情况下,你必须注意其他一些事情:

= aTriangle
  self class = aTriangle class ifFalse: [^false].
  a = aTriangle a ifFalse: [^false].
  b = aTriangle b ifFalse: [^false].
  ^c = aTriangle c

课程检查的原因是为了确保#=是健壮的,即它没有发出MessageNotUnderstood异常的信号。另一件事是,每当你(重新)定义#=时,你还应该(重新)定义#hasht1 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的方法。

答案 2 :(得分:0)

你可能不应该这样写,但有些情况下元编程很有用

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