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

问题描述 投票:1回答:2

我认为我的问题主要是语法,但可能是我对类层次结构的整体理解。基本上它是一个带有Card对象的数组的Deck类,Card是Deck的子类,所以Deck应该能够使用Card的块和方法,对吧?如果是的话,我正在搞乱语法,试图调用它。我使用嵌套的while循环来填充数组,但我希望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 .
smalltalk gnu-smalltalk
2个回答
3
投票

编辑一次 - 由于联邦公司的评论

欢迎来到SO。

免责声明:我使用的是Smalltalk / X-jv分支,它与gnu-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 initDeck 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(读取\加载超类初始化)然后返回^ self?很难说。
  2. Suit := Club .这是什么意思?你在某种程度上创造一个新的对象? (缺少#new消息)。你想要一个字符串吗?那应该是suit := 'Club'.。 (同样适用于所有Sunit变量分配)。

更多的只是阅读水晶球。尝试阅读有关Smalltalk的更多信息,我希望我的提示可以帮助您在路上。


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:and setRank:),因为你可能不想在之后更改这些属性,除非在构造时。这就是为什么我通常更喜欢一个单独的安装者,我将其归类为“私人”。这只是一个惯例,但这就是我们如何定义Smalltalk中的契约,通过一组非正式约定,以及通过方法和类注释(以及SUnit TestCase,特别是如果您接受测试驱动设计)。

你决定用1到13的整数编码。这没关系。但你还必须决定如何代表诉讼。在你的代码中不清楚,因为有一个Integer(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教程,以便更好地了解这些基本概念。

© www.soinside.com 2019 - 2024. All rights reserved.