Переопределение getPreferredSize () ломает LSP
Я всегда вижу советы в этом сайте переопределения getPreferredSize()
вместо использования setPreferredSize()
, как показано, например, в этих предыдущих потоках.
- использование переопределения getPreferredSize () вместо использования setPreferredSize() для компонентов фиксированного размера
- следует ли мне избегать использования методов set (Preferred / Maximum / Minimum) Size в Java Swing?
- переопределение 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 был установлен в ненулевое значение, просто возвращает его . Если метод getPreferredSize делегата пользовательского интерфейса возвращает ненулевое значение значение, то верните его; в противном случае отложите до макета компонента менеджер.
Таким образом, это явно нарушает подстановку Лискова Принцип .
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 cause setPreferredSize
состояния Sets the preferred size of this component.
, но не как.2 ответа:
Несколько аспектов этого интересного вопроса (Mad уже упоминал запасной-мой-коллега-разработчик)
нарушаем ли мы LSP при переопределении только getXXSize () (против setXXSize () также)?
Нет, если мы делаем это правильно : -) первый авторитет-это API doc свойства, лучший из его происхождения, то есть компонент:
Устанавливает предпочтительный размер этого компонента в постоянное значение. Последующие вызовы getPreferredSize всегда будут возвращать это значение.
Это обязательный контракт, поэтому, как бы мы ни реализовывали геттер, он должен уважать значение константы , если задано:
@Override public Dimension getPreferredSize() { // comply to contract if set if(isPreferredSizeSet()) return super.getPreferredSize(); // do whatever we want return new Dimension(dim); }
XXSize это связанное свойство - это?
В родословной J Component есть только косвенные доказательства: на самом деле компонент запускает свойство Changeevent в сеттере. Сам J component, кажется, документирует этот факт (выделено мной жирным шрифтом):
@beaninfo предпочтительно: правда граница: истина описание: предпочтительный размер компонента.
То есть ... простая ошибка: будучи связанным свойством, слушатели должны получать уведомления всякий раз, когда значение изменяется, то есть должен пройти следующий (псевдотест):
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 (по уважительным причинам). В своем бэкпорте к awt он мутировал в свойство бобов, которым никогда не был.
Вообще говоря, на этот вопрос нет простого (или правильного) ответа.
Нарушает ли переопределение
getPreferredSize
Принцип подстановки Лискова? Да (на основании имеющейся документации).Но разве большинство не возражает против расширения? Какой смысл изменять поведение метода, если он должен строго придерживаться ожиданий первоначальной реализации (да, есть хорошие примеры, когда вы должны это сделать, например
В этом случае проблема, по-видимому, проистекает из неуместного использованияhashcode
иequals
и другие, где линия является поседел)?setXxxSize
и того факта, что эти методы на самом делеpublic
. Почему они публичны? Я понятия не имею, поскольку они являются причиной большего количества проблем, чем любая другая часть API (включаяKeyListener
).Предпочтительно переопределение
getPreferredSize
, поскольку изменение осуществляется с объектом, в отличие от вызоваsetPreferredSize
извне объекта ownership / contextПотому что
getXxxSize
, как предполагается, обеспечивает калибровку намекает менеджеру по верстке, что на самом деле нет никакой разумной причины для того, чтобы на самом деле иметь методыsetXxxSize
public
, поскольку, ИМХО, разработчики не должны возиться с ними - компонент необходим для обеспечения наилучшей оценки размера, в котором он нуждается, исходя из его собственных внутренних требований.Причина переопределения
getXxxSize
таким образом также будет заключаться в том, чтобы помешать другим людям изменять указанное вами значение, которое может быть сделано для определенных целей. причины.С одной стороны, как вы предположили, мы ожидаем API, но с другой стороны, бывают моменты, когда мы хотим контролировать размер и много раз, когда вы не хотите, чтобы пользователь изменял значение.
Мое личное чувство состоит в том, чтобы игнорироватьsetXxxSize
как можно больше (или относиться к нему как кprotected
). Одна из причин переопределенияgetXxxSize
заключается в том, что люди не могут изменять размер, но в равной степени вы можете переопределитьsetXxxSize
и создать не поддерживаемое исключение.Если вы должны были документировать решения за игнорирование
Мое общее чутье-понять, что пытается сделать принцип замещения Лискова, знать, когда вы должны использовать его, а когда нет. не может быть четкого правила, которое соответствует каждому случаю, особенно если вы рассматриваете случай, когда сам дизайн неправильный.setXxxSize
будет ли это нарушением принципа замещения Лискова? Возможно, поскольку компонент все еще может действовать как родитель.Основываясь на вашем примере, вы не должно быть переопределения
Итак, куда бы вы ни посмотрели, вы наступаете кому-то на пятки...getXxxSize
илиsetXxxSize
вообще, но вызовsetXxxSize
из конструктора, так как это будет поддерживать текущий контракт API, но также будет наступать на пальцы вызова переопределяемых методов из конструктора...Короче всего. Если это важно для вас (чтобы сохранить принцип подстановки Лискова), вы должны использовать
setXxxSize
из контекста ваших собственных компонентов. Проблема с этим в том, что это невозможно остановить кого-то от уничтожения ваших дизайнерских решений с их собственными ценностями, и, как я сказал в комментариях, когда люди делают это, фактически не понимая, что они делают, это просто делает работу всех остальных кошмаром.Не злоупотребляйте
setPreferredSize
, используйте его только из контекста экземпляра объекта и сопротивляйтесь вызову его извне...ИМХО