返回不可变列表

时间:2012-02-21 18:02:03

标签: java java-ee

我正在创建一个名为Question的类。这个类有答案,所以我希望能够返回附加到它的答案列表。

但是,如果用户对答案进行了更改,我希望用户调用更新方法,以便我可以进行其他验证等。如果用户获得答案列表,他现在仍然可以通过说明更改答案question.getAnswers().get(0).setDescription("BLAH BLAH").

所以我想回复每个答案的副本,让用户改变这个,他必须将它合并/更新回问题。通过这种方法,我可以确保答案是有效的,但答案的等于方法基于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;
    ...
}

4 个答案:

答案 0 :(得分:1)

你应该重新考虑你的应用程序的设计,但由于我不确定你的域约束总体如何,我不能建议稍微重新设计或任何类型。

一个简单明了的答案是:程序到界面,而不是实现。如果要在修改setter之后强制调用update方法,请考虑使用Decorator模式。

  • 创建界面
  • 让你的具体类实现这个接口(答案)
  • 添加一个具体的类(选择一个更好的名字,但是AnswerDecorator),它实现接受上面提到的类的构造函数中的具体类的接口

然后你只需将所有方法委托给内部实例,并将你想要调用的方法委托给update,就这样做:

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

答案 1 :(得分:0)

为什么不在更新方法中使用id字段进行比较?当您向用户提供答案对象的副本时,请确保您的副本具有与当前答案对象相同的ID。因此,当用户更改答案时,您的更新验证代码仍然可以根据ID字段找出已更改的答案。

答案 2 :(得分:0)

一般来说,我知道2个解决方案。

  1. 返回列表的深层副本而不是源列表。在这种情况下,用户在答案描述中所做的任何更改都不会产生任何影响。 从性能和内存利用的角度来看,这个解决方案很简单但效果很差。它也不是用户友好的。当用户尝试执行受限操作时,良好的API应该抛出异常。

  2. 其他解决方案是返回不可修改对象的列表,即当用户尝试调用其setter时抛出异常的对象。

  3. 此解决方案有几种可能的实现方式。

    2.1。修改模型类的setter。如果以只读模式创建对象,则所有setter都应该能够抛出异常。

    2.2。用户包装(装饰)模式。

    2.2.1。为所有值对象创建接口,并为每个类实现包装器。例如,AnswerWrapper将实现接口Answer,保持在Answer类型的包装对象内,将除setters之外的所有方法委托给该对象的适当方法,并从每个setter中抛出异常。

    2.2.2。使用动态代理技术。您仍然需要接口,但不必为所有类实现所有setter。此解决方案类似于方面。

    2.3。使用方面。例如AspectJ。

答案 3 :(得分: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);