1

我無法在SmallTalk中獲取郵件的發件人。我想完成的是從另一個方法(B)修改方法(A)的返回值,該方法由第一個方法(A)調用。再次... A呼叫B,我希望B從A的上下文中返回一個值。如何在SmallTalk(Pharo)中獲得消息發送者?

示例代碼:

這將是:

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中提出例外來處理它,並且很容易從那裏返回。

我想我可以使用thisContext來做到這一點,但發件人是零。這也不會傷害也得到答案,爲什麼是...

在此先感謝!

+0

這不是破解封裝爲什麼B應該知道關於A的一切? – Mark

+0

B和A是_same object_上的方法。 –

+0

@Guillermo我仍然試圖圍繞你正在嘗試的東西......除了指定返回值之外,你還在做B中的錯誤處理程序嗎?這是你的目標,即你是否堅持A/B部門,還是靈活?真的,如果你能以較少的以實現爲中心的方式陳述具體的問題,那將會是什麼。我們可以很容易地回答你的問題,但是這在Smalltalk中似乎非常罕見,直覺表示有更好的方法來處理它... –

回答

3

吉列爾莫,異常處理可以無縫地在這裏代替一些不好的想法:

  1. 使用thisContext(這是幾乎從未必要,通常 一個壞主意)
  2. 繞過例如串'1 |',的UserInterface invalidCartIdErrorMessage
  3. 使用return:與這些字符串沿着

而且傳失誤,retrieveCart:onErrorReturnFrom:做太多。使用所有錯誤處理程序,實際的邏輯會丟失。

因此,我想要做的第一件事就是創建Error子類來表示您的域概念,例如AddBookError,CartExpiredError,InvalidCartError

然後你剛纔設置的錯誤消息,也許是這樣的:

CartExpiredError>>initialize 

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

接下來的事情(其實是兩步)被替換爲私人存取原始字典方法,可以使用新的錯誤類,像這樣:

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'. 

這可以仍然被清潔(例如爲所有預先設置'1 |'的錯誤類創建一個超類到messageText),顯然你將不得不將這個簡化版本加入到你的實際項目中,但是你能否開始看到異常情況如何讓你的生活更輕鬆?

Here是代碼的工作樣機,以在github

N.B.通過測試我注意到的另一件事是aCartCreationDateAndTime。將它作爲購物車的一個屬性似乎更自然,但也許在實際應用中沒有意義。

+0

不錯。我比我的教授的回答更喜歡它(當然這比我的安靜得多)。就像你說的那樣,關於aCartCreationDateAndTime的評論是一個背景問題。購物車的失效是與服務器資源保存相關的垃圾收集需求,因此它不屬於商店模型。或者至少這是我的課程所給予的。謝謝大家的答案! –

1

一個簡單的方法是,通過一個模塊裏面返回到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,但似乎沒有任何方便的消息訪問此內部狀態...

+0

這部分解決了我的問題,但B應該能夠使A返回一系列結果,而不僅僅是一個。我也可以做一些類似BOnEvent1Do的事情:BOnEvent2Do:等等,但我試圖保持A儘可能乾淨。另外,如果最終B被更改爲處理新的錯誤,我將不得不更正B @ –

+0

@GuillermoAres的所有發件人,您可以對每個partialResult重複使用相同的塊,因爲該塊將只是將任何partialResult的返回發送給發件人的A,所以它會是BOnAnyEventDo:但是現在你暴露了你的意圖,我也喜歡肖恩的解決方案。取決於messageText編碼對CartExpiredError的所有用法是否有價值,或者是非常特定於處理程序... –

0

知道了!

這裏有一個:

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檢索購物車,在購物車上進行一些驗證,並在發生錯誤時返回臨時消息(始終爲字符串)。該檢索和驗證代碼將在每個必須檢索購物車的消息中重複。

這是正確的代碼,我終於得到了:

這是一個:(不注意的#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. 
+0

聽起來像你想在這裏繼續。檢查應該有一個實現延續的庫 – mathk

+0

爲什麼不在'#retrieveCart:'方法中使用簡單的異常。所以你可以捕捉並從中返回。 '[self retrieveCart:aCartId] on:UserInterfaceMessageException do:[| msgException | ^'1 |',msgException消息]' – mathk

+0

@mathk:有一個:http://ss3.gemstone.com/ss/Control/。儘管如此,延續似乎在這裏矯枉過正。 –

0

一個

| aResult aPartialResult |

aPartialResult:=自B.

「做事情aPartialResult」

^aResult。

| aResult |

[aResult:=「do something」] on:異常 做:[「讓一個返回的東西」]。 ^ a結果。

我會把它寫成

一個

| aResult aPartialResult |

aPartialResult:=自B. aPartialResult ifNotNil:

「做aPartialResult的事情」。

^aResult。

| aResult |

[aResult:=「do something」] on:異常 do:[:e |^nil]。 ^ a結果。

那麼這就是我!