10

例如,我总是在这个站点中看到覆盖getPreferredSize() 而不是使用setPreferredSize() 这些先前线程中所示的建议。

  1. 使用覆盖 getPreferredSize() 而不是 setPreferredSize() 用于固定大小的组件
  2. 我应该避免在 Java Swing 中使用 set(Preferred|Maximum|Minimum)Size 方法吗?
  3. 覆盖 setPreferredSize() 和 getPreferredSize()

看这个例子:

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 值,则返回该值;否则遵从组件的布局管理器。

所以这样做显然违反了里氏替换原则

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.,但不会破坏如何。

4

2 回答 2

5

一般来说,这个问题没有简单(或正确)的答案。

压倒一切会getPreferredSize破坏 Liskov 替换原则吗?是的(基于可用的文档)。

但不是大多数对象扩展吗?如果必须严格遵守原始实现的期望,那么改变方法的行为有什么意义(是的,有很好的例子,当你应该这样做时,比如hashcodeequals其他行是灰色的)?

在这种情况下,问题似乎源于对 的不当使用setXxxSize以及这些方法实际上是public. 为什么他们是公开的?我不知道,因为它们导致的问题比 API 的任何其他部分(包括KeyListener)更多。

覆盖getPreferredSize是首选,因为更改是随对象携带的,这与setPreferredSize从对象所有权/上下文外部调用不同

因为getXxxSize假设向布局管理器提供大小提示,实际上似乎没有任何合理的理由来实际拥有这些setXxxSize方法public,因为恕我直言,开发人员不应该弄乱它们 - 需要一个组件来提供最好的根据自己的内部需求估计它需要的大小。

以这种方式覆盖的原因getXxxSize也是为了防止其他人更改您指定的值,这可能是出于特定原因。

一方面,正如您所建议的,我们对 API 有期望,但另一方面,有时我们想要控制大小,而很多时候您不希望用户更改值.

我个人的感觉是尽量忽略setXxxSize(或者当作protected)。覆盖的原因之一getXxxSize是阻止人们更改大小,但同样,您可以覆盖setXxxSize并抛出不受支持的异常。

如果您将忽略的决定记录在案,setXxxSize这是否构成对 Liskov 替换原则的破坏?可能,因为组件仍然可以像它的父级一样工作。

我的一般直觉是了解 Liskov 替换原则试图做什么,知道什么时候应该使用它,什么时候不应该使用它。不可能有适用于所有情况的明确规则,尤其是当您考虑设计本身错误的情况时。

根据您的示例,您不应该覆盖getXxxSize或根本不应该覆盖setXxxSize,而是setXxxSize从构造函数调用,因为这将维护当前的 API 合同,但也会从构造函数调用可覆盖的方法...

所以无论你往哪里看,你都在踩别人的脚趾……

这一切的短处。如果它对您很重要(维护 Liskov 替换原则),您应该setXxxSize在自己的组件上下文中使用。这样做的问题是,不可能阻止某人用自己的价值观抹杀你的设计决策,正如我在评论中所说,当人们在没有真正理解他们在做什么的情况下这样做时,这只会让其他人的工作成为一场噩梦.

不要滥用setPreferredSize,仅在对象实例的上下文中使用它并拒绝从外部调用它......恕我直言

于 2014-01-11T05:20:18.543 回答
5

这个有趣问题的几个方面(Mad 已经提到了我的备用开发者)

我们是否违反了 LSP 仅覆盖 getXXSize() (与 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 首选:true bound:true 描述:组件的首选大小。

这是......完全错误:作为绑定属性意味着每当值更改时都需要通知侦听器,即以下(伪测试)必须通过:

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 的常量部分成为绑定属性——这样的覆盖根本不可能。可能是(当然是疯狂地猜测)一个历史问题:最初,setter 仅在 Swing 中可用(有充分的理由)。在其向后移植到 awt 时,它突变为从未有过的 bean 属性。

于 2014-01-11T11:12:47.457 回答