如何在超类方法中从子类调用块?

时间:2019-03-18 01:39:56

标签: smalltalk gnu-smalltalk

我认为我的问题主要是语法,但这可能是我对类层次结构的整体理解。基本上,这是一个Deck类,其数组中填充有Card对象,Card是Deck的子类,因此Deck应该能够使用Card的块和方法,对吗?如果是的话,我会搞砸语法,以尝试调用它。我正在使用嵌套的while循环来填充数组,但是我希望Card对象的每个实例都具有该卡的外观和等级,而不是仅打印“ A Card”。我离开了我尝试使Card对象成为另一个尺寸为2的数组以容纳Suit和Rank的位置,但是我的gst编译器说它期望一个“对象”,所以显然我在做错什么。我粘贴了代码,这样您就可以了解我在说什么。除了块调用之外,它还用空白卡对象填充了大小为52的数组,因此Deck类的其余部分基本上可以正常工作。

"The Deck object class is a deck of 52 Card objects. "
Object subclass: Deck [
    | Content |
        <comment: 'I represent of Deck of Cards'>
    Deck class >> new [
        <category: 'instance creation'>
        | r |
        r := super new . 
        Transcript show: 'start ' .
        r init .
        ^r
    ] 

    init [
        <category: 'initialization'>

        |a b c|
        Content := Array new: 52 .
        a := 1 .
        c := 1 . 



        [a <= 4] whileTrue:[
            b := 1 . 
            [b <= 13] whileTrue:[
                |card|
                card := Card new .
                Card := Array new: 2 .            "This is where I'm trying to use the Card class blocks to make the Card objects have Rank and Suit" 
                Card at: 1 put: Card setRank: b| . "and here the rank"
                Card at: 2 put: Card getSuit: a| . "and the suit"
                Content at: c put: card .

                b := b + 1 .
                c := c + 1 . 

            ].

            a := a + 1 . 

        ].
     Content printNl . 
    ]

] .

"The Card object has subclass Suit and a FaceValue array of Suit and Rank. "
Object subclass: Card [
    | Suit Rank |
        <comment: 'I represent a playing Card' >
    init [
        <category: 'initialization'>
        Suit := Club .
        Rank := 1 .
        Transcript show: 'nCard ' .
        ^super init
    ]
    getSuit: suitVal [
        suitVal = 1 ifTrue: [Suit := Club] . 
        suitVal = 2 ifTrue: [Suit := Diamond] . 
        suitVal = 3 ifTrue: [Suit := Heart] . 
        suitVal = 4 ifTrue: [Suit := Spade] . 
        ^Suit 
    ] "getSuit"

    setRank: rankVal [
    Rank := rankVal . 
    ^Rank
    ]
] 

z := Deck new .

2 个答案:

答案 0 :(得分:3)

编辑一次-由于联邦调查局的评论

欢迎来到。

免责声明:我使用的是Smalltalk / X-jv分支,该分支与gnu-smalltalk不同,因此它不是smalltalk,所以我不是gnu-smalltalk专家。

我会指出一些我发现的不足之处。有太多需要指出的地方。我给你一些一般的想法。

  1. 我通常不建议使用abc ... z作为变量。如果过了一段时间再返回代码,您将无法理解。

  2. 对于变量,请使用小写字母,例如content而不是Content。首先 大写字母保留用于全局变量。在您的用例中,这将是一个类名(不要混合使用)。

  3. 如果要创建新实例,请像下面这样使用它:aCard := Card new.

  4. 不要在#init(初始化)方法中创建整个应用程序逻辑!您应该使用小的可读方法破坏代码。

您的init应该遵循以下原则:

  Deck extend [
      init [
          <category: 'initialization'>
          content := Array new: 52.
          Transcript show: 'Initializing Deck...'.
      ]
  ]

将使用实例变量#init创建一个content方法。不要忘记为变量创建访问器。阅读guide了解gnu-smalltalk和创建实例方法。

  1. 您应该在whileTrue:循环上有注释。为什么有人要猜测限制的原因?

     [a <= 4] whileTrue:[
           b := 1 . 
           [b <= 13] whileTrue:[
     ...
    
  2. 有充分的理由重新定义new消息。 Transcript消息可以是ini init

    Deck class >> new [
         <category: 'instance creation'>
         | r |
         r := self new . 
         Transcript show: 'start  '.
         r init .
         ^r
    ] 
    

您为什么要重新定义新的?如果您有一个Object subclass:对象,那么它 已经了解new消息。

在代码的底部,您有z := Deck new.(我建议使用例如myDeck := Deck new.)。如果要运行初始化,只需执行myDeck init

  1. 您为什么在Card >> init返回^super init?也许您想做第一行super init(读取\加载超类init)然后返回^ self?很难说。

  2. Suit := Club .是什么意思?您是否正在以某种方式创建新对象? (缺少#new条消息)。您是否在尝试串音?然后应该是suit := 'Club'.。 (所有Sunit变量分配都一样)。

更多的只是从水晶球中读取。尝试阅读有关Smalltalk的更多信息,希望我的技巧对您有所帮助。

答案 1 :(得分:2)

一些更正:您不调用块,而是调用方法。

您提交的代码中唯一的块是whileTrue:的参数,并且是方法专用的(不能从外部访问)。

这种混乱不是您的错。这是由于此文件格式用于描述方法的块符号引起的。就个人而言,我不喜欢它,我发现它比实用更令人困惑(除了为了看起来更主流的语言,向基于文件的语言致敬之外)。

那么您如何调用方法?您通过发送消息来做到这一点,没有其他方法。您所能做的就是发送一条消息。 Smalltalk是面向消息的。消息一直传下来。

当接收到消息时,该对象将在其类方法字典中查找消息选择器。如果不存在,它将在超类中进行查找,等等。但这是对象本身的事。

因此,最后,您并没有真正考虑调用方法,因为不同的对象可以使用不同的方法来响应相同的消息。您必须考虑将任务委派给特定对象,即消息和相关合同。

那合同是什么?您希望一张牌具有西服(俱乐部,黑桃,...)和等级(1到13)。因此,您使用这两个实例变量创建了Card类。到目前为止,一切都很好。

然后,您要创建52张卡片以填充卡片组。那就是实例化Card类。你是怎样做的?您已将消息new发送到Card类。您可以创建一个更合适的类消息,该消息将充当Card构造函数,并给出一个西装和一个等级。

那将是

Card class >> newWithSuit: aSuit rank: anInteger
    [<category: 'instance creation'>
    ^self basicNew setSuit: aSuit rank: anInteger ]

setSuit: aSuit rank: anInteger
    [<category: 'private'>
    suit := aSuit.
    rank := anInteger ]

然后,您不需要定义单个的setter(在代码中为getSuit:setRank:),因为您可能不希望以后更改这些属性,除非在构造时。这就是为什么我通常更喜欢一个归类为“私人”的二传手。那只是一个约定,但这就是我们在Smalltalk中定义合同的方式,是通过一组非正式约定,以及方法和类注释(以及SUnit TestCase,尤其是如果您采用测试驱动的设计)。

您决定将等级编码为1到13之间的整数。没关系。 但是,您还必须决定如何代表诉讼。您的代码尚不清楚,因为混合使用了整数(1到4)和未声明的变量(Club,Spade等)。这些是全局变量吗?

然后,您不必编码那些西服并在大小为2的数组中排名,由于所有这些数据已经包含在Card对象中,因此绝对没有必要。因此,您需要在甲板上填充:

content at: deckRank put: (Card newWithSuit: suitNumber rank: rankNumber).

注意括号:您将带有两个参数at:put:的消息content发送到deckRank,而另一条消息返回的值发送了(Card newWithSuit: suitNumber rank: rankNumber),我们希望这是一个正确初始化的Card实例。

在您提供的代码Card at: 1 put: Card setRank: b中,您向带有三个参数的类at:put:setRank:发送消息Card,该参数包含3个参数,即整数常量1,类Card和所指向的对象通过变量b。那可能不是你的意图。在|之后还有一个b栏,对我来说似乎不太正确。条形图用于描绘临时变量,或将块自变量与块内块指令分开,或者也可以是二进制消息,但在这种情况下,需要一个自变量(每条二进制消息,例如+-* /都有一个接收器和一个自变量)。

希望我能给您一些有用的提示,但也许您必须阅读并应用一些分步的Smalltalk教程才能更好地了解这些基本概念。