struts-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Matthias Bauer <Matthias.Ba...@livinglogic.de>
Subject Re: composable RequestProcessor
Date Wed, 04 Jun 2003 08:49:41 GMT
During this whole discussion I feel torn back and forth whether to solve 
this challenge with configuration or in code. One thing we are trying to 
achieve when going in the configuration direction is the following: 
Different extensions don't need to know anything of each other and we 
can (if we are finding the right granularity) selectively choose to use 
processXXX from extension 1 and processYYY from extension 2 just by 
configuring this.

I am afraid this will only work in very few cases. First there is the 
issue, David raises: The following code snippet cannot be configured in XML:

> public processActionCreate() {
>  super();  //do parent behavior
>  tilesRP.processActionCreate();  // do tiles
>  // do some  custom stuff
>  someotherRP.processActionCreate();
> } 

The second issue is: Will processXXX and processYYY always work together 
without conflicting? I don't think so. But allowing a developer to 
configure arbitrary request processor methods together implies that they 
will be compatible among each other.

My third point is: If we decide to only let the methods processXXX be 
reconfigured, this might not be sufficient, because (look at Tiles) an 
extension might want to redefine an internal method that is used from 
several processXXX methods.

The more we are discussing, the more it looks to me that we don't want 
to give up the flexibility of composition by coding. Therefore, here is 
a different proposal that requires only a minimum of changes to the 
request processor and looks very much like the approach I took for the 
workflow extension:

Look at the UML diagram I attached to this mail (I hope it comes through 
to the list). It shows the TilesRequestProcessor and the 
WorkflowRequestProcessor directly being derived from RequestProcessor. 
If I want to implement a TilesWorkflowRequestProcessor, I have to 
subclass TilesWorkflowRequestProcessor and add the workflow 
functionality (in our example processYYY). This is painful if processYYY 
contains some complex logic, because it would normally require to have a 
copy of the code in both WorkflowRequestProcessor and 
TilesWorkflowRequestProcessor.  In order to prevent this code 
duplication, the method processYYY is extracted into a separate class 
WorkflowRequestProcessorLogic.

Now the method processYYY in WorkflowRequestProcessor and 
TilesWorkflowRequestProcessor becomes trivial:

public Some_Type processYYY()
{
    return this.getWfLogic().processYYY();
}


The method getWfLogic must look like this:

private WorkflowRequestProcessorLogic getWfLogic()
{
   if (null == this.wfLogic)
   {
        this.wfLogic = new WorkflowRequestProcessorLogic(this);
    }
}

As the instance of WorkflowRequestProcessorLogic receives a reference to 
the RequestProcessor, its method processYYY can look this way:

public Some_Type processYYY()
{
    ...
    reqProc.processXXX();
    ...
    reqProc.doSomething();
    ...
}

This only works, if all methods in RequestProcessor become public and 
all members of RequestProcessor become accessible with public setters 
and getters. But this is the only change needed to enable extension 
developers to implement this pattern.

What are the consequences?

1. Drawback: For each combination of different extensions, a new 
subclass needs to be created. But if every extension follows this 
pattern proposed here, this is a trivial task for all the cases that 
could also be covered by configuration and does not necessarily need to 
be done by the creator of an extension but can as well be done by the 
struts developer who wants to use the extensions A, B and C together. In 
more sophisticated cases (e. g. when methods need to be chained), the 
configuration approach does not work anyway, but the coding approach can 
at least be taken by the people who are knowing what they do.

2. The one who assembles valid combinations of request processors is the 
one in charge to test whether the extension play well together. 
Therefore the danger of configuring arbitrary parts of different request 
processors is banned.

3. One of the extensions (in our diagram TilesRequestProcessor) does not 
even need to stick to the pattern, i. e. can have the logic embedded in 
its request processor.

4. Maximum flexibility, because any method can be overwritten by an 
extension.

Comments?

--- Matthias


Mime
View raw message