星期四, 7月 22, 2004

My favorite usage of Struts ActionForm

First ! my English is very bad.... so... you know what I mean !

It seems like there are many developers favorite dynamic ActionForm, and there are many discussions with it. For myself, I hate that anything can't check at compile time, and increasing size of struts-config.xml is not so funny. I tend to keep struts-config.xml small and reuse ActionForms as many as possible. The ideas borrow from some books (I forgot the source...)

public class EmailForm extends ActionForm {
private String emailDisplay ;
public String getEmailDisplay() {...}
public void setEmailDisplay(String email) {...}
public Email getEmail() {
return new Email(getEmailDisplay());
}
public void reset(
ActionMapping mapping,
HttpServletRequest request) {
setEmailDisplay(null);
}
public ActionErrors validate(
ActionMapping mapping,
HttpServletRequest request) {
//...
EmailValidate.check(getEmailDisplay());
//...
}
}

above EmailForm do some validation, and convert String emailDisplay to business object "Email" via getEmail(). We can directly call getEmail() in Struts Action to prevent annoy conversion codes. Now let's reuse it:

public class UserForm extends ActionForm {
private EmailForm emailForm = new EmailForm();
private String userName ;

public String getEmailForm() {...}
public void setEmailForm(EmailForm emailForm) {...}

public Email getContactEmail() {
return getEmailForm().getEmail();
}
public void reset(
ActionMapping mapping,
HttpServletRequest request) {
setUserName(null);
getEmailForm().reset(mapping,request); // reuse reset()
}
public ActionErrors validate(
ActionMapping mapping,
HttpServletRequest request) {
//reuse validate()
ActionErrors errors
= getEmailForm().validate(mapping, request);
// do rest of validation.
UserName.check(getUserName());
}
public String getUserName() {...}
public void setUserName(String userName) {...}
}

In UserForm, we reuse getEmail(), validate(), and reset() methods of EmailForm. Of course, we can apply another email validation/resetting rule if required. In Struts Action, using delegated getContactEmail() makes better sense. in jsp, just write "emailForm.emailDisplay", as below:

<html:form ... >
User Name: <html:text property="userName" />
Contact Email: <html:text property="emailForm.emailDisplay" />
</html:form>

Quit simple ! As application grow, we may extract and refactor more elemantary ActionForms like EmailForm to reduce code duplication. Delegating ActionForm not only use for fields, but also behaviors (button), for example:

public class CRUDForm extends ActionForm {
private String createButton ;
private String readButton ;
private String updateButton ;
private String deleteButton ;
public boolean isCreate() {
return isButtonPressed(getCreateButton());
}
public boolean isRead() {
return isButtonPressed(getReadButton());
}
public boolean isUpdate() {
return isButtonPressed(getUpdateButton());
}
public boolean isDelete() {
return isButtonPressed(getDeleteButton());
}
private boolean isButtonPressed(String button) {
return button != null

|| button.trim.length() > 0
}

// getter/setter of createButton, readButton,....etc.
}

Above create an elementory CRUD (create/read/update/delete) ActionForm, we can apply it to our UserForm too.

public class UserForm extends ActionForm {
private CRUDForm operation = new CRUDForm();
public void setOperation(CRUDForm operation) {...}
public CRUDForm getOperation() {...}
// rest of fields like userName, email... etc
}

while in Action, write some codes like below:

public ActionForward execute(...) {
UserForm userForm = (UserForm) form;
if(userForm.getOperation().isCreate()) {
doCreateUser();
} else if (userForm.getOperation().isRead() {
doQueryUser();
} else ....
// rest of doXXXUser()....
}

A little messy codes.... But at least we don't need to "extends" DispatchAction or LookupDispatchAction to limit our Struts' Action inheritance (yes, usually we have a generic BaseBusinessAction across all of our Struts Action hierarchy). Addtionally, this avoid hard code string in struts-config.xml or methods such as getKeyMethodMap(...) from LookupDispatchAction. Less hard code string lead less run time error and we can do happy refactoring without worry. As application grow, we may have a big ActionForm called OperationForm which gathers lots of operations like isSubmit(), isCancel(), isCreate(), isWithDraw() ,and isPublish().... etc. to gain maximum of reuse.

Personally, I prefer to spend more time on building more compact and reusable ActionForms rather than fighting hard code string stuff such as dynamic ActionForm or LookupDispatchAction.

2 Comments:

At 8:51 上午, Anonymous 匿名 said...

我有一個小問題。UserForm所用到的MailForm和CRUDForm這兩個表單物件會由誰所產生呢?!是Struts嗎?!很好奇,直接用就行了嗎?!

 
At 1:22 下午, Blogger ingramchen said...

Hi !
我們可以這樣寫:
public UserForm extends ActionForm {
  //create while UserForm instantiate
  private MailForm mailForm = new MailForm() ;
  public void reset(
    ActionMapping mapping,
    HttpServletRequest request) {
     getEmailForm().reset(mapping,request);
  }
}
直接 new 一個就行了。

 

張貼留言

<< Home