2011-06-09 6 views
2

Мы избегаем этого предупреждения PMD, перемещая большую часть нашего кода конструктора в onInitialize(). Но мы просто переместим проблему (дефект дизайна?) В разное место?Калитка и метод конструктора вызывает переопределяемый метод Предупреждение PMD

Является нашим onInitialize() просто суррогатным конструктором, который не замечен PMD?

У нас возникли проблемы сорта, возникающие при вызове переопределяемых методов в конструкторе, но это, по-видимому, связано с тем, что сам Wicket называет один (не может найти точную строку источника, но onInitialize(), overridable method, заканчивается тем, что вызывается при вызове add() в конструкторе).

Счастливый предоставить образец кода, если он поможет.

public class PageA extends WebPage { 

    protected SomeBean bean; 

    public PageA() { 
     add(new Label("foo", "bar")); 
     bean = new SomeBean(); 
    } 

} 

public class PageB extends PageA { 

    public PageB() { 
     super(); 
    } 

    @Override 
    protected void onInitialize() { 
     add(new Label("rofl", bean.getSomeText())); 
    } 
} 

Вы бы ожидать, что это будет хорошо, но призыв к onInitialize не бывает, где вы можете думать, что делает:

При вызове add() на странице потока метод:

MarkupContainer add()  
MarkupContainer addedComponent() 
Page    componentAdded() 
MarkupContainer initialize() 
Component   fireInitialize() 
Component   onInitialize() 

Итак, вы можете увидеть, добавлен ли компонент к WebPage, запущен метод onInitialize(), который является переопределяемым методом, приводящим к экземплярам вышеуказанного здравого кода, создающим NullPointerException s.

Единственное предупреждение вы получите, что это может произойти, это JavaDoc из onInitialize():

ПРИМЕЧАНИЕ: Время этого вызова не является точным, то договор является то, что его называют иногда до {@link Компонент № onBeforeRender()}.

+0

Да, для меня это звучит как дефект дизайна. Тем не менее, это полезно, чтобы обеспечить конкретную вещь. –

+2

Также смотрите: http://stackoverflow.com/questions/4914151 –

+2

Если вы просматриваете форумы Wicket (http://apache-wicket.1842946.n4.nabble.com/VOTE-WICKET-3218-Component-onInitialize- is-broken-for-Pages-tc3341090.html # none), вы увидите, что это признанная и обсуждаемая проблема. – jbrookover

ответ

4

Если вы только добавляете компоненты в контейнер из своего метода onInitialized(), эта проблема не возникает. Но он не может быть проверен PMD, по крайней мере, не по встроенным правилам.

Я не думаю, что это дизайн недостаток, хотя. Это дизайн решение. Вы просто не можете основывать свой дизайн на инструментах статического анализа и предопределенных правилах. Удобство использования API также является важным аспектом дизайна, иногда даже более важным, чем принципы проектирования.

Например, принцип CQS (Command-Query Separation) бы предписывали, что метод, который делает что-то (изменения состояния) не должен ничего возвращать, и методы, которые возвращают что-то не должно быть никаких побочных эффектов (изменение состояния).

Если это было жесткое правило, плавные интерфейсы (методы, которые изменяют состояние объекта и возвращают this, позволяющие цепочку) не могут быть реализованы. Wicket использует его широко (почти все методы манипуляции с компонентами возвращают this), и это одна из тех вещей, которые делают ее радостью использовать.

PMD - очень полезный инструмент. Но вы должны быть мастером инструмента, а не его рабом. Вы должны учитывать его предупреждения как возможные проблемы, но если вы уверены в своих вариантах дизайна, просто отметьте код, который нужно обойти и быть счастливым.

1

Рассмотрим следующий класс:

public class A { 

    public A() { 
     System.out.println(val().toString()); 
    } 

    protected Integer val() { 
     return 0; 
    } 

} 

Это выглядит хорошо на первый взгляд, предположим, что val() не должны возвращать null, что это так. Теперь B подклассы A:

public class B extends A { 

    private final Integer i; 

    public B() { 
     //super(); //implicit 
     i = 1; 
    } 

    @Override 
    protected Integer val() { 
     return i; 
    } 
} 

Класс B отлично на первый взгляд, а - она ​​возвращает i от val() который никогда не null, так как это final при его инициализации в конструкторе. Однако создание B экземпляра будет кидать NullPointerException. Вы можете понять, почему? Подсказка: посмотрите, где неявно super() имеет место.

Считаете ли вы, что перемещение i инициализации поможет? Почему нет?

private final Integer i = 1; 

Как правило, никогда не вызывайте несобственные методы из конструктора не конечного класса. На самом деле я бы даже вызвал ошибку компиляции в этом случае. Как вы можете видеть, эта проблема не имеет ничего общего с Wicket и перемещение инициализации до onInitialize() - это избежать таких ловушек.

+0

Привет, спасибо за ваш ответ и полезную ссылку в комментариях, но я уже понимаю, почему при вызове переопределяемых методов в конструкторе плохо. Я пытаюсь выяснить, могут ли цепочки при вызове Initialize вызвать те же проблемы. – artbristol

+0

Конечно, переход к отдельному методу нарушает другой принцип, а именно, что после построения ваш объект должен всегда находиться в согласованном состоянии, и его методы должны быть вызваны в любом порядке, если это не будет выполнено. – biziclop

0

Мы имеем проблему рода, которые появляются, когда вы звоните переопределение методов в конструкторе, но, кажется, проистекают из того факта, что сама Калитка называет один (не может найти строку источника точной, но onInitialize(), метод overridable, заканчивается тем, что вызывается при вызове add() в конструкторе).

Я довольно уверен onInitialize() метод, который вызывается при вызове add(component) является onInitialize() метода компоненты добавляются (аргумент метода надстройки), а не из класса вы в настоящее время строите. Это должно быть хорошо, поскольку этот компонент уже полностью построен.

+0

См. Пример кода, чтобы узнать, как вы можете активировать 'onInitialized' после вызова' add'. – artbristol