如果我们谈论域对象,为什么setter在接口上不好?
澄清:
我有一个存储在db中的域对象。它有几个领域,设置成本很高。即。
class JurasicPark {
private long area;
...other fields ...
getters and setters
....
private Collection<Dinosaur> dinosaurs;
private Collection<ExoticTree> flora;
public Collection<Dinosaur> getDinosaurus(){
...
}
public Collection<ExoticTree> getFlora(){
...
}
}
字段dinosaurs
和flora
的初始化和设置成本非常高。但在许多情况下,我不需要每次都设置这个字段。
问题在于,如果我返回到具有dinosaurs
或flora
的JurasicPark类的用户实例,请不要初始化它,请将导致填充到NPE或我自己投掷的一些预测。我不想让api用户考虑这个并记住哪些字段可能没有设置。
因此,为了解决这个问题,我考虑创建两个接口IJurasicPark
和IFullJurasicPark
,第一个将声明所有访问方法转换为简单字段,前者将声明访问方法flora
和{{ 1}}字段。
dinosaurs
在这种方法中,IJurasicPark界面将包含getter和setter,所以我实际上问这个设计是不是很糟糕?
我也不想在使用LazyInit异常的hibernate风格中实现它。
答案 0 :(得分:10)
谁告诉过你的?一般来说,这当然不是真的。
在某些情况下 可能,其中有关财产不应由外部方修改。例如。不可变类的接口肯定不能包含setter。
确实在设计界面时应该仔细考虑,而不是盲目地为类的所有属性自动生成getter + setter。但这并不意味着制定者总是不好。
您的澄清完全改变了这个问题......您可以立即从这个问题开始,从而节省了您和其他人的时间。虽然我没有抱怨 - 我得到了很多(不应当的)赞成票: - )
所以你的问题是在域对象中初始化/设置昂贵的集合属性。我的第一种方法确实是延迟加载/保存,可以根据上下文以很多不同的方式实现。
您的帖子建议(但未明确说明)有问题的集合是从数据库中提取/持久存储到数据库中的。但是,您没有提到您使用的持久性解决方案。你也没有解释为什么你不想要一个“Hibernate style lazy init” - 恕我直言,它在大多数情况下非常有效且易于使用。如果您向我们提供有关这些的更多详细信息,我们可能会提供更好的答案。
如果没有恐龙和植物群的JurassicPark对象具有逻辑意义并且可以按原样使用,那么您建议的界面分割可能会有意义。同样,如果没有更多的背景,很难判断这一点。您似乎正在根据实现细节做出设计决策,这通常不是一个好主意。
如果你真的想这样做,你实际上不需要单独的接口 - 只需定义一个包含简单成员及其访问器方法的JurassicPark
(抽象)基类,然后将其子类化为另一个类,添加重量级字段。
答案 1 :(得分:1)
一般来说,setter不是问题,并且在接口中包含非常有用,有几种情况它们并不好。但无论如何这两种情况都很常见。
基本上,如果类(或类成员)是不可变的,那么使用set方法是没有意义的。彼得提到了这一点。
另一次是在你的抽象中没有意义的时候。例如,假设你有一个Car
类(是的,这是一个汽车类比),在赛车模拟/游戏中有一个int speed
类成员。实际上,一个人可以加速和减速,一个人无法立即设定他们的速度,所以揭示setSpeed
方法是没有意义的。
话虽如此,setSpeed
方法仍可用于调试目的或内部操作。例如,可以编写类的内部。
private void setSpeed(int newSpeed) {
if(newSpeed < 0)
error();
speed = newSpeed;
}
public void accelerate() {
setSpeed(getSpeed() + 1);
}
请注意,set方法是私有的,而加速功能是公共的。这里的setSpeed
主要是为了避免代码重复。
答案 2 :(得分:1)
我认为你的界面不应该包含修改它不知道的属性的方法。
这似乎是Java,这意味着您无法在界面中声明私有/受保护的属性;根本不允许这样做。因此,您将接口绑定到实现(使用特定属性),这可能被视为不良做法。一般来说,这是我试图避开的。
答案 3 :(得分:-1)
老实说,我不太了解这是什么,但他们在学校教我的是,你可以使用界面作为商务舱的外壳。因此,与界面交谈的对象不了解其背后的实际对象。因此,你可能不想制作setter,因为它们可以修改和查看实际对象的内容。
相反,我被告知使用方法来修改对象。
有人可以澄清我在说什么,或者我现在正在混淆什么?
我在代码中谈论的内容翻译为:
Interface IBoard = new ActualBoard();