Как вызвать блоки из подклассов в методе суперкласса? - PullRequest
1 голос
/ 18 марта 2019

Я думаю, что моя проблема главным образом в синтаксисе, но это может быть мое общее понимание иерархии классов.По сути, это класс Deck с массивом, заполненным объектами Card, Card является подклассом Deck, поэтому Deck должна иметь возможность использовать блоки и методы Card, верно?Если это так, я испортил синтаксис, ужасно пытаясь вызвать его.Я использую вложенные циклы while, чтобы заполнить массив, но я хочу, чтобы каждый экземпляр объекта Card имел масть и ранг этой карты, а не просто печатал «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 ]

3 голосов
/ 18 марта 2019

Отредактировано один раз - из-за комментария fede s.

Добро пожаловать в SO.

Отказ от ответственности : я использую Smalltalk/ X-jv ветвь, которая отличается от smalltalk от gnu-smalltalk, поэтому я не эксперт gnu-smalltalk.

Я укажу вам на некоторые недостатки, которые я обнаружил.Слишком много, чтобы указывать.Я дам вам несколько общих идей.

  1. Обычно я не рекомендую использовать a, b, c ... z в качестве переменных.Если вы вернетесь к коду через некоторое время, вы его не поймете.

  2. Для переменных используйте строчные буквы, такие как content вместо Content.Первая заглавная буква зарезервирована для глобальных переменных.В вашем случае использования это будет имя класса (не смешивайте его).

  3. Если вы хотите создать новый экземпляр, используйте его следующим образом: aCard := Card new.

  4. Не создавайте всю логику приложения в своем методе #init (initialize)!Вы должны разбивать ваш код небольшими читаемыми методами.

Ваш init должен выглядеть следующим образом:

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

, который создаст метод #init с экземпляромпеременная content.Не забудьте создать методы доступа к переменной.Прочтите руководство для gnu-smalltalk и создания методов экземпляра.

Вы должны иметь комментарии к whileTrue: петлям.Почему кто-то должен угадывать причину ограничений?

 [a <= 4] whileTrue:[
       b := 1 . 
       [b <= 13] whileTrue:[
 ...

Имеют веские основания для переопределения сообщения 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.

Почему вы на Card >> init возвращаете ^super init?Может быть, вы хотели сделать первую строку super init (чтение \ загрузка инициализации суперкласса), а затем вернуть ^ self?Трудно сказать.

Suit := Club . Что это значит?Вы создаете как-то новый объект?(пропущено #new сообщение).Ты пытаешься задеть ниточку?Это должно быть suit := 'Club'. тогда.(то же самое относится ко всем переменным Sunit).

Больше было бы просто читать с хрустального шара.Попробуйте прочитать больше о Smalltalk, и я надеюсь, что мои советы помогут вам в дороге.

2 голосов
/ 20 марта 2019

Небольшое исправление: вы не вызываете блок, вы вызываете метод.

Единственные блоки в представленном вами коде - это аргументы whileTrue: и являются частными для метода (недоступны изснаружи).

Это заблуждение не ваша вина.Это связано с блочной нотацией, используемой этим форматом файла для разграничения методов.Лично мне это не нравится, я нахожу это более запутанным, чем полезным (за исключением того, что я выгляжу как более распространенные языки, дань языкам на основе файлов).

Итак, как вы вызываете метод?Вы делаете это, отправляя сообщение, другого пути нет.Все, что вы можете сделать, это отправить сообщение.Smalltalk ориентирован на сообщения.Это сообщения до самого конца.

При получении сообщения объект будет искать селектор сообщений в своем словаре методов класса.Если нет, это будет поиск в суперклассе и т. Д. Но это дело самого объекта.

Итак, в конце концов, вы на самом деле не думаете о вызове метода, потому что другой объект может ответитьк тому же сообщению другим методом.Вы должны думать с точки зрения делегирования задачи специализированному объекту, то есть с точки зрения сообщения и связанного с ней контракта.

Так, каковы контракты?Вы хотите, чтобы на карте был костюм (Club, Spade, ...) и ранг (от 1 до 13).Итак, вы создали класс Card с этими двумя переменными экземплярами.Пока все хорошо.

Затем вы хотите создать свои 52 карты, чтобы заполнить колоду.Это экземпляр класса Card.Как ты это делаешь?Вы отправили сообщение new в класс 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 ]

Тогда вам не нужноОпределите отдельные сеттеры (getSuit: и setRank: в вашем коде), потому что вы, вероятно, не хотите изменять эти атрибуты впоследствии, кроме как во время создания.Вот почему я обычно предпочитаю один сеттер, который я классифицирую как «частный».Это всего лишь соглашение, но именно так мы определяем контракты в Smalltalk с помощью набора неформальных соглашений, а также с помощью методов и комментариев к классам (и SUnit TestCase, особенно если вы используете тестовый дизайн).

Youрешили закодировать ранг в целое число от 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, чтобы лучше ознакомиться с этими основными понятиями.

...