2012-02-21 98 views
4

我正在創建一個名爲Question的類。這個類有答案,所以我想能夠返回附加的答案列表。返回不可變列表

但是,如果用戶對答案進行了更改,我希望用戶調用update-method,以便我可以執行其他驗證等。現在,如果用戶獲得答案列表,他仍然可以通過更改答案說question.getAnswers().get(0).setDescription("BLAH BLAH").

所以我想返回每個答案的副本,並讓用戶改變這一點,他必須合併/更新回到問題。通過這種方法,我可以確保答案是有效的,但答案的equals方法基於descriptioncorrect字段,而不是基於id字段,因爲我使用的是JPA。如果用戶使用這種方法更改答案,則更新方法將無法找到答案,因爲說明字段已更改並且不再相同,因此它在列表中找不到它。

任何建議?

public void updateAnswer(Answer answer) { 
    int index = answers.indexOf(answer); 
    answers.set(index, answer); 
} 

public List<Answer> getAnswers() { 
    return Collections.unmodifiableList(answers); 
} 


@Test 
public void shouldUpdateAnswerInQuestion() { 
    // Get first answer, make an update on the description 
    // and then update answer on question. 
    Answer answerThatWillBeUpdated = question.getAnswers().get(0); 
    String updatedAnswerDescription = "Hey, it is now updated!"; 
    answerThatWillBeUpdated.setDescription(updatedAnswerDescription); 
    question.updateAnswer(answerThatWillBeUpdated); 

    // After updating check that the answer in the list is equal 
    // to the answer updated. 
    Answer answerFromList = question.getAnswers().get(0); 

    assertEquals(answerThatWillBeUpdated, answerFromList); 
} 

答案類:

public class Answer { 

    private long id; 
    private String description; 
    private Boolean correct; 
    ... 
} 
+0

誰是「用戶」?他究竟得到了什麼:它的答案是一個獨立的問題,還是附帶的問題與答案?他是否應該修改答案,或者問題及其答案列表?爲什麼你的服務層沒有服務'updateAnswer(Answer a)'? – 2012-02-21 18:17:49

+0

因爲問題是聚合根 – LuckyLuke 2012-02-21 18:44:05

回答

0

爲什麼不使用id字段在更新方法比較呢?當您向用戶提供答案對象的副本時,請確保您的副本與當前答案對象具有相同的ID。因此,當用戶更改答案時,您的更新驗證代碼仍然可以根據id字段找出已更改的答案。

+0

我之所以提供JPA到問題的上下文是因爲人們會理解它是「壞」的。 – LuckyLuke 2012-02-21 18:39:40

+0

但是,在驗證數據之前,您沒有將數據存儲在數據庫中,對吧?是不是可以簡單地刪除該行,並在驗證完成後用新的替換它? – CodeBlue 2012-02-21 18:42:30

1

你應該重新考慮你的應用程序設計,但由於我不確定你的域名限制是什麼,所以我不能提出一個輕微的重新設計或類似的任何事情。

一個簡單而直接的答案是:編程到接口,而不是實現。如果要在修改setter後修改更新方法,請考慮使用Decorator模式。

  • 創建一個接口
  • 有你的具體類實現這個接口(答案)
  • 添加的具體類(選擇一個更好的名字,但AnswerDecorator)實現該接受的構造函數的具體類的接口在點以上

提到的類然後你只需委託的所有方法向內部實例和你想調用更新方法,做這樣的事情:

public void setField(int a){0} {0} {0} {0} innerInstance.setField(a); 更新(...); }

+0

但我不希望實體是隻讀的。用戶必須被允許改變,但由於問題是聚合根,所有的操作都應該通過公共方法來完成。 – LuckyLuke 2012-02-21 18:41:53

+0

你是對的,我沒有明確地回答。我會編輯我的答案.... – vinnybad 2012-02-21 18:55:38

+0

如果您發現它有用,請將此答案標記爲已接受。謝謝。 – vinnybad 2012-08-08 15:14:51

0

一般我知道2個解決方案。

  1. 返回列表的深層副本而不是源列表。在這種情況下,用戶在答案描述中所做的任何更改都不會產生任何影響。 從性能和內存利用角度來看,此解決方案非常簡單但效果不佳。它也不是用戶友好的。當用戶嘗試執行受限操作時,良好的API應該會引發異常。

  2. 其他解決方案是返回不可修改對象的列表,即當用戶嘗試調用其setter時拋出異常的對象。

該解決方案有幾種可能的實現方式。

2.1。修改你的模型類的setter。如果在只讀模式下創建對象,所有設置者應該能夠引發異常。

2.2。用戶包裝(裝飾)模式。

2.2.1。爲所有值對象創建接口併爲每個類實現包裝器。例如,AnswerWrapper將實現接口Answer,將其保存在Answer類型的包裝對象中,將除setter之外的所有方法委託給此對象的適當方法,並從每個setter中拋出異常。

2.2.2。使用動態代理技術。你仍然需要接口,但是你不必爲所有的類實現所有的setter。該解決方案與方面相似。

2.3。使用方面。例如AspectJ。

0

客戶機代碼列表,所以它知道了列表索引,有它告訴你哪一個更新:

public void updateAnswer(int index, Answer newAnswer) { 
    answers.set(index, answer); 
} 

// ... 

Answer answerThatWillBeUpdated = question.getAnswers().get(0); 
String updatedAnswerDescription = "Hey, it is now updated!"; 
answerThatWillBeUpdated.setDescription(updatedAnswerDescription); 
question.updateAnswer(0,answerThatWillBeUpdated);