我总是在此网站中看到覆盖getPreferredSize()
的建议,而不是使用setPreferredSize()
,例如这些先前的主题中所示。
见这个例子:
public class MyPanel extends JPanel{
private final Dimension dim = new Dimension(500,500);
@Override
public Dimension getPreferredSize(){
return new Dimension(dim);
}
public static void main(String args[]){
JComponent component = new MyPanel();
component.setPreferredSize(new Dimension(400,400));
System.out.println(component.getPreferredSize());
}
}
setPreferredSize()
- 设置此组件的首选大小。
getPreferredSize()
- 如果preferredSize已设置为非空值,则返回。如果UI委托的getPreferredSize方法返回非null 价值然后返回;否则,请遵循组件的布局 管理器。
这样做可以明显打破Liskov Substitution Principle。
prefferedSize
是绑定属性,因此在设置时会执行firePropertyChange
。所以我的问题是,当您覆盖getPrefferedSize()
时,是否还需要覆盖setPreferredSize(..)
?
示例:
public class MyPanel extends JPanel{
private Dimension dim = null;
@Override
public Dimension getPreferredSize(){
if(dim == null)
return super.getPreferredSize();
return new Dimension(dim);
}
@Override
public void setPrefferedSize(Dimension dimension){
if(dim == null)
dim = new Dimension(500,500);
super.setPreferredSize(this.dim); //
}
public static void main(String args[]){
JComponent component = new MyPanel();
component.setPreferredSize(new Dimension(400,400));
System.out.println(component.getPreferredSize());
}
}
现在我们看到我们得到了相同的结果,但是听众会得到真实值的通知,而且我们不会破坏LSP导致setPreferredSize
状态Sets the preferred size of this component.
而不是如何。
答案 0 :(得分:5)
一般来说,这个问题没有简单(或正确)的答案。
重写getPreferredSize
是否打破Liskov替换原则?是(基于可用的文档)。
但是大多数对象扩展都没有?如果必须严格遵守原始实现的期望,那么改变方法行为的重点是什么(是的,当你应该这样做时有很好的例子,比如hashcode
和equals
和其他线条变灰的人?)
在这种情况下,问题似乎从setXxxSize
的不当使用以及这些方法实际上是public
的事实延伸出来。他们为什么公开?我不知道,因为它们引起的问题多于API的任何其他部分(包括KeyListener
)。
首选覆盖getPreferredSize
,因为对象携带更改,而不是从对象所有权/上下文外部调用setPreferredSize
因为假设getXxxSize
为布局管理器提供了大小调整提示,实际上似乎没有任何合理的理由让setXxxSize
方法public
实际上具有,{IMO}开发人员不应该搞乱他们 - 需要一个组件根据自己的内部要求提供所需大小的最佳估计。
以这种方式覆盖getXxxSize
的原因还在于阻止其他人更改您指定的值,这可能是由于特定原因造成的。
一方面,正如您所建议的那样,我们对API有所期待,但另一方面,有时我们想要控制大小和很多次,当您不希望用户改变价值。
我个人的感觉是尽可能地忽略setXxxSize
(或将其视为protected
)。覆盖getXxxSize
的原因之一是阻止人们更改大小,但同样,您可以覆盖setXxxSize
并抛出不受支持的异常。
如果你要记录忽视setXxxSize
的决定,那将构成Liskov替代原则的中断吗?可能,因为组件仍然可以像它的父母那样行事。
我的一般直觉是要了解Liskov Substitution Principle正在尝试做什么,知道何时应该使用它以及何时不应该使用它。不能满足每种情况的明确规则,特别是在考虑设计本身错误的情况时。
根据您的示例,您不应该覆盖getXxxSize
或setXxxSize
,而是从构造函数中调用setXxxSize
,因为这会保留当前的API合约,但是也踩过从构造函数中调用可覆盖方法的脚趾......
所以无论你看到什么,你都会踩到别人的脚趾......
缺乏一切。如果它对您很重要(维护Liskov替换原则),则应在您自己的组件上下文中使用setXxxSize
。这样做的问题在于,不可能阻止某人用自己的价值观来消除你的设计决策,正如我在评论中所说的那样,当人们在没有真正了解他们正在做的事情的情况下这样做时,这只会让每个人的工作成为噩梦
不要滥用setPreferredSize
,只能在对象实例的上下文中使用它并拒绝从外部调用它...恕我直言
答案 1 :(得分:5)
这个有趣问题的几个方面(Mad已经提到了备用我的伙伴开发人员)
我们是否在覆盖getXXSize()时违反了LSP(也与setXXSize()相比)?
如果我们正确地执行它,那就不行了:-)第一个权限是属性的API文档,最好来自其来源,即组件:
将此组件的首选大小设置为常量值。对getPreferredSize的后续调用将始终返回此值。
这是一个绑定合同,所以无论我们实现getter,它必须遵守常量值,如果设置:
@Override
public Dimension getPreferredSize() {
// comply to contract if set
if(isPreferredSizeSet())
return super.getPreferredSize();
// do whatever we want
return new Dimension(dim);
}
XXSize是绑定属性 - 是吗?
在JComponent的祖先中只有间接证据:实际上,Component会在setter中触发PropertyChangeEvent。 JComponent本身似乎记录了这个事实(由我加粗):
@beaninfo 首选:是的 bound:true description:组件的首选大小。
哪个是......明显错误:作为绑定属性意味着只要值发生变化就需要通知侦听器,即以下(伪测试)必须通过:
JLabel label = new JLabel("small");
Dimension d = label.getPreferredSize();
PropertyChangeListener l = new PropertyChangeListener() ...
boolean called;
propertyChanged(...)
called = true;
label.addPropertyChangeListener("preferredSize", l);
label.setText("just some longer text");
if (!d.equals(label.getPreferredSize())
assertTrue("listener must have been notified", l.called);
......但失败了。由于某种原因(不知道为什么这可能认为合适),他们希望xxSize的常量部分成为绑定属性 - 这样的叠加是根本不可能的。本来可以(疯狂地猜测)一个历史性的问题:最初,只有Swing才能使用setter(有充分的理由)。在它的backport中,它变成了一个从未有过的bean属性。