SlideShare ist ein Scribd-Unternehmen logo
1 von 107
Part 1

Daan van Etten
     October 29, 2010
I think most programmers spend
the first 5 years of their career
mastering complexity, and the rest
of their lives learning simplicity.

                        — BUZZ ANDERSEN
Architecture   Reusability and
overview       maintainability

               Layout / logic
Old to new
               separation

BluePrint      Resources
Old to new
Controllers
Controllers
Controllers

@SpringBean
@SpringBean
Do not use SpringBean in generic UI components.
 (try a constructor argument instead)
Do not use SpringBean in generic UI components.
 (try a constructor argument instead)


(If you do, the @SpringBean will be a hidden
dependency when you add a generic component)
Only use the ‘name’ argument when there
are multiple beans that implement the same
interface.

@SpringBean (name = “selector”)
private GenericService m_genericService;
public class SomePanel extends Panel {

	 @SpringBean
	 private UserService m_userService;

	 @SpringBean
	 private TranslationService m_translationService;
HTML+CSS
for high level layout
24 columns
<wicket:extend>
	 <h1 class="title">Settings</h1>
	
	 <div class="span-12">
	 	 <div wicket:id="personaldetails"></div>
	 </div>
	
	 <div class="span-12 last">
	 	 <div wicket:id="emailnotification"></div>
	 </div>
	
	 <div class="span-12">
	 	 <div wicket:id="changePassword"></div>
	 </div>
	 	 	
	 <div class="span-12 last">	
	 	 <div wicket:id="languagetimezone"></div>
	 </div>	 	
</wicket:extend>
http://www.mdaines.com/plumb/
http://www.blueprintcss.org/tests/parts/grid.html
Do not assume too much...
Do not assume too much...
  For example: do not assume that your
  panel is always used with 4 columns.
Reusability
Maintainability
Principles of Object Oriented Design
Principles of Object Oriented Design

         The first five principles are principles of class design:

         SRP
         The Single Responsibility Principle
         A class should have one, and only one, reason to change.

         OCP
         The Open Closed Principle
         You should be able to extend a classes behavior, without modifying it.

         LSP
         The Liskov Substitution Principle
         Derived classes must be substitutable for their base classes.

         DIP
         The Dependency Inversion Principle
         Depend on abstractions, not on concretions.

         ISP
         The Interface Segregation Principle
         Make fine grained interfaces that are client specific.
Composition over Inheritance
Tools for clear responsibilities


  Page
  Panel
  Form
  Validator
  Renderer
  DataProvider
  Behavior
Tools for clear responsibilities


  Page           Provides high level layout




               only reusable by extending, so
          do not implement reusable logic in Page!
Tools for clear responsibilities


  Page            Provides high level layout

  Panel           Implement logic of a clearly separated part of a page




                Panel is very good for reuse!

          Just make sure your interface is clear, and
            your Panel does not contain too much
                         knowledge.
Tools for clear responsibilities


  Page            Provides high level layout

  Panel           Implement logic of a clearly separated part of a page




               Panel is very good for reuse!

               The interface of a Panel is the
              combination of all the public and
                    protected methods.
                    Think about reuse!
Panel

                  “a class should do one thing”



  Single Responsibility Principle
  In the context of the Single Responsibility Principle,
  responsibility is “a reason for change.” If you can think of
  more than one motive for changing a class, then that class
  has more than one responsibility. Another way to put it:
  “a class should do one thing (and be good at it)”




  https://wiki.cordys.com/display/edv/How+to+write+a+good+Wicket+component
public class ChangePasswordBlock extends HeaderLayoutBlockPanel {

	   public ChangePasswordBlock(String id) {
	   	   super(id, new ResourceModel(PreferencesUITranslations.CHANGE_PASSWORD));

	   	    FeedbackPanel feedbackPanel = new FeedbackPanel("feedbackPanel");
	   	    feedbackPanel.setOutputMarkupId(true);

	    	   Form<ChangePassword> passwordForm = createPasswordForm("passwordForm", feedbackPanel);
	    	   add(passwordForm);
	    	   passwordForm.add(feedbackPanel);
[..]
	    	   PasswordTextField confirmNewPassword = new PasswordTextField("confirmNewPassword");
	    	   confirmNewPassword.add(new RequiredValidator<String>());
	    	   passwordForm.add(confirmNewPassword);

	   	    passwordForm.add(new EqualPasswordInputValidator(newPassword, confirmNewPassword));
	   	    passwordForm.add(new DefaultAjaxFallbackButton("changePasswordButton", passwordForm));
	   }

	   private Form<ChangePassword> createPasswordForm(String id, final Component feedbackPanel) {
	   	   return new Form<ChangePassword>(id, new CompoundPropertyModel<ChangePassword>(new ChangePassword())) {
	   	   	    protected void onError() {
	   	   	    	   WicketUtil.refresh(AjaxRequestTarget.get(), feedbackPanel);
	   	   	    };

	   	    	    protected void onSubmit() {
	   	    	    	   changePassword(getModel(), this);
	   	    	    	   WicketUtil.refresh(AjaxRequestTarget.get(), feedbackPanel);
	   	    	    };
	   	    };
	   }

	   private void changePassword(IModel<ChangePassword> changedModel, Component feedbackComponent) {
	   	    ChangePassword changed = changedModel.getObject();
	   	    try {
	   	    	   m_userService.changePasswordForCurrentUser(changed.getCurrentPassword(),
	   	    	   	    	   changed.getNewPassword());
	   	    	   feedbackComponent.info(new ResourceModel
(PreferencesUITranslations.CHANGE_PASSWORD_SUCCESSFULL).getObject());
	   	    } catch (BadCredentialsException e) {
	   	    	   feedbackComponent.error(new ResourceModel(
	   	    	   	    	   PreferencesUITranslations.CURRENT_PASSWORD_INCORRECT).getObject());
	   	    }
	   }
Tools for clear responsibilities


  Page           Provides high level layout

  Panel          Implement logic of a clearly separated part of a page
                 Form, put your FeedbackPanel in here and handle
  Form           logic of submitting and refreshing FeedbackPanel.


              Think: should your Form really
             implement the Save of an object?
Tools for clear responsibilities


  Page             Provides high level layout

  Panel            Implement logic of a clearly separated part of a page
                   Form, put your FeedbackPanel in here and handle
  Form             logic of submitting and refreshing FeedbackPanel.


               Think: should your Form really
              implement the Save of an object?


            Is reuse possible if you want the same Form for:

               creating new object / edit existing object?
Tools for clear responsibilities


  Page           Provides high level layout

  Panel          Implement logic of a clearly separated part of a page
                 Form, put your FeedbackPanel in here and handle
  Form           logic of submitting and refreshing FeedbackPanel
  Validator      Handle validation of a FormComponent or a Form
Tools for clear responsibilities


  Page           Provides high level layout

  Panel          Implement logic of a clearly separated part of a page
                 Form, put your FeedbackPanel in here and handle
  Form           logic of submitting and refreshing FeedbackPanel
  Validator      Handle validation of a FormComponent or a Form



           Validations should only do Validation.
Tools for clear responsibilities


  Page           Provides high level layout

  Panel          Implement logic of a clearly separated part of a page
                 Form, put your FeedbackPanel in here and handle
  Form           logic of submitting and refreshing FeedbackPanel
  Validator      Handle validation of a FormComponent or a Form



           Validations should only do Validation.

              Have a clear separation between
                 Validation and Behavior!
Tools for clear responsibilities


  Page           Provides high level layout

  Panel          Implement logic of a clearly separated part of a page
                 Form, put your FeedbackPanel in here and handle
  Form           logic of submitting and refreshing FeedbackPanel
  Validator      Handle validation of a FormComponent or a Form

  Renderer       Render a given Object to a string representation
Tools for clear responsibilities


  Page           Provides high level layout

  Panel          Implement logic of a clearly separated part of a page
                 Form, put your FeedbackPanel in here and handle
  Form           logic of submitting and refreshing FeedbackPanel
  Validator      Handle validation of a FormComponent or a Form

  Renderer       Render a given Object to a string representation



           Renderer: do rendering only! No logic,
              no filtering, nothing. Just render.
Tools for clear responsibilities


  Page           Provides high level layout

  Panel          Implement logic of a clearly separated part of a page
                 Form, put your FeedbackPanel in here and handle
  Form           logic of submitting and refreshing FeedbackPanel
  Validator      Handle validation of a FormComponent or a Form

  Renderer       Render a given Object to a string representation

  DataProvider   Provide the right data based on given input.
Tools for clear responsibilities


  Page           Provides high level layout

  Panel          Implement logic of a clearly separated part of a page
                 Form, put your FeedbackPanel in here and handle
  Form           logic of submitting and refreshing FeedbackPanel
  Validator      Handle validation of a FormComponent or a Form

  Renderer       Render a given Object to a string representation

  DataProvider   Provide the right data based on given input.

               No rendering, no updating of
                FeedbackPanels or Toolbar.
Tools for clear responsibilities


  Page           Provides high level layout

  Panel          Implement logic of a clearly separated part of a page
                 Form, put your FeedbackPanel in here and handle
  Form           logic of submitting and refreshing FeedbackPanel
  Validator      Handle validation of a FormComponent or a Form

  Renderer       Render a given Object to a string representation

  DataProvider   Provide the right data based on given input.

  Behavior       Behaviors are plugins for components. They can
                 provide custom markup, custom pieces of logic.
                 Think of them as decorators.
Tools for clear responsibilities: Behaviors

   All Known Implementing Classes:
   AbstractAjaxBehavior, AbstractAjaxTimerBehavior,
   AbstractAutoCompleteBehavior, AbstractBehavior,
   AbstractDefaultAjaxBehavior, AbstractHeaderContributor,
   AbstractTransformerBehavior, AjaxEditableLabel.EditorAjaxBehavior,
   AjaxEditableLabel.LabelAjaxBehavior, AjaxEventBehavior,
   AjaxFormChoiceComponentUpdatingBehavior,
   AjaxFormComponentUpdatingBehavior, AjaxFormSubmitBehavior,
   AjaxFormValidatingBehavior, AjaxIndicatorAppender,
   AjaxPagingNavigationBehavior, AjaxSelfUpdatingTimerBehavior,
   AttributeAppender, AttributeModifier, AutoCompleteBehavior,
   BodyTagAttributeModifier, ContainerWithAssociatedMarkupHelper,
   ContextPathGenerator, DatePicker, HeaderContributor, OnChangeAjaxBehavior,
   OrderByLink.CssModifier, SimpleAttributeModifier, StringHeaderContributor,
   TextTemplateHeaderContributor, VelocityContributor,
   VelocityHeaderContributor, VelocityJavascriptContributor,
   WicketAjaxIndicatorAppender, WicketMessageTagHandler.AttributeLocalizer,
   XsltTransformerBehavior
Tools for clear responsibilities: Behaviors

          Learn to write your own in 4 steps!

                1. Choose the most appropriate base class


   All Known Implementing Classes:
   AbstractAjaxBehavior, AbstractAjaxTimerBehavior, AbstractAutoCompleteBehavior, AbstractBehavior,
   AbstractDefaultAjaxBehavior, AbstractHeaderContributor, AbstractTransformerBehavior,
   AjaxEditableLabel.EditorAjaxBehavior, AjaxEditableLabel.LabelAjaxBehavior, AjaxEventBehavior,
   AjaxFormChoiceComponentUpdatingBehavior, AjaxFormComponentUpdatingBehavior, AjaxFormSubmitBehavior,
   AjaxFormValidatingBehavior, AjaxIndicatorAppender, AjaxPagingNavigationBehavior,
   AjaxSelfUpdatingTimerBehavior, AttributeAppender, AttributeModifier, AutoCompleteBehavior,
   BodyTagAttributeModifier, ContainerWithAssociatedMarkupHelper, ContextPathGenerator, DatePicker,
   HeaderContributor, OnChangeAjaxBehavior, OrderByLink.CssModifier, SimpleAttributeModifier,
   StringHeaderContributor, TextTemplateHeaderContributor, VelocityContributor, VelocityHeaderContributor,
   VelocityJavascriptContributor, WicketAjaxIndicatorAppender, WicketMessageTagHandler.AttributeLocalizer,
   XsltTransformerBehavior
Tools for clear responsibilities: Behaviors

      Learn to write your own in 4 steps!

         2. Extend from your chosen base class

    public class MyBehavior extends AbstractBehavior {
    ..
    }
Tools for clear responsibilities: Behaviors

      Learn to write your own in 4 steps!

         3. Decide which methods to implement
Tools for clear responsibilities: Behaviors

             Learn to write your own in 4 steps!

                   3. Decide which methods to implement
 afterRender(Component component)
           Called when a component that has this behavior
 coupled was rendered.
Tools for clear responsibilities: Behaviors

             Learn to write your own in 4 steps!

                    3. Decide which methods to implement
 afterRender(Component component)
           Called when a component that has this behavior
 coupled was rendered.

 bind(Component component)
           Bind this handler to the given component.
Tools for clear responsibilities: Behaviors

             Learn to write your own in 4 steps!

                    3. Decide which methods to implement
 afterRender(Component component)
           Called when a component that has this behavior
 coupled was rendered.

 bind(Component component)
           Bind this handler to the given component.



 beforeRender(Component component)
           Called when a component is about to render.
Tools for clear responsibilities: Behaviors

             Learn to write your own in 4 steps!

                    3. Decide which methods to implement
 afterRender(Component component)
           Called when a component that has this behavior
 coupled was rendered.

 bind(Component component)
           Bind this handler to the given component.



 beforeRender(Component component)
           Called when a component is about to render.



 cleanup()
           This method is called either by onRendered
 (Component) or onException(Component,
 RuntimeException) AFTER they called their respective
 template methods.
Tools for clear responsibilities: Behaviors

             Learn to write your own in 4 steps!

                    3. Decide which methods to implement
 afterRender(Component component)                           onRendered(Component component)
           Called when a component that has this behavior             Called when a component that has this behavior
 coupled was rendered.                                      coupled was rendered.

 bind(Component component)
           Bind this handler to the given component.



 beforeRender(Component component)
           Called when a component is about to render.



 cleanup()
           This method is called either by onRendered
 (Component) or onException(Component,
 RuntimeException) AFTER they called their respective
 template methods.
Tools for clear responsibilities: Behaviors

             Learn to write your own in 4 steps!

                    3. Decide which methods to implement
 afterRender(Component component)                           onRendered(Component component)
           Called when a component that has this behavior             Called when a component that has this behavior
 coupled was rendered.                                      coupled was rendered.

 bind(Component component)                                  isTemporary()
           Bind this handler to the given component.                  Specifies whether or not this behavior is temporary.



 beforeRender(Component component)
           Called when a component is about to render.



 cleanup()
           This method is called either by onRendered
 (Component) or onException(Component,
 RuntimeException) AFTER they called their respective
 template methods.
Tools for clear responsibilities: Behaviors

             Learn to write your own in 4 steps!

                    3. Decide which methods to implement
 afterRender(Component component)                           onRendered(Component component)
           Called when a component that has this behavior             Called when a component that has this behavior
 coupled was rendered.                                      coupled was rendered.

 bind(Component component)                                  isTemporary()
           Bind this handler to the given component.                  Specifies whether or not this behavior is temporary.


                                                            onComponentTag(Component component,
 beforeRender(Component component)                          ComponentTag tag)
           Called when a component is about to render.                Called any time a component that has this behavior
                                                            registered is rendering the component tag.

 cleanup()
           This method is called either by onRendered
 (Component) or onException(Component,
 RuntimeException) AFTER they called their respective
 template methods.
Tools for clear responsibilities: Behaviors

             Learn to write your own in 4 steps!

                    3. Decide which methods to implement
 afterRender(Component component)                           onRendered(Component component)
           Called when a component that has this behavior             Called when a component that has this behavior
 coupled was rendered.                                      coupled was rendered.

 bind(Component component)                                  isTemporary()
           Bind this handler to the given component.                  Specifies whether or not this behavior is temporary.


                                                            onComponentTag(Component component,
 beforeRender(Component component)                          ComponentTag tag)
           Called when a component is about to render.                Called any time a component that has this behavior
                                                            registered is rendering the component tag.

 cleanup()
                                                            detach(Component component)
           This method is called either by onRendered
 (Component) or onException(Component,                                Allows the behavior to detach any state it has
 RuntimeException) AFTER they called their respective       attached during request processing.
 template methods.
Tools for clear responsibilities: Behaviors

           Learn to write your own in 4 steps!

                4. Implement your behavior!
public class AjaxFocusBehavior extends AbstractBehavior {
	   private Component m_component;

	   @Override
	   public void bind(Component component) {
    this.m_component = component;
    component.setOutputMarkupId(true);
	   }

	   @Override
	   public void renderHead(IHeaderResponse response) {
	   	   super.renderHead(response);
    response.renderOnLoadJavascript("document.getElementById('" + m_component.getMarkupId() + "').focus()");
	   }

	   /**
	     * {@inheritDoc}
	     */
	   @Override
	   public boolean isTemporary() {
	   	    return true;
	   }
Composing an AutoCompleteField




AutoCompleteField<Long, Pizza> autocomplete = new AutoCompleteField<Long, Pizza>(id, model, choicesProvider,
                                                                                 renderer, settings);
Composing an AutoCompleteField


AutoCompleteChoicesProvider<Long, Pizza> choicesProvider = new PizzaAutoCompleteChoicesProvider
(m_pizzaService);



AutoCompleteField<Long, Pizza> autocomplete = new AutoCompleteField<Long, Pizza>(id, model, choicesProvider,
                                                                                 renderer, settings);
Composing an AutoCompleteField


AutoCompleteChoicesProvider<Long, Pizza> choicesProvider = new PizzaAutoCompleteChoicesProvider
(m_pizzaService);
PizzaAutoSuggestRenderer renderer = new PizzaAutoSuggestRenderer();

AutoCompleteField<Long, Pizza> autocomplete = new AutoCompleteField<Long, Pizza>(id, model, choicesProvider,
                                                                                 renderer, settings);
Composing an AutoCompleteField


AutoCompleteChoicesProvider<Long, Pizza> choicesProvider = new PizzaAutoCompleteChoicesProvider
(m_pizzaService);
PizzaAutoSuggestRenderer renderer = new PizzaAutoSuggestRenderer();

AutoCompleteField<Long, Pizza> autocomplete = new AutoCompleteField<Long, Pizza>(id, model, choicesProvider,
                                                                                 renderer, settings);

renderer.setOnSelectJavascriptProvider(autocomplete);
Composing an AutoCompleteField


AutoCompleteChoicesProvider<Long, Pizza> choicesProvider = new PizzaAutoCompleteChoicesProvider
(m_pizzaService);
PizzaAutoSuggestRenderer renderer = new PizzaAutoSuggestRenderer();

AutoCompleteField<Long, Pizza> autocomplete = new AutoCompleteField<Long, Pizza>(id, model, choicesProvider,
                                                                                 renderer, settings);

renderer.setOnSelectJavascriptProvider(autocomplete);

autocomplete.add(new PlaceHolderTextBehavior(Model.of("Search for pizza...")));
Composing an AutoCompleteField


AutoCompleteChoicesProvider<Long, Pizza> choicesProvider = new PizzaAutoCompleteChoicesProvider
(m_pizzaService);
PizzaAutoSuggestRenderer renderer = new PizzaAutoSuggestRenderer();

AutoCompleteField<Long, Pizza> autocomplete = new AutoCompleteField<Long, Pizza>(id, model, choicesProvider,
                                                                                 renderer, settings);

renderer.setOnSelectJavascriptProvider(autocomplete);

autocomplete.add(new PlaceHolderTextBehavior(Model.of("Search for pizza...")));
autocomplete.add(new AjaxSelectTextOnClickBehavior());
Composing an AutoCompleteField


AutoCompleteChoicesProvider<Long, Pizza> choicesProvider = new PizzaAutoCompleteChoicesProvider
(m_pizzaService);
PizzaAutoSuggestRenderer renderer = new PizzaAutoSuggestRenderer();

AutoCompleteField<Long, Pizza> autocomplete = new AutoCompleteField<Long, Pizza>(id, model, choicesProvider,
                                                                                 renderer, settings);

renderer.setOnSelectJavascriptProvider(autocomplete);

autocomplete.add(new PlaceHolderTextBehavior(Model.of("Search for pizza...")));
autocomplete.add(new AjaxSelectTextOnClickBehavior());
autocomplete.add(new RequiredValidator<Pizza>());
Give your classes clear names.


 If it’s a page, call it Page
  (example: LoginPage instead of Login)


If it’s a panel, call it Panel
  (example: LoginPanel instead of Login)




The same goes for Form,
Validator, Renderer, etcetera.
Use wicket:message instead of Label
     (for simple translations)
Use wicket:message instead of Label
     (for simple translations)

  <wicket:message key="SELECT_LANGUAGE_LABEL"/>
Use wicket:message instead of Label
     (for simple translations)

   <wicket:message key="SELECT_LANGUAGE_LABEL"/>




Put this in your HTML and it will be replaced by
Wicket for the translation in your property file.
Use wicket:message instead of Label
      (for simple translations)

<input type="button" wicket:id="someid" wicket:message="value:CHANGE" />
Use wicket:message instead of Label
      (for simple translations)

<input type="button" wicket:id="someid" wicket:message="value:CHANGE" />




         Wicket will add a ‘value’ attribute and
             set it to the localized value!
Smells
http://xkcd.com/221/
Smell 1

      Class names containing:


                                  *With*

                               *Without*


 public class CheckBoxWithoutLabel extends CheckBox
 public class CheckBox extends LabeledComponent<CheckBoxWithoutLabel>

                      Duplicated code hierarchies are ugly...
LabelWithLabel:

 LabeledComponent<Label> lbl = new
 LabeledComponent<Label>(property,
 dopmd.getTranslationKey(), position, label);




               (In DOMRComponentFactory...)
Yes, we also have something like a

LabelWithoutLabel:
 position = LabelPosition.NONE;


 LabeledComponent<Label> lbl = new
 LabeledComponent<Label>(property,
 dopmd.getTranslationKey(), position, label);
Smell 2

                               instanceof


          @Override
          public MarkupContainer setDefaultModel(IModel<?> model) {
     	   	 IModel<String> model1 = new Model<String>();
     	   	 if (model != null && !(model instanceof DOMRObjectModel)) {

     	   	   	 Revision revision = (Revision) model.getObject();
     	   	   	 if (revision != null) {
     	   	   	 	 model1.setObject(revision.getId().toString());
     	        	 }
     	   	   	 return super.setDefaultModel(model1);
     	   	   }
     	        return super.setDefaultModel(model);
         }

                                CatalogAutoSuggestField
Smell 3


                        many constructors


     public class CheckBox extends LabeledComponent<CheckBoxWithoutLabel> {
     	   private static final long serialVersionUID = -8246992163993958051L;
Smell 3


                                    many constructors


               public class CheckBox extends LabeledComponent<CheckBoxWithoutLabel> {
               	   private static final long serialVersionUID = -8246992163993958051L;




	   public CheckBox(String translationKey, IModel<Boolean> model) {
	   	   this(null, translationKey, model);
	   }
Smell 3


                                    many constructors


               public class CheckBox extends LabeledComponent<CheckBoxWithoutLabel> {
               	   private static final long serialVersionUID = -8246992163993958051L;




	   public CheckBox(String id, String translationKey, IModel<Boolean> model) {
	   	   this(id, translationKey, LabelPosition.RIGHT, model);
	   }
Smell 3


                                    many constructors


               public class CheckBox extends LabeledComponent<CheckBoxWithoutLabel> {
               	   private static final long serialVersionUID = -8246992163993958051L;




	   public CheckBox(String translationKey, LabelPosition position, IModel<Boolean> model) {
	   	   this(null, translationKey, position, model);
	   }
Smell 3


                                    many constructors


               public class CheckBox extends LabeledComponent<CheckBoxWithoutLabel> {
               	   private static final long serialVersionUID = -8246992163993958051L;




	   public CheckBox(String id, String translationKey, LabelPosition position, IModel<Boolean> model) {
	   	   super(id, translationKey, position, new CheckBoxWithoutLabel(id + "CheckBox", model));
	   	   getLabel().addCssClass("checkBoxWithLabel");
	   }
Smell 3


                                    many constructors


               public class CheckBox extends LabeledComponent<CheckBoxWithoutLabel> {
               	   private static final long serialVersionUID = -8246992163993958051L;




	   public CheckBox(IModel<Boolean> model) {
	   	   this(null, model);
	   }
Smell 4


                        getPage() calls




     Page page = (Page) event.getPage();
     event.refreshComponents(m_grid, page.getFeedbackPanel());
Smell 5


            FeedbackPanel not inside Form




     Page page = (Page) event.getPage();
     event.refreshComponents(m_grid, page.getFeedbackPanel());
Smell 5


          Refreshing FeedbackPanel, Toolbar or
         other classes from unexpected locations



     // Refresh the toolbar
     if (getPage() instanceof Page) {
     	   ToolBar tb = (ToolBar) ((Page) getPage()).getComponent("toolbar");

     	    if (tb != null) {
     	    	   target.addComponent(tb);
     	    }
     }
Smell 6


                                  Casting




     // Refresh the toolbar
     if (getPage() instanceof Page) {
     	   ToolBar tb = (ToolBar) ((Page) getPage()).getComponent("toolbar");

     	   if (tb != null) {
     	   	   target.addComponent(tb);
     	   }
     }
Smell 7


                             Using getParent()

    	   public void setButtonBar(ButtonBar buttonBar) {
    	   	   if (!buttonBar.getId().equals(s_buttonPanel)) {
    	   	   	   throw new WicketRuntimeException("Buttonbar id is wrong.");
    	   	   }

    	   	   if   (m_buttonBarPanel.getParent() == null) {
    	   	   	     if (m_form.get(s_buttonPanel) != null) {
    	   	   	     	   m_form.get(s_buttonPanel)
    	   	   	     	   	     .replaceWith(m_buttonBarPanel);
    	   	   	     } else {
    	   	   	     	   m_form.add(m_buttonBarPanel);
    	   	   	     }
    	   	   }

    	   	   m_buttonBarPanel.replaceWith(buttonBar);
    	   }
Smell 8


                     Getting components
                       from the Page




   ToolBar tb = (ToolBar) ((Page) getPage()).getComponent("toolbar");
Smell 9


                      Doing layout and styling in Java



 	   	   ColumnLayoutContainer pptfContainer = new ColumnLayoutContainer("proofPaidTuitionFeeContainer")
 	   	   	   .addComponent(sourceLabel, Size.percent(30))
 	   	   	   .addComponent(amountLabel, Size.percent(30))
 	   	   	   .addComponent(factory.createDateTimeLabel("dateTime", m_dateTimeModel,
                  LabelPosition.TOP, m_controller.getTimeZoneCurrentUser()))
 	   	   	   .newLine();
Smell 10


                 Using onBeforeRender() too often
 	   protected void onBeforeRender() {
 	   	   super.onBeforeRender();

 	   	   if (isEnabled()) {
 	   	   	   AjaxRequestTarget target = AjaxRequestTarget.get();
 	   	   	   if (target != null) {
 	   	   	   	   target.appendJavascript(TextTemplateHeaderContributor.forPlainJs(s_textTemplate,
 	   	   	   	   	   	   getHeaderContributor(false)).toString());
 	   	   	   } else {
 	   	   	   	   add(TextTemplateHeaderContributor.forJavaScript(s_textTemplate,
 getHeaderContributor(true)));
 	   	   	   }
 	   	   }
 	   }


                           tip: check onConfigure()
                          (and in this case: check IHeaderContributor)
Smell 11


           Calling ResourceModel.getObject()

   feedback.info(new ResourceModel(XTranslation.PROPERTY).getObject());
Smell 11


           Calling ResourceModel.getObject()

   feedback.info(new ResourceModel(XTranslation.PROPERTY).getObject());
Smell 11


           Calling ResourceModel.getObject()

   feedback.info(new ResourceModel(XTranslation.PROPERTY).getObject());


   feedback.info(getString(XTranslation.PROPERTY));
Smell 11


           Calling ResourceModel.getObject()

   feedback.info(new ResourceModel(XTranslation.PROPERTY).getObject());


   feedback.info(getString(XTranslation.PROPERTY));




           Call getString only outside constructor!
                (check methods that are called by the constructor)
Smell 11


                      Calling ResourceModel.getObject()

         feedback.info(new ResourceModel(XTranslation.PROPERTY).getObject());


         feedback.info(getString(XTranslation.PROPERTY));




                       Call getString only outside constructor!
                                (check methods that are called by the constructor)

 Tried to retrieve a localized string for a component that has not yet been added to the page. This can sometimes lead to an invalid or
 no localized resource returned. Make sure you are not calling Component#getString() inside your Component's constructor. Offending
 component: [MarkupContainer [Component id = panelmenu, page = <No Page>, path = panelmenu.PanelMenu]]
Smell 12


           Hidden Javascript dependencies



     AjaxRequestTarget target = AjaxRequestTarget.get();
     if (target != null) {
     	 target.appendJavascript("clickFeedbackRefreshLink();");
     }
Smell 13


            Transient fields

           (expect Exceptions!)
Resources
Groups
Wicket
                    Mailing List
http://wicket.apache.org/help/email.html
Users list - Archives - Search - Subscribe - Unsubscribe - Help
Wicket
                             examples
Not always a good example of code quality, but get ideas from:
http://wicketstuff.org/wicket14/index.html
Wicket
                    Wiki
https://cwiki.apache.org/WICKET/
Reference
                        library
https://cwiki.apache.org/WICKET/reference-library.html

Weitere ähnliche Inhalte

Ähnlich wie Part 1 overview and principles

Java EE 7 Recipes
Java EE 7 RecipesJava EE 7 Recipes
Java EE 7 RecipesJosh Juneau
 
Functional Components in Vue.js
Functional Components in Vue.jsFunctional Components in Vue.js
Functional Components in Vue.jsAustin Gil
 
what is context API and How it works in React.pptx
what is context API and How it works in React.pptxwhat is context API and How it works in React.pptx
what is context API and How it works in React.pptxBOSC Tech Labs
 
React for Dummies
React for DummiesReact for Dummies
React for DummiesMitch Chen
 
React Hooks Best Practices in 2022.pptx
React Hooks Best Practices in 2022.pptxReact Hooks Best Practices in 2022.pptx
React Hooks Best Practices in 2022.pptxBOSC Tech Labs
 
Common design principles and design patterns in automation testing
Common design principles and design patterns in automation testingCommon design principles and design patterns in automation testing
Common design principles and design patterns in automation testingKMS Technology
 
1) workbench basics
1) workbench basics1) workbench basics
1) workbench basicstechbed
 
Rc085 010d-vaadin7
Rc085 010d-vaadin7Rc085 010d-vaadin7
Rc085 010d-vaadin7Cosmina Ivan
 
Better User Experience with .NET
Better User Experience with .NETBetter User Experience with .NET
Better User Experience with .NETPeter Gfader
 
Metamorphosis from Forms to Java: A technical lead's perspective, part II
Metamorphosis from Forms to Java:  A technical lead's perspective, part IIMetamorphosis from Forms to Java:  A technical lead's perspective, part II
Metamorphosis from Forms to Java: A technical lead's perspective, part IIMichael Fons
 
Salesforce API: Salesforce Console Deep Dive
Salesforce API: Salesforce Console Deep DiveSalesforce API: Salesforce Console Deep Dive
Salesforce API: Salesforce Console Deep DiveSalesforce Developers
 
POS 408  Focus Dreams/newtonhelp.com
POS 408  Focus Dreams/newtonhelp.comPOS 408  Focus Dreams/newtonhelp.com
POS 408  Focus Dreams/newtonhelp.commyblue69
 
POS 408 Creative and Effective/newtonhelp.com
POS 408 Creative and Effective/newtonhelp.comPOS 408 Creative and Effective/newtonhelp.com
POS 408 Creative and Effective/newtonhelp.commyblue99
 
POS 408 Extraordinary Life/newtonhelp.com 
POS 408 Extraordinary Life/newtonhelp.com POS 408 Extraordinary Life/newtonhelp.com 
POS 408 Extraordinary Life/newtonhelp.com myblue39
 
Define and Manage Requirements with IBM Rational Requirements Composer
Define and Manage Requirements with IBM Rational Requirements ComposerDefine and Manage Requirements with IBM Rational Requirements Composer
Define and Manage Requirements with IBM Rational Requirements ComposerAlan Kan
 
Patterns (contd)Software Development ProcessDesign patte.docx
Patterns (contd)Software Development ProcessDesign patte.docxPatterns (contd)Software Development ProcessDesign patte.docx
Patterns (contd)Software Development ProcessDesign patte.docxdanhaley45372
 

Ähnlich wie Part 1 overview and principles (20)

Java EE 7 Recipes
Java EE 7 RecipesJava EE 7 Recipes
Java EE 7 Recipes
 
Functional Components in Vue.js
Functional Components in Vue.jsFunctional Components in Vue.js
Functional Components in Vue.js
 
React patterns
React patternsReact patterns
React patterns
 
what is context API and How it works in React.pptx
what is context API and How it works in React.pptxwhat is context API and How it works in React.pptx
what is context API and How it works in React.pptx
 
React a11y-csun
React a11y-csunReact a11y-csun
React a11y-csun
 
React for Dummies
React for DummiesReact for Dummies
React for Dummies
 
React Hooks Best Practices in 2022.pptx
React Hooks Best Practices in 2022.pptxReact Hooks Best Practices in 2022.pptx
React Hooks Best Practices in 2022.pptx
 
Common design principles and design patterns in automation testing
Common design principles and design patterns in automation testingCommon design principles and design patterns in automation testing
Common design principles and design patterns in automation testing
 
React-JS.pptx
React-JS.pptxReact-JS.pptx
React-JS.pptx
 
1) workbench basics
1) workbench basics1) workbench basics
1) workbench basics
 
Rc085 010d-vaadin7
Rc085 010d-vaadin7Rc085 010d-vaadin7
Rc085 010d-vaadin7
 
Better User Experience with .NET
Better User Experience with .NETBetter User Experience with .NET
Better User Experience with .NET
 
Metamorphosis from Forms to Java: A technical lead's perspective, part II
Metamorphosis from Forms to Java:  A technical lead's perspective, part IIMetamorphosis from Forms to Java:  A technical lead's perspective, part II
Metamorphosis from Forms to Java: A technical lead's perspective, part II
 
Salesforce API: Salesforce Console Deep Dive
Salesforce API: Salesforce Console Deep DiveSalesforce API: Salesforce Console Deep Dive
Salesforce API: Salesforce Console Deep Dive
 
POS 408  Focus Dreams/newtonhelp.com
POS 408  Focus Dreams/newtonhelp.comPOS 408  Focus Dreams/newtonhelp.com
POS 408  Focus Dreams/newtonhelp.com
 
POS 408 Creative and Effective/newtonhelp.com
POS 408 Creative and Effective/newtonhelp.comPOS 408 Creative and Effective/newtonhelp.com
POS 408 Creative and Effective/newtonhelp.com
 
POS 408 Extraordinary Life/newtonhelp.com 
POS 408 Extraordinary Life/newtonhelp.com POS 408 Extraordinary Life/newtonhelp.com 
POS 408 Extraordinary Life/newtonhelp.com 
 
Lec9Handout.ppt
Lec9Handout.pptLec9Handout.ppt
Lec9Handout.ppt
 
Define and Manage Requirements with IBM Rational Requirements Composer
Define and Manage Requirements with IBM Rational Requirements ComposerDefine and Manage Requirements with IBM Rational Requirements Composer
Define and Manage Requirements with IBM Rational Requirements Composer
 
Patterns (contd)Software Development ProcessDesign patte.docx
Patterns (contd)Software Development ProcessDesign patte.docxPatterns (contd)Software Development ProcessDesign patte.docx
Patterns (contd)Software Development ProcessDesign patte.docx
 

Kürzlich hochgeladen

How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyesHow to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyesThousandEyes
 
[Webinar] SpiraTest - Setting New Standards in Quality Assurance
[Webinar] SpiraTest - Setting New Standards in Quality Assurance[Webinar] SpiraTest - Setting New Standards in Quality Assurance
[Webinar] SpiraTest - Setting New Standards in Quality AssuranceInflectra
 
Zeshan Sattar- Assessing the skill requirements and industry expectations for...
Zeshan Sattar- Assessing the skill requirements and industry expectations for...Zeshan Sattar- Assessing the skill requirements and industry expectations for...
Zeshan Sattar- Assessing the skill requirements and industry expectations for...itnewsafrica
 
Moving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfMoving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfLoriGlavin3
 
Abdul Kader Baba- Managing Cybersecurity Risks and Compliance Requirements i...
Abdul Kader Baba- Managing Cybersecurity Risks  and Compliance Requirements i...Abdul Kader Baba- Managing Cybersecurity Risks  and Compliance Requirements i...
Abdul Kader Baba- Managing Cybersecurity Risks and Compliance Requirements i...itnewsafrica
 
QCon London: Mastering long-running processes in modern architectures
QCon London: Mastering long-running processes in modern architecturesQCon London: Mastering long-running processes in modern architectures
QCon London: Mastering long-running processes in modern architecturesBernd Ruecker
 
UiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to HeroUiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to HeroUiPathCommunity
 
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxA Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxLoriGlavin3
 
Emixa Mendix Meetup 11 April 2024 about Mendix Native development
Emixa Mendix Meetup 11 April 2024 about Mendix Native developmentEmixa Mendix Meetup 11 April 2024 about Mendix Native development
Emixa Mendix Meetup 11 April 2024 about Mendix Native developmentPim van der Noll
 
Potential of AI (Generative AI) in Business: Learnings and Insights
Potential of AI (Generative AI) in Business: Learnings and InsightsPotential of AI (Generative AI) in Business: Learnings and Insights
Potential of AI (Generative AI) in Business: Learnings and InsightsRavi Sanghani
 
So einfach geht modernes Roaming fuer Notes und Nomad.pdf
So einfach geht modernes Roaming fuer Notes und Nomad.pdfSo einfach geht modernes Roaming fuer Notes und Nomad.pdf
So einfach geht modernes Roaming fuer Notes und Nomad.pdfpanagenda
 
Varsha Sewlal- Cyber Attacks on Critical Critical Infrastructure
Varsha Sewlal- Cyber Attacks on Critical Critical InfrastructureVarsha Sewlal- Cyber Attacks on Critical Critical Infrastructure
Varsha Sewlal- Cyber Attacks on Critical Critical Infrastructureitnewsafrica
 
Design pattern talk by Kaya Weers - 2024 (v2)
Design pattern talk by Kaya Weers - 2024 (v2)Design pattern talk by Kaya Weers - 2024 (v2)
Design pattern talk by Kaya Weers - 2024 (v2)Kaya Weers
 
A Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersA Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersNicole Novielli
 
Scale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterScale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterMydbops
 
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...Wes McKinney
 
Bridging Between CAD & GIS: 6 Ways to Automate Your Data Integration
Bridging Between CAD & GIS:  6 Ways to Automate Your Data IntegrationBridging Between CAD & GIS:  6 Ways to Automate Your Data Integration
Bridging Between CAD & GIS: 6 Ways to Automate Your Data Integrationmarketing932765
 
Decarbonising Buildings: Making a net-zero built environment a reality
Decarbonising Buildings: Making a net-zero built environment a realityDecarbonising Buildings: Making a net-zero built environment a reality
Decarbonising Buildings: Making a net-zero built environment a realityIES VE
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsPixlogix Infotech
 
MuleSoft Online Meetup Group - B2B Crash Course: Release SparkNotes
MuleSoft Online Meetup Group - B2B Crash Course: Release SparkNotesMuleSoft Online Meetup Group - B2B Crash Course: Release SparkNotes
MuleSoft Online Meetup Group - B2B Crash Course: Release SparkNotesManik S Magar
 

Kürzlich hochgeladen (20)

How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyesHow to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
 
[Webinar] SpiraTest - Setting New Standards in Quality Assurance
[Webinar] SpiraTest - Setting New Standards in Quality Assurance[Webinar] SpiraTest - Setting New Standards in Quality Assurance
[Webinar] SpiraTest - Setting New Standards in Quality Assurance
 
Zeshan Sattar- Assessing the skill requirements and industry expectations for...
Zeshan Sattar- Assessing the skill requirements and industry expectations for...Zeshan Sattar- Assessing the skill requirements and industry expectations for...
Zeshan Sattar- Assessing the skill requirements and industry expectations for...
 
Moving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfMoving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdf
 
Abdul Kader Baba- Managing Cybersecurity Risks and Compliance Requirements i...
Abdul Kader Baba- Managing Cybersecurity Risks  and Compliance Requirements i...Abdul Kader Baba- Managing Cybersecurity Risks  and Compliance Requirements i...
Abdul Kader Baba- Managing Cybersecurity Risks and Compliance Requirements i...
 
QCon London: Mastering long-running processes in modern architectures
QCon London: Mastering long-running processes in modern architecturesQCon London: Mastering long-running processes in modern architectures
QCon London: Mastering long-running processes in modern architectures
 
UiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to HeroUiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to Hero
 
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxA Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
 
Emixa Mendix Meetup 11 April 2024 about Mendix Native development
Emixa Mendix Meetup 11 April 2024 about Mendix Native developmentEmixa Mendix Meetup 11 April 2024 about Mendix Native development
Emixa Mendix Meetup 11 April 2024 about Mendix Native development
 
Potential of AI (Generative AI) in Business: Learnings and Insights
Potential of AI (Generative AI) in Business: Learnings and InsightsPotential of AI (Generative AI) in Business: Learnings and Insights
Potential of AI (Generative AI) in Business: Learnings and Insights
 
So einfach geht modernes Roaming fuer Notes und Nomad.pdf
So einfach geht modernes Roaming fuer Notes und Nomad.pdfSo einfach geht modernes Roaming fuer Notes und Nomad.pdf
So einfach geht modernes Roaming fuer Notes und Nomad.pdf
 
Varsha Sewlal- Cyber Attacks on Critical Critical Infrastructure
Varsha Sewlal- Cyber Attacks on Critical Critical InfrastructureVarsha Sewlal- Cyber Attacks on Critical Critical Infrastructure
Varsha Sewlal- Cyber Attacks on Critical Critical Infrastructure
 
Design pattern talk by Kaya Weers - 2024 (v2)
Design pattern talk by Kaya Weers - 2024 (v2)Design pattern talk by Kaya Weers - 2024 (v2)
Design pattern talk by Kaya Weers - 2024 (v2)
 
A Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersA Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software Developers
 
Scale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterScale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL Router
 
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
 
Bridging Between CAD & GIS: 6 Ways to Automate Your Data Integration
Bridging Between CAD & GIS:  6 Ways to Automate Your Data IntegrationBridging Between CAD & GIS:  6 Ways to Automate Your Data Integration
Bridging Between CAD & GIS: 6 Ways to Automate Your Data Integration
 
Decarbonising Buildings: Making a net-zero built environment a reality
Decarbonising Buildings: Making a net-zero built environment a realityDecarbonising Buildings: Making a net-zero built environment a reality
Decarbonising Buildings: Making a net-zero built environment a reality
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and Cons
 
MuleSoft Online Meetup Group - B2B Crash Course: Release SparkNotes
MuleSoft Online Meetup Group - B2B Crash Course: Release SparkNotesMuleSoft Online Meetup Group - B2B Crash Course: Release SparkNotes
MuleSoft Online Meetup Group - B2B Crash Course: Release SparkNotes
 

Part 1 overview and principles

  • 1. Part 1 Daan van Etten October 29, 2010
  • 2.
  • 3.
  • 4. I think most programmers spend the first 5 years of their career mastering complexity, and the rest of their lives learning simplicity. — BUZZ ANDERSEN
  • 5.
  • 6. Architecture Reusability and overview maintainability Layout / logic Old to new separation BluePrint Resources
  • 12. Do not use SpringBean in generic UI components. (try a constructor argument instead)
  • 13. Do not use SpringBean in generic UI components. (try a constructor argument instead) (If you do, the @SpringBean will be a hidden dependency when you add a generic component)
  • 14. Only use the ‘name’ argument when there are multiple beans that implement the same interface. @SpringBean (name = “selector”) private GenericService m_genericService;
  • 15. public class SomePanel extends Panel { @SpringBean private UserService m_userService; @SpringBean private TranslationService m_translationService;
  • 17. for high level layout
  • 19. <wicket:extend> <h1 class="title">Settings</h1> <div class="span-12"> <div wicket:id="personaldetails"></div> </div> <div class="span-12 last"> <div wicket:id="emailnotification"></div> </div> <div class="span-12"> <div wicket:id="changePassword"></div> </div> <div class="span-12 last"> <div wicket:id="languagetimezone"></div> </div> </wicket:extend>
  • 22. Do not assume too much...
  • 23. Do not assume too much... For example: do not assume that your panel is always used with 4 columns.
  • 26. Principles of Object Oriented Design
  • 27. Principles of Object Oriented Design The first five principles are principles of class design: SRP The Single Responsibility Principle A class should have one, and only one, reason to change. OCP The Open Closed Principle You should be able to extend a classes behavior, without modifying it. LSP The Liskov Substitution Principle Derived classes must be substitutable for their base classes. DIP The Dependency Inversion Principle Depend on abstractions, not on concretions. ISP The Interface Segregation Principle Make fine grained interfaces that are client specific.
  • 29. Tools for clear responsibilities Page Panel Form Validator Renderer DataProvider Behavior
  • 30. Tools for clear responsibilities Page Provides high level layout only reusable by extending, so do not implement reusable logic in Page!
  • 31. Tools for clear responsibilities Page Provides high level layout Panel Implement logic of a clearly separated part of a page Panel is very good for reuse! Just make sure your interface is clear, and your Panel does not contain too much knowledge.
  • 32. Tools for clear responsibilities Page Provides high level layout Panel Implement logic of a clearly separated part of a page Panel is very good for reuse! The interface of a Panel is the combination of all the public and protected methods. Think about reuse!
  • 33. Panel “a class should do one thing” Single Responsibility Principle In the context of the Single Responsibility Principle, responsibility is “a reason for change.” If you can think of more than one motive for changing a class, then that class has more than one responsibility. Another way to put it: “a class should do one thing (and be good at it)” https://wiki.cordys.com/display/edv/How+to+write+a+good+Wicket+component
  • 34. public class ChangePasswordBlock extends HeaderLayoutBlockPanel { public ChangePasswordBlock(String id) { super(id, new ResourceModel(PreferencesUITranslations.CHANGE_PASSWORD)); FeedbackPanel feedbackPanel = new FeedbackPanel("feedbackPanel"); feedbackPanel.setOutputMarkupId(true); Form<ChangePassword> passwordForm = createPasswordForm("passwordForm", feedbackPanel); add(passwordForm); passwordForm.add(feedbackPanel); [..] PasswordTextField confirmNewPassword = new PasswordTextField("confirmNewPassword"); confirmNewPassword.add(new RequiredValidator<String>()); passwordForm.add(confirmNewPassword); passwordForm.add(new EqualPasswordInputValidator(newPassword, confirmNewPassword)); passwordForm.add(new DefaultAjaxFallbackButton("changePasswordButton", passwordForm)); } private Form<ChangePassword> createPasswordForm(String id, final Component feedbackPanel) { return new Form<ChangePassword>(id, new CompoundPropertyModel<ChangePassword>(new ChangePassword())) { protected void onError() { WicketUtil.refresh(AjaxRequestTarget.get(), feedbackPanel); }; protected void onSubmit() { changePassword(getModel(), this); WicketUtil.refresh(AjaxRequestTarget.get(), feedbackPanel); }; }; } private void changePassword(IModel<ChangePassword> changedModel, Component feedbackComponent) { ChangePassword changed = changedModel.getObject(); try { m_userService.changePasswordForCurrentUser(changed.getCurrentPassword(), changed.getNewPassword()); feedbackComponent.info(new ResourceModel (PreferencesUITranslations.CHANGE_PASSWORD_SUCCESSFULL).getObject()); } catch (BadCredentialsException e) { feedbackComponent.error(new ResourceModel( PreferencesUITranslations.CURRENT_PASSWORD_INCORRECT).getObject()); } }
  • 35. Tools for clear responsibilities Page Provides high level layout Panel Implement logic of a clearly separated part of a page Form, put your FeedbackPanel in here and handle Form logic of submitting and refreshing FeedbackPanel. Think: should your Form really implement the Save of an object?
  • 36. Tools for clear responsibilities Page Provides high level layout Panel Implement logic of a clearly separated part of a page Form, put your FeedbackPanel in here and handle Form logic of submitting and refreshing FeedbackPanel. Think: should your Form really implement the Save of an object? Is reuse possible if you want the same Form for: creating new object / edit existing object?
  • 37. Tools for clear responsibilities Page Provides high level layout Panel Implement logic of a clearly separated part of a page Form, put your FeedbackPanel in here and handle Form logic of submitting and refreshing FeedbackPanel Validator Handle validation of a FormComponent or a Form
  • 38. Tools for clear responsibilities Page Provides high level layout Panel Implement logic of a clearly separated part of a page Form, put your FeedbackPanel in here and handle Form logic of submitting and refreshing FeedbackPanel Validator Handle validation of a FormComponent or a Form Validations should only do Validation.
  • 39. Tools for clear responsibilities Page Provides high level layout Panel Implement logic of a clearly separated part of a page Form, put your FeedbackPanel in here and handle Form logic of submitting and refreshing FeedbackPanel Validator Handle validation of a FormComponent or a Form Validations should only do Validation. Have a clear separation between Validation and Behavior!
  • 40. Tools for clear responsibilities Page Provides high level layout Panel Implement logic of a clearly separated part of a page Form, put your FeedbackPanel in here and handle Form logic of submitting and refreshing FeedbackPanel Validator Handle validation of a FormComponent or a Form Renderer Render a given Object to a string representation
  • 41. Tools for clear responsibilities Page Provides high level layout Panel Implement logic of a clearly separated part of a page Form, put your FeedbackPanel in here and handle Form logic of submitting and refreshing FeedbackPanel Validator Handle validation of a FormComponent or a Form Renderer Render a given Object to a string representation Renderer: do rendering only! No logic, no filtering, nothing. Just render.
  • 42. Tools for clear responsibilities Page Provides high level layout Panel Implement logic of a clearly separated part of a page Form, put your FeedbackPanel in here and handle Form logic of submitting and refreshing FeedbackPanel Validator Handle validation of a FormComponent or a Form Renderer Render a given Object to a string representation DataProvider Provide the right data based on given input.
  • 43. Tools for clear responsibilities Page Provides high level layout Panel Implement logic of a clearly separated part of a page Form, put your FeedbackPanel in here and handle Form logic of submitting and refreshing FeedbackPanel Validator Handle validation of a FormComponent or a Form Renderer Render a given Object to a string representation DataProvider Provide the right data based on given input. No rendering, no updating of FeedbackPanels or Toolbar.
  • 44. Tools for clear responsibilities Page Provides high level layout Panel Implement logic of a clearly separated part of a page Form, put your FeedbackPanel in here and handle Form logic of submitting and refreshing FeedbackPanel Validator Handle validation of a FormComponent or a Form Renderer Render a given Object to a string representation DataProvider Provide the right data based on given input. Behavior Behaviors are plugins for components. They can provide custom markup, custom pieces of logic. Think of them as decorators.
  • 45. Tools for clear responsibilities: Behaviors All Known Implementing Classes: AbstractAjaxBehavior, AbstractAjaxTimerBehavior, AbstractAutoCompleteBehavior, AbstractBehavior, AbstractDefaultAjaxBehavior, AbstractHeaderContributor, AbstractTransformerBehavior, AjaxEditableLabel.EditorAjaxBehavior, AjaxEditableLabel.LabelAjaxBehavior, AjaxEventBehavior, AjaxFormChoiceComponentUpdatingBehavior, AjaxFormComponentUpdatingBehavior, AjaxFormSubmitBehavior, AjaxFormValidatingBehavior, AjaxIndicatorAppender, AjaxPagingNavigationBehavior, AjaxSelfUpdatingTimerBehavior, AttributeAppender, AttributeModifier, AutoCompleteBehavior, BodyTagAttributeModifier, ContainerWithAssociatedMarkupHelper, ContextPathGenerator, DatePicker, HeaderContributor, OnChangeAjaxBehavior, OrderByLink.CssModifier, SimpleAttributeModifier, StringHeaderContributor, TextTemplateHeaderContributor, VelocityContributor, VelocityHeaderContributor, VelocityJavascriptContributor, WicketAjaxIndicatorAppender, WicketMessageTagHandler.AttributeLocalizer, XsltTransformerBehavior
  • 46. Tools for clear responsibilities: Behaviors Learn to write your own in 4 steps! 1. Choose the most appropriate base class All Known Implementing Classes: AbstractAjaxBehavior, AbstractAjaxTimerBehavior, AbstractAutoCompleteBehavior, AbstractBehavior, AbstractDefaultAjaxBehavior, AbstractHeaderContributor, AbstractTransformerBehavior, AjaxEditableLabel.EditorAjaxBehavior, AjaxEditableLabel.LabelAjaxBehavior, AjaxEventBehavior, AjaxFormChoiceComponentUpdatingBehavior, AjaxFormComponentUpdatingBehavior, AjaxFormSubmitBehavior, AjaxFormValidatingBehavior, AjaxIndicatorAppender, AjaxPagingNavigationBehavior, AjaxSelfUpdatingTimerBehavior, AttributeAppender, AttributeModifier, AutoCompleteBehavior, BodyTagAttributeModifier, ContainerWithAssociatedMarkupHelper, ContextPathGenerator, DatePicker, HeaderContributor, OnChangeAjaxBehavior, OrderByLink.CssModifier, SimpleAttributeModifier, StringHeaderContributor, TextTemplateHeaderContributor, VelocityContributor, VelocityHeaderContributor, VelocityJavascriptContributor, WicketAjaxIndicatorAppender, WicketMessageTagHandler.AttributeLocalizer, XsltTransformerBehavior
  • 47. Tools for clear responsibilities: Behaviors Learn to write your own in 4 steps! 2. Extend from your chosen base class public class MyBehavior extends AbstractBehavior { .. }
  • 48. Tools for clear responsibilities: Behaviors Learn to write your own in 4 steps! 3. Decide which methods to implement
  • 49. Tools for clear responsibilities: Behaviors Learn to write your own in 4 steps! 3. Decide which methods to implement afterRender(Component component)           Called when a component that has this behavior coupled was rendered.
  • 50. Tools for clear responsibilities: Behaviors Learn to write your own in 4 steps! 3. Decide which methods to implement afterRender(Component component)           Called when a component that has this behavior coupled was rendered. bind(Component component)           Bind this handler to the given component.
  • 51. Tools for clear responsibilities: Behaviors Learn to write your own in 4 steps! 3. Decide which methods to implement afterRender(Component component)           Called when a component that has this behavior coupled was rendered. bind(Component component)           Bind this handler to the given component. beforeRender(Component component)           Called when a component is about to render.
  • 52. Tools for clear responsibilities: Behaviors Learn to write your own in 4 steps! 3. Decide which methods to implement afterRender(Component component)           Called when a component that has this behavior coupled was rendered. bind(Component component)           Bind this handler to the given component. beforeRender(Component component)           Called when a component is about to render. cleanup()           This method is called either by onRendered (Component) or onException(Component, RuntimeException) AFTER they called their respective template methods.
  • 53. Tools for clear responsibilities: Behaviors Learn to write your own in 4 steps! 3. Decide which methods to implement afterRender(Component component) onRendered(Component component)           Called when a component that has this behavior           Called when a component that has this behavior coupled was rendered. coupled was rendered. bind(Component component)           Bind this handler to the given component. beforeRender(Component component)           Called when a component is about to render. cleanup()           This method is called either by onRendered (Component) or onException(Component, RuntimeException) AFTER they called their respective template methods.
  • 54. Tools for clear responsibilities: Behaviors Learn to write your own in 4 steps! 3. Decide which methods to implement afterRender(Component component) onRendered(Component component)           Called when a component that has this behavior           Called when a component that has this behavior coupled was rendered. coupled was rendered. bind(Component component) isTemporary()           Bind this handler to the given component.           Specifies whether or not this behavior is temporary. beforeRender(Component component)           Called when a component is about to render. cleanup()           This method is called either by onRendered (Component) or onException(Component, RuntimeException) AFTER they called their respective template methods.
  • 55. Tools for clear responsibilities: Behaviors Learn to write your own in 4 steps! 3. Decide which methods to implement afterRender(Component component) onRendered(Component component)           Called when a component that has this behavior           Called when a component that has this behavior coupled was rendered. coupled was rendered. bind(Component component) isTemporary()           Bind this handler to the given component.           Specifies whether or not this behavior is temporary. onComponentTag(Component component, beforeRender(Component component) ComponentTag tag)           Called when a component is about to render.           Called any time a component that has this behavior registered is rendering the component tag. cleanup()           This method is called either by onRendered (Component) or onException(Component, RuntimeException) AFTER they called their respective template methods.
  • 56. Tools for clear responsibilities: Behaviors Learn to write your own in 4 steps! 3. Decide which methods to implement afterRender(Component component) onRendered(Component component)           Called when a component that has this behavior           Called when a component that has this behavior coupled was rendered. coupled was rendered. bind(Component component) isTemporary()           Bind this handler to the given component.           Specifies whether or not this behavior is temporary. onComponentTag(Component component, beforeRender(Component component) ComponentTag tag)           Called when a component is about to render.           Called any time a component that has this behavior registered is rendering the component tag. cleanup() detach(Component component)           This method is called either by onRendered (Component) or onException(Component,           Allows the behavior to detach any state it has RuntimeException) AFTER they called their respective attached during request processing. template methods.
  • 57. Tools for clear responsibilities: Behaviors Learn to write your own in 4 steps! 4. Implement your behavior! public class AjaxFocusBehavior extends AbstractBehavior { private Component m_component; @Override public void bind(Component component) { this.m_component = component; component.setOutputMarkupId(true); } @Override public void renderHead(IHeaderResponse response) { super.renderHead(response); response.renderOnLoadJavascript("document.getElementById('" + m_component.getMarkupId() + "').focus()"); } /** * {@inheritDoc} */ @Override public boolean isTemporary() { return true; }
  • 58.
  • 59. Composing an AutoCompleteField AutoCompleteField<Long, Pizza> autocomplete = new AutoCompleteField<Long, Pizza>(id, model, choicesProvider, renderer, settings);
  • 60. Composing an AutoCompleteField AutoCompleteChoicesProvider<Long, Pizza> choicesProvider = new PizzaAutoCompleteChoicesProvider (m_pizzaService); AutoCompleteField<Long, Pizza> autocomplete = new AutoCompleteField<Long, Pizza>(id, model, choicesProvider, renderer, settings);
  • 61. Composing an AutoCompleteField AutoCompleteChoicesProvider<Long, Pizza> choicesProvider = new PizzaAutoCompleteChoicesProvider (m_pizzaService); PizzaAutoSuggestRenderer renderer = new PizzaAutoSuggestRenderer(); AutoCompleteField<Long, Pizza> autocomplete = new AutoCompleteField<Long, Pizza>(id, model, choicesProvider, renderer, settings);
  • 62. Composing an AutoCompleteField AutoCompleteChoicesProvider<Long, Pizza> choicesProvider = new PizzaAutoCompleteChoicesProvider (m_pizzaService); PizzaAutoSuggestRenderer renderer = new PizzaAutoSuggestRenderer(); AutoCompleteField<Long, Pizza> autocomplete = new AutoCompleteField<Long, Pizza>(id, model, choicesProvider, renderer, settings); renderer.setOnSelectJavascriptProvider(autocomplete);
  • 63. Composing an AutoCompleteField AutoCompleteChoicesProvider<Long, Pizza> choicesProvider = new PizzaAutoCompleteChoicesProvider (m_pizzaService); PizzaAutoSuggestRenderer renderer = new PizzaAutoSuggestRenderer(); AutoCompleteField<Long, Pizza> autocomplete = new AutoCompleteField<Long, Pizza>(id, model, choicesProvider, renderer, settings); renderer.setOnSelectJavascriptProvider(autocomplete); autocomplete.add(new PlaceHolderTextBehavior(Model.of("Search for pizza...")));
  • 64. Composing an AutoCompleteField AutoCompleteChoicesProvider<Long, Pizza> choicesProvider = new PizzaAutoCompleteChoicesProvider (m_pizzaService); PizzaAutoSuggestRenderer renderer = new PizzaAutoSuggestRenderer(); AutoCompleteField<Long, Pizza> autocomplete = new AutoCompleteField<Long, Pizza>(id, model, choicesProvider, renderer, settings); renderer.setOnSelectJavascriptProvider(autocomplete); autocomplete.add(new PlaceHolderTextBehavior(Model.of("Search for pizza..."))); autocomplete.add(new AjaxSelectTextOnClickBehavior());
  • 65. Composing an AutoCompleteField AutoCompleteChoicesProvider<Long, Pizza> choicesProvider = new PizzaAutoCompleteChoicesProvider (m_pizzaService); PizzaAutoSuggestRenderer renderer = new PizzaAutoSuggestRenderer(); AutoCompleteField<Long, Pizza> autocomplete = new AutoCompleteField<Long, Pizza>(id, model, choicesProvider, renderer, settings); renderer.setOnSelectJavascriptProvider(autocomplete); autocomplete.add(new PlaceHolderTextBehavior(Model.of("Search for pizza..."))); autocomplete.add(new AjaxSelectTextOnClickBehavior()); autocomplete.add(new RequiredValidator<Pizza>());
  • 66. Give your classes clear names. If it’s a page, call it Page (example: LoginPage instead of Login) If it’s a panel, call it Panel (example: LoginPanel instead of Login) The same goes for Form, Validator, Renderer, etcetera.
  • 67. Use wicket:message instead of Label (for simple translations)
  • 68. Use wicket:message instead of Label (for simple translations) <wicket:message key="SELECT_LANGUAGE_LABEL"/>
  • 69. Use wicket:message instead of Label (for simple translations) <wicket:message key="SELECT_LANGUAGE_LABEL"/> Put this in your HTML and it will be replaced by Wicket for the translation in your property file.
  • 70. Use wicket:message instead of Label (for simple translations) <input type="button" wicket:id="someid" wicket:message="value:CHANGE" />
  • 71. Use wicket:message instead of Label (for simple translations) <input type="button" wicket:id="someid" wicket:message="value:CHANGE" /> Wicket will add a ‘value’ attribute and set it to the localized value!
  • 73.
  • 75.
  • 76. Smell 1 Class names containing: *With* *Without* public class CheckBoxWithoutLabel extends CheckBox public class CheckBox extends LabeledComponent<CheckBoxWithoutLabel> Duplicated code hierarchies are ugly...
  • 77. LabelWithLabel: LabeledComponent<Label> lbl = new LabeledComponent<Label>(property, dopmd.getTranslationKey(), position, label); (In DOMRComponentFactory...)
  • 78. Yes, we also have something like a LabelWithoutLabel: position = LabelPosition.NONE; LabeledComponent<Label> lbl = new LabeledComponent<Label>(property, dopmd.getTranslationKey(), position, label);
  • 79. Smell 2 instanceof @Override public MarkupContainer setDefaultModel(IModel<?> model) { IModel<String> model1 = new Model<String>(); if (model != null && !(model instanceof DOMRObjectModel)) { Revision revision = (Revision) model.getObject(); if (revision != null) { model1.setObject(revision.getId().toString()); } return super.setDefaultModel(model1); } return super.setDefaultModel(model); } CatalogAutoSuggestField
  • 80. Smell 3 many constructors public class CheckBox extends LabeledComponent<CheckBoxWithoutLabel> { private static final long serialVersionUID = -8246992163993958051L;
  • 81. Smell 3 many constructors public class CheckBox extends LabeledComponent<CheckBoxWithoutLabel> { private static final long serialVersionUID = -8246992163993958051L; public CheckBox(String translationKey, IModel<Boolean> model) { this(null, translationKey, model); }
  • 82. Smell 3 many constructors public class CheckBox extends LabeledComponent<CheckBoxWithoutLabel> { private static final long serialVersionUID = -8246992163993958051L; public CheckBox(String id, String translationKey, IModel<Boolean> model) { this(id, translationKey, LabelPosition.RIGHT, model); }
  • 83. Smell 3 many constructors public class CheckBox extends LabeledComponent<CheckBoxWithoutLabel> { private static final long serialVersionUID = -8246992163993958051L; public CheckBox(String translationKey, LabelPosition position, IModel<Boolean> model) { this(null, translationKey, position, model); }
  • 84. Smell 3 many constructors public class CheckBox extends LabeledComponent<CheckBoxWithoutLabel> { private static final long serialVersionUID = -8246992163993958051L; public CheckBox(String id, String translationKey, LabelPosition position, IModel<Boolean> model) { super(id, translationKey, position, new CheckBoxWithoutLabel(id + "CheckBox", model)); getLabel().addCssClass("checkBoxWithLabel"); }
  • 85. Smell 3 many constructors public class CheckBox extends LabeledComponent<CheckBoxWithoutLabel> { private static final long serialVersionUID = -8246992163993958051L; public CheckBox(IModel<Boolean> model) { this(null, model); }
  • 86. Smell 4 getPage() calls Page page = (Page) event.getPage(); event.refreshComponents(m_grid, page.getFeedbackPanel());
  • 87. Smell 5 FeedbackPanel not inside Form Page page = (Page) event.getPage(); event.refreshComponents(m_grid, page.getFeedbackPanel());
  • 88. Smell 5 Refreshing FeedbackPanel, Toolbar or other classes from unexpected locations // Refresh the toolbar if (getPage() instanceof Page) { ToolBar tb = (ToolBar) ((Page) getPage()).getComponent("toolbar"); if (tb != null) { target.addComponent(tb); } }
  • 89. Smell 6 Casting // Refresh the toolbar if (getPage() instanceof Page) { ToolBar tb = (ToolBar) ((Page) getPage()).getComponent("toolbar"); if (tb != null) { target.addComponent(tb); } }
  • 90. Smell 7 Using getParent() public void setButtonBar(ButtonBar buttonBar) { if (!buttonBar.getId().equals(s_buttonPanel)) { throw new WicketRuntimeException("Buttonbar id is wrong."); } if (m_buttonBarPanel.getParent() == null) { if (m_form.get(s_buttonPanel) != null) { m_form.get(s_buttonPanel) .replaceWith(m_buttonBarPanel); } else { m_form.add(m_buttonBarPanel); } } m_buttonBarPanel.replaceWith(buttonBar); }
  • 91. Smell 8 Getting components from the Page ToolBar tb = (ToolBar) ((Page) getPage()).getComponent("toolbar");
  • 92. Smell 9 Doing layout and styling in Java ColumnLayoutContainer pptfContainer = new ColumnLayoutContainer("proofPaidTuitionFeeContainer") .addComponent(sourceLabel, Size.percent(30)) .addComponent(amountLabel, Size.percent(30)) .addComponent(factory.createDateTimeLabel("dateTime", m_dateTimeModel, LabelPosition.TOP, m_controller.getTimeZoneCurrentUser())) .newLine();
  • 93. Smell 10 Using onBeforeRender() too often protected void onBeforeRender() { super.onBeforeRender(); if (isEnabled()) { AjaxRequestTarget target = AjaxRequestTarget.get(); if (target != null) { target.appendJavascript(TextTemplateHeaderContributor.forPlainJs(s_textTemplate, getHeaderContributor(false)).toString()); } else { add(TextTemplateHeaderContributor.forJavaScript(s_textTemplate, getHeaderContributor(true))); } } } tip: check onConfigure() (and in this case: check IHeaderContributor)
  • 94. Smell 11 Calling ResourceModel.getObject() feedback.info(new ResourceModel(XTranslation.PROPERTY).getObject());
  • 95. Smell 11 Calling ResourceModel.getObject() feedback.info(new ResourceModel(XTranslation.PROPERTY).getObject());
  • 96. Smell 11 Calling ResourceModel.getObject() feedback.info(new ResourceModel(XTranslation.PROPERTY).getObject()); feedback.info(getString(XTranslation.PROPERTY));
  • 97. Smell 11 Calling ResourceModel.getObject() feedback.info(new ResourceModel(XTranslation.PROPERTY).getObject()); feedback.info(getString(XTranslation.PROPERTY)); Call getString only outside constructor! (check methods that are called by the constructor)
  • 98. Smell 11 Calling ResourceModel.getObject() feedback.info(new ResourceModel(XTranslation.PROPERTY).getObject()); feedback.info(getString(XTranslation.PROPERTY)); Call getString only outside constructor! (check methods that are called by the constructor) Tried to retrieve a localized string for a component that has not yet been added to the page. This can sometimes lead to an invalid or no localized resource returned. Make sure you are not calling Component#getString() inside your Component's constructor. Offending component: [MarkupContainer [Component id = panelmenu, page = <No Page>, path = panelmenu.PanelMenu]]
  • 99. Smell 12 Hidden Javascript dependencies AjaxRequestTarget target = AjaxRequestTarget.get(); if (target != null) { target.appendJavascript("clickFeedbackRefreshLink();"); }
  • 100. Smell 13 Transient fields (expect Exceptions!)
  • 102. Groups
  • 103.
  • 104. Wicket Mailing List http://wicket.apache.org/help/email.html Users list - Archives - Search - Subscribe - Unsubscribe - Help
  • 105. Wicket examples Not always a good example of code quality, but get ideas from: http://wicketstuff.org/wicket14/index.html
  • 106. Wicket Wiki https://cwiki.apache.org/WICKET/
  • 107. Reference library https://cwiki.apache.org/WICKET/reference-library.html