1

SmallTalkでメッセージの送信者を取得できません。私が達成したいのは、最初のメソッド(A)によって呼び出される別のメソッド(B)からのメソッド(A)の戻り値を変更することです。繰り返しますが...AがBを呼び出し、BがAのコンテキストから値を返すようにします。

コード例:

これはAになります:

A

| aResult aPartialResult |

aPartialResult := self B.

"do things with aPartialResult"

^aResult.

そしてこれはBになります:

B

| aResult |

[ aResult := "do something" ]
                        on: Exception
                        do: ["make A return something"].
^aResult.

問題は、Bで発生する可能性のある例外をBでも処理できるようにしたいということです。そのため、Bで例外を発生させて、Aで処理し、そこから簡単に戻ることができません。

thisContextを使用してこれを実行できると思いましたが、送信者はnilです。なぜそれなのかについての答えを得ることも害はありません...

前もって感謝します!

4

4 に答える 4

3

Guillermo、例外処理は、ここでいくつかの悪いアイデアをシームレスに置き換えることができます。

  1. thisContextを使用する(これはほとんど必要ではなく、通常は悪い考えです)
  2. たとえば「1|」の周りに文字列を渡す、UserInterfaceinvalidCartIdErrorMessage
  3. returnの使用:これらの文字列を使用してエラーを渡します

また、retrieveCart:onErrorReturnFrom:の実行が多すぎます。すべてのエラーハンドラーを使用すると、実際のロジックが失われます。

したがって、最初に行うことは、ドメインの概念を表すErrorサブクラスを作成することです(例:AddBookError、CartExpiredError、InvalidCartError)。

次に、次のようにエラーメッセージを設定します。

CartExpiredError>>initialize

    super initialize.
    self messageText: '1|', UserInterface cartHasExpiredErrorMessage.

次のこと(実際には2つのステップ)は、生のディクショナリメソッドをプライベートアクセサに置き換えることです。プライベートアクセサは、次のように新しいErrorクラスを使用できます。

timestampFor: aCartId

    ^ cartCreationDateAndTime at: aCartId ifAbsent: [ InvalidCartError signal ].

cartNumber: aCartId 

     ^ carts at: aCartId ifAbsent: [ InvalidCartError signal ].

Cart>>add: aQuantity booksWithISBN: aBookISBN

    fail ifTrue: [ AddBookError signal ].

これで、retrieveCart:onErrorReturnFrom:は次のようになります。

retrieveCart: aCartId

    | aCartCreationDateAndTime |
    aCartCreationDateAndTime := self timestampFor: aCartId.
    Time now > (aCartCreationDateAndTime + 30 minutes) ifTrue:  [ CartExpiredError signal ].
    ^ self cartNumber: aCartId.

そして最後に、大幅に簡略化されたAは次のようになります。

add: aQuantity booksWithISBN: aBookISBN toCart: aCartId

     | aCart |
    [aCart := self retrieveCart: aCartId.
    aCart add: aQuantity booksWithISBN: aBookISBN]
        on: Error
        do: [ :e |  ^ e messageText ].
    ^ '0|OK'.

これはまだクリーンアップでき(たとえば、messageTextの前に「1 |」を付けるすべてのErrorクラスのスーパークラスを作成する)、明らかにこの簡略化されたバージョンを実際のプロジェクトに組み込む必要がありますが、例外がどのようにできるかを確認し始めることができますあなたの人生を楽にしますか?

これは、 githubでのテストに合格した、コードの動作するモックアップです。

nb私が気付いたもう1つのことは、aCartCreationDateAndTimeでした。これがカートのプロパティである方が自然に思えますが、実際のアプリケーションでは意味がないかもしれません...

于 2012-06-27T16:44:02.020 に答える
1

簡単な方法は、次のように、AがBに戻るブロックを渡すことです。

A
   | aResult aPartialResult |
   aPartialResult := self BonSpecialConditionDo: [:partial | ^partial].
   ...snip...

それで

BonSpecialConditionDo: aBlock
    | partialResult |
    partialResult := self doSomethingPartial.
    ^[self doSomething]
        on: SomeException
        do: [:exc | aBlock value: partialResult]

注意してください、例外をキャッチすることは危険であると考えられます(あなたはあまりにも多くのものをキャッチします)。

編集:ハンドラー内の不要なリターン^を削除しました

編集:スーパーパワーでそれを行う(ただし、ハンマーでハエを殺さないでください)

B
    | partialResult |
    partialResult := self doSomethingPartial.
    ^[self doSomething]
        on: SomeException
        do: [:exc | thisContext home sender home return: partialResult ]

Exception exc(これはインスタンス変数handlerContextです)を介してthisContextにアクセスできると思いましたが、この内部状態にアクセスするための便利なメッセージはないようです...

于 2012-06-27T10:15:47.073 に答える
0

とった!

これがAです:

A

| aResult aPartialResult |

aPartialResult := self BOnErrorReturnFrom: thisContext.

"do things with aPartialResult"

^aResult.

そしてこれはBになります:

BOnErrorReturnFrom: aContext

| aResult |

[ aResult := "do something" ]
                        on: Exception
                        do: [aContext return: "whatever you want :)"].
^aResult.

キーワード「thisContext」をBlockClosureで使用すると、ブロックが宣言されたContextPartではなく、他の何かが返されることに気付いた後、それほど難しくはありませんでした(ただし、それが何であるかはまだわかりません)。

ショーンへの返答:

私がこれでやろうとしているのは、コードの繰り返しを避けることです。AとBを実装するオブジェクトは、RESTインターフェースの内部(モデル側)部分であるため、文字列と文字列のみを返すようにします(例外や文字列オブジェクトとは異なるものは通過させたくありません) 。私の特定の問題では、A(Smalltalkメッセージの命名規則に違反して申し訳ありませんが、これを変更するとさらに混乱が生じると思います...)はカートIDを受け取り、そのカートで何かを実行します。Aは、そのIDを使用してカートを取得し、カートでいくつかの検証を行い、エラーが発生した場合は、アドホックメッセージを(常に文字列として)返す必要があります。この取得と検証のコードは、カートを取得する必要のある各メッセージで繰り返されます。

これは私が最終的に得た適切なコードです:

これはAです:(#tryメッセージに注意を払わないでください。文字列に変換せずに例外が発生しないことを保証します。誰かがこれをより良い方法で行う方法を知っている場合は、方法を教えてください!)

add: aQuantity booksWithISBN: aBookISBN toCart: aCartId

    | aCart aContext |

    aContext := thisContext.

    ^self try: [

        aCart := self retrieveCart: aCartId onErrorReturnFrom: aContext.

        [aCart add: aQuantity booksWithISBN: aBookISBN]
                                                    on: Error
                                                    do: [ :anError | ^'1|', (self formatAsResponse: anError messageText) ].

        ^'0|OK'.
    ].

これはBです:

retrieveCart: aCartId onErrorReturnFrom: aContext

    | aCartCreationDateAndTime aCart |

    [aCartCreationDateAndTime := cartCreationDateAndTime at: aCartId asInteger.]
                                                                                    on: KeyNotFound 
                                                                                    do: [ aContext return: ('1|', (UserInterface invalidCartIdErrorMessage)).].

    (systemClock now > (aCartCreationDateAndTime + 30 minutes)) 
                                                            ifTrue:  [aContext return: ('1|', (UserInterface cartHasExpiredErrorMessage))].

    [aCart := carts at: aCartId asInteger.]
                                    on: KeyNotFound
                                    do: [ aContext return: ('1|', (UserInterface invalidCartIdErrorMessage))].

    ^aCart.
于 2012-06-27T14:28:40.537 に答える
0

A

| aResult aPartialResult |

aPartialResult:=selfB。

「aPartialResultで何かをする」

^aResult。

B

| aResult |

[aResult:= "do something"] on:例外do:["make Areturnsomething"]。^aResult。

私はそれを次のように書きます

A

| aResult aPartialResult |

aPartialResult:= self B. aPartialResult ifNotNil:[

「aPartialResultで何かをする」]。

^aResult。

B

| aResult |

[aResult:= "do something"] on:例外do:[:e |^nil]。^aResult。

それではそれは私だけです!!!

于 2012-06-27T19:56:02.013 に答える