File APIs for Java Developers
Manipulate DOC, XLS, PPT, PDF and many others from your application.
http://aspose.com/file-tools
The moose likes OO, Patterns, UML and Refactoring and the fly likes Design Mental Roadblock Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login
JavaRanch » Java Forums » Engineering » OO, Patterns, UML and Refactoring
Bookmark "Design Mental Roadblock" Watch "Design Mental Roadblock" New topic
Author

Design Mental Roadblock

jason adam
Chicken Farmer ()
Ranch Hand

Joined: May 08, 2001
Posts: 1932
Have a fun little problem, but can't quite figure out the best way to go about handling it.
The very general overview of the situation is this:
I have 4 different types of requests
3 types use the same controller to handle the different operations used for the request, 1 type uses a completely different controller.
The type-to-controller configuration is in an xml file. Underneath each controller element is a listing of operations associated with it.
A user can request to do any number of requests, so you could have multiple requests of the same type, as well as multiple types.
The requests are wrapped up in a collection and sent to a stateless session bean, which handles instantiating initializing the controller (as well as the underlying operations), and telling them to do their thing.
It's easy enough to iterate over the collection of requests and get the controller for each type, however that seems pretty resource intensive. I'd like to get one instance of the controller that handles all types associated with it. Just can't think of a really elegant way to do this.
Hoping it's one of those things where I walk away from the problem and later get blind-sided by a wonderful solution, but never hurts to ask for suggestions.
Thanks
Jason
Frank Carver
Sheriff

Joined: Jan 07, 1999
Posts: 6920
I can't help thinking the design as it stands seems a shade overcomplicated.
Is there any particular reason why you need to re-query the request-controller mapping for every request? Does the configuration file really change between builds? It seems to me that you are maybe pushing a decision to runtime, which should be done at build time.
If your mapping is a bit more static than that, I'd be tempted to have all the requests dispatch to a single controller which in turn decides what to do with the requests (i.e. delegate to the two current ones, maybe). You can generate the code for this uber-controller based on an external config file if you like, but I'd start with hard-coding it and see what really needs changing later (if anything).
If this approach doesn't fit your problem, please explain a little more so we can understand your need a bit better.


Read about me at frankcarver.me ~ Raspberry Alpha Omega ~ Frank's Punchbarrel Blog
jason adam
Chicken Farmer ()
Ranch Hand

Joined: May 08, 2001
Posts: 1932
The Controller gets passed a collection of requests, so it iterates through those and performs on each one the operations that it was initialized with.
The reason that the group I'm with is really big on not hardcoding just about anything is that we're creating a product baseline that other projects, companies, etc. would use and tailor to their specific needs. So if some other group here decides to use our application, we want them to be able to plug in specific operations to be done on some new type of request. Needs to be very adaptable.
Basically the way I'm looking at it is iterating through the current list of requests, and separating those requests into related groups based on controller type. Each group would be assigned one controller. I'll then go and get a collection of operations for each controller type, and then pass all that mess to the controller to do its work.
So not only are we writing this specific to the project we are working on, we're also in charge of developing the baseline for other projects as well.
[ April 08, 2003: Message edited by: jason adam ]
Ilja Preuss
author
Sheriff

Joined: Jul 11, 2001
Posts: 14112
Originally posted by jason adam:
So not only are we writing this specific to the project we are working on, we're also in charge of developing the baseline for other projects as well.

How do you figure out the needs of those other projects? In (not only) my experience, it is very hard to do so without actually developing (at least some of) them in parallel...
[ April 08, 2003: Message edited by: Ilja Preuss ]

The soul is dyed the color of its thoughts. Think only on those things that are in line with your principles and can bear the light of day. The content of your character is your choice. Day by day, what you do is who you become. Your integrity is your destiny - it is the light that guides your way. - Heraclitus
Junilu Lacar
Bartender

Joined: Feb 26, 2001
Posts: 4456
    
    6

Other reservations/issues expressed aside, I would try using a Map. Each map entry would have a unique controller as its key and a List as its value. Iterate through the requests, look up each request's controller in the map and add the request to the corresponding List. Then iterate over the Map, passing each controller its associated list of requests to perform.
You said something about a process being "resource intensive". Is this just a gut feeling or have you used an objective measure, e.g. a profiler? The reason I ask is because of the Rules of Optimization
Martin Fowler's thoughts: http://www.martinfowler.com/ieeeSoftware/yetOptimization.pdf
[ April 08, 2003: Message edited by: Junilu Lacar ]

Junilu - [How to Ask Questions] [How to Answer Questions]
jason adam
Chicken Farmer ()
Ranch Hand

Joined: May 08, 2001
Posts: 1932
Ilja, while not having specific requirements for other projects, we do have general requirements of the mission management baseline that must be met for all projects that will be using the application. Those general requirements must be written in such a way that will allow individualization within our general framework. Give them the base, they plug in the implementation.
Despite this posting, I'm actually a fan of YAGNI, but this isn't one of those situations where that applies. We know that in the future there will be a need for other types of requests and operations than what we are immediately dealing with, and time/money/people constraints practically dictate that we design for those possibilities now.
Junilu, nope haven't done any actual performance testing, so I guess I shouldn't worry about it until then. Very good point, and thanks for the links! Though I would be interested in the other reservations
I figured I'd end up with two Maps that I'd have to work with, one that maps controller types to a collection of requests, and one that would map controller types to operations to perform. My calling class would use the type-to-operations map to initialize the controller, and then for each controller type pass in the associated collection of requests. My main concern for optimization was the use of XML and perhaps too many references hanging about, but probably just being paranoid.
Stan James
(instanceof Sidekick)
Ranch Hand

Joined: Jan 29, 2003
Posts: 8791
See the Command Pattern. What if the requests knew what kind of controllers they wanted? Then rather than

you could just say

This dispatcher will accept totally new command and controller types with no code change.
[ April 08, 2003: Message edited by: Stan James ]

A good question is never answered. It is not a bolt to be tightened into place but a seed to be planted and to bear more seed toward the hope of greening the landscape of the idea. John Ciardi
jason adam
Chicken Farmer ()
Ranch Hand

Joined: May 08, 2001
Posts: 1932
I'll need to dig more into the pattern, possibly a way to go, but based on my initial pass through of it if it will meet my needs.
Let me give more a concrete process example. A user, from a GUI, makes a request to do something (I have to be vague, unfortunately). All the parameters are wrapped up into an object and passed on to a bean (good ol' session facade). Now that request may have one, two, or several operations associated with it. Based on a configuration file and the type of the request, a controller is created, which holds implementation specific details on what types of operations to call on the request.
Now, there may be multiple requests of the same type. Easy enough, one controller calls the same operations for each request.
However, there may be multiple requests of different types. Request1 may need operationA and operationB, but Request2 may only want operationC. Problem is, sometimes Request1 will only want operationB. Or it may be decided that Request2 wants A and C, etc.
Needs to be configurable without changing existing code, or repeating code from request type to request type (since mutliple types will use the same controller, sometimes).
I came up with the following, not sure if it is the best right now, but it works:

[ April 08, 2003: Message edited by: jason adam ]
Frank Carver
Sheriff

Joined: Jan 07, 1999
Posts: 6920
OK. Here's another go. First I'll describe what I see as the important aspects of this system, then I'll offer a solution.
  • As a general policy you don't want to include any extra code to handle specific guesses about what may be needed.
  • You don't know everything that the future holds, but you seem fairly sure you need to allow for reconfiguration without editing and rebuilding the application.
  • For the purposes of this question, one of the points of flexibility is to be able to configure the system to handle new types of request, recognize them, and associate them either with existing or with new behaviour.
  • An initial (untested, but accepted as reasonable) estimate is that the "effort" of processing the requests is large compared with the "effort" of dispatching them.
  • Each user operation can generate several requests, which may need the same or different processing.

  • Now, a suggestion for a solution approach.
    I'm inclined to treat the "bundling" and "unbundling" of requests as an optimization rather than a specific feature of the application. It doesn't seem to materially affect how the system works if we consider the input from the user interface as simply a stream of requests to be dealt with. If the "bundling" is vital, then it seems simple enough to produce a method which just returns the next request.
    So the problem might be summarized as:

    Now, for minimum "infrastructure" and maximum flexibility, I suggest that the decision of which processing to apply to the request might be best delegated to the objects which do the request processing (I'll call these "processors"). So the pseudocode might become

    The major advantage of this is that the configuration of the processors is very simple but also very flexible. Imagine just a text file of class names, instantiated at startup and stored in an array or List.
    This approach also begins to offer other advantages, which may help to "cut across" the problem.
    For example: you said Request1 may need operationA and operationB, but Request2 may only want operationC. Problem is, sometimes Request1 will only want operationB. Or it may be decided that Request2 wants A and C, etc..
    It seems that maybe your introduction of the "controller" objects just a symptom of a design assumption. Why not specify the processing mostly in terms of these basic "operations" rather than inventing a new "controller" for every combination.
    We instantiate a "wrapper" for each operation which has one method "handle(request)". From experience I reckon that you might need two types of return status from this method ( "finished" and "keep going" ). This allows extra customization flexibility by enabling objects at the head of the list to "override" later ones without recompiling them, if necessary.
    Here's the code for the system:

    And here's an example Processor (I shall assume that somehow you can get some sort of "type" from a request to decide how to deal with it:

    It's easy to imagine more complex Processors (such as the equivalent of your "Controller" which takes a list of operations rather than just one, or ones where the decision of which requests to handle depends on other criteria). The key point, though, is that all of those decisions can safely be made long after the core system code is written, compiled and deployed. If you provide a method to update or reload the list of processors in the dispatcher, this approach will even happily support "hot updates" while the application is still running (like some servlet containers do, for example).
    However fancy the decision making for future applications, it won't need to affect this core code, just potential "pluggable" extensions. With that thought in mind you can then safely ignore other projects and future possibilities, and get down to writing just the operations and processors for your specific need.
    Does this seem at all apropriate ?
    [ April 08, 2003: Message edited by: Frank Carver ]
    jason adam
    Chicken Farmer ()
    Ranch Hand

    Joined: May 08, 2001
    Posts: 1932
    Definitely does Frank, I can easily see implementing in this fashion (though the reworking might not be as easy ).
    I'm going to toy with writing this up a couple of different ways, and see which is best suited for our needs.
    jason adam
    Chicken Farmer ()
    Ranch Hand

    Joined: May 08, 2001
    Posts: 1932
    OK, here we go again. I've been rolling through different ways to use your approach Frank, and running into some snags. I think a more concrete explanation is in order *giggles as all the abstract artists run away screaming*
    For the purpose of this example, we have five types, named Type1, Type2 ... Type5. We will have operations named Foo, Bar, and Fee. The break down is such:
    Type1 needs to be worked on by Foo, then Bar, in that order.
    Type2 needs to be worked on by Foo, then Bar, in that order.
    Type3 needs to be worked on only by Foo.
    Type4 needs to be worked on by Fee.
    (turn your head XPers) Type5 doesn't exist yet but we know it will in the future, and it also needs to be worked on by Foo, then Bar, in that order.
    Even though Type1, 2, 3, and 5 need Foo, each one doesn't like how the others are worked on by Foo, so we end up with Foo1, Foo2, Foo3 and Foo5. Same thing with Bar, though we only have Bar1, Bar2, and Bar5.
    As far as Fee goes, in the future we may need specific types of Fee, but since we only have one needing Fee, and we only expect to have one Fee for some time, we aren't worried about a Fee4.
    The way I see it, we need 3 processors (what I'm calling "controller"). One to handle the Foo-then-Bar combination, one to handle just the Foo, and one to handle the Fee. These processors need to work on these operations in general terms, but are also responsible of making sure the right Foo or Bar or Fee is used for the correct Type.
    So if we look at just Type1, we should have a ProcessorA (probably implementing some generic Processor interface so our client doesn't have to deal with specifics, either), which has a list of Foos and Bars, and a list of types (Type1 and Type2, presently). When it takes Type1, it sends it to Foo then Bar, but in reality Type1 is going to Foo1 and Bar1.
    So my problem with the proposed solution is that I still need to define not only what types that processor works on, which is easy enough, but also a mapping between the specific type to the correct operations, while still allowing the Processor to be oblivious to what is really going on. To do this, I find myself moving back to the way I have it now.
    So I end up with a mapping of Strings that tie a specific operation to a specific classname, and having my controller do the following:
    String type = "Foo" + type.getType();
    String className = map.get( type );
    Foo foo = Class.forName( className ).newInstance();
    So if Type1's type returns "1", then "Foo1" would be mapped to the actual class name, for instance com.myfoos.Foo1.
    Since ProcessorA knows that it always does Foo then Bar, using this method doesn't necessarily bother me, but I'm thinking there must be a better way. I just can't find it, even with the great suggestions given already.
    Now, how can I adapt your above example Frank to work with what is needed, and keep the code simpler than it already is?
    Frank Carver
    Sheriff

    Joined: Jan 07, 1999
    Posts: 6920
    I'll think about this and try and come back with some more constructive comments later. But i did notice one thing:
    ...needs to be worked on by Foo, then Bar, in that order
    That's why I suggested splitting out the operations. So anything which needs to be processed by Foo is caught by the Foo processor, and passed on. If it also needs to be caught by the Bar processor, then that does its thing and passes it on too.
    I may be wrong, but it looks as if I didn't emphasise that subtlety in the design enough.
    jason adam
    Chicken Farmer ()
    Ranch Hand

    Joined: May 08, 2001
    Posts: 1932
    Aaah, ok I think I get what you mean.
    So Foo1, which works on Type1, does it's thing and returns CONTINUE, which the processor then calls Bar1, which does its thing and returns FINISHED. The processor then moves to the next type. Is that correct?
    That would work, but we'd still need a mapping between types and Foos and Bars, which basically keeps bringing me back to where I am now.
    Stan James
    (instanceof Sidekick)
    Ranch Hand

    Joined: Jan 29, 2003
    Posts: 8791
    I still like the idea of Command here. Put the knowledge of what has to be done inside the Request. Where better to encapsulate this knowledge?
    Type1's execute() method says:
    FooFactory.get().do( me ); // or process( this ) but I liked Do Me!
    BarFactory.get().do( me );
    Type3's execute() method says:
    FooFactory.get().do( me );
    Type4's execute() method says:
    FeeFactory.get().do( me );
    No controllers, no mappings. The service just says:
    request = getNextRequest();
    request.execute();
    Now, say the service comes to me as a shrink-wrapped binary - I cannot change it. I can write a new request, factory and processor implementing any interfaces that came with the service. I can feed the new requests to the service and it will happily execute them. Ahhhhh, encapsulation!
    [ April 10, 2003: Message edited by: Stan James ]
    Frank Carver
    Sheriff

    Joined: Jan 07, 1999
    Posts: 6920
    So Foo1, which works on Type1, does it's thing and returns CONTINUE, which the processor then calls Bar1, which does its thing and returns FINISHED. The processor then moves to the next type. Is that correct?
    Almost. I'd envisage the default return value always being CONTINUE. Why should the "bar" processor have to know that no more processing is needed for a Type1? FINISHED is there for the (hopefully rare) case where one processor needs to actively prevent further processing.
    That would work, but we'd still need a mapping between types and Foos and Bars, which basically keeps bringing me back to where I am now.
    I guess I must be missing something. Could you not just have a processor sequence of Foo1, Foo2, Foo3, Bar1, Bar2, Bar3 etc. Where Foo1 and Bar1 recognize and deal with Type1 requests, Foo2 and Bar2 recognize and deal with Type2 etc.
    I can't help thinking that your fondness for a Map to dispatch these feels a bit like a premature optimization. Do you really have enough type and processor combinations that this might have noticeable performance implications ?
    Stan's solution has merit, although I was (maybe incorrectly) imagining a model more like a web server where the incoming requests are relatively dumb packages of parameters, and the intelligence is in the processing. In this case, to work out which processes wil be needed for each request and to add such knowledge to the request object would seem to need some sort of code quite like what we are discusssing.
    How did you plan to work this out, Stan?
    Stan James
    (instanceof Sidekick)
    Ranch Hand

    Joined: Jan 29, 2003
    Posts: 8791
    Yup, it gets harder when the request comes across the wire like that. I'd probably try a command factory that looks at the HTTP request and instantiates the correct command class. Yes, you're back to mappings and lookups, but encapsulated well. Think of the one-line descriptions of class responsibilities in various options:
    Command Factory: Maps HTTP requests to commands.
    My Processor: Gets the command for a request and executes the command.
    The Processor in other posts: This class maps HTTP requests to all the steps that have to be done and manages execution of the steps in sequence unless one says "stop".
    Of course I spun the descriptions to make the first one look simpler, but it really is simpler. I put knowledge of the steps inside the command instead of an omniscient controller that knows everything about every command. I made the controller dead easy - get a command, execute it.
    This is a tip I find people missing at work from time to time. Don't just write code that works. Step back and look at the classes and methods now and then. Can you describe them simply? Do they do one thing well or lots of things? This is the kind of thing that CRC analysis/design reveals nicely.
    And just for grins, the command factory would be easy to TEST with JUnit. Pass it an HTTP request, see if it gives you the right command. Done.
    BTW: I think this is exactly what Struts does. It looks at the incoming request, finds a mapping of request to classname in XML configuration, creates an instance of the class and sends the request to it. Probably a very common pattern.
    [ April 11, 2003: Message edited by: Stan James ]
    jason adam
    Chicken Farmer ()
    Ranch Hand

    Joined: May 08, 2001
    Posts: 1932
    Frank, you are correct about Stan's solution.
    Stan, I definitely like that pattern, now that your wonderful example gives me a clear way to view how to use it. However, in this case, my example is a little misleading in that Type1, Type2, etc. are not instances. The distinction between types is a simple String within a general data object. The jist is I work purely on the backend logic, and have no control over what I am being given. The object is being generated by GUI input, and the developer of the GUI wants to avoid any logic that would lead the GUI into creating subclasses of a general data type. I actually approached him with that idea, was shot down. So it is up to my EJB and classes on the lower tiers to decide what type the object represents and how to deal with it.
    Frank, so what I'm reading is that Foo1 will take in an object, look to see if it is Type1, and if so, process it. If not, it returns CONTINUE and waits for the next request. Ditto for all the other types of Foo and Bar. That sounds really doable, I just need to make sure sequencing happens correctly (not mentioned is all the JMS, RMI and timer stuff happening for "Bar" to communicate with external subsystems, but these happen asynchronously so that shouldn't be too big an issue).
    jason adam
    Chicken Farmer ()
    Ranch Hand

    Joined: May 08, 2001
    Posts: 1932
    And Stan, in regards to "Do Me!", I'd have to get to know you through a few more posts before I even considered that approach
    [ April 11, 2003: Message edited by: jason adam ]
    Rajesh S. Dalvi
    Greenhorn

    Joined: Apr 17, 2003
    Posts: 2
    May be u can use Service Locator patters.
    In your case u can always request your controller classes from central Service Locator class. Which will cache controller object.
    So u don't have to create new object for each request.
    Stan James
    (instanceof Sidekick)
    Ranch Hand

    Joined: Jan 29, 2003
    Posts: 8791
    Making your command factory decide what kind of command to create based on a string in the HTTP request is just fine. I made one web app with a hidden field on every page that contained an abstract name for the page handler (command). The controller gets the name, looks up the class, does createInstance(), and executes the handler. (Guess I left out the factory that time. Now I know better.)
    Again, I think something like this is very common in the real world. I'm working with a vendor product now that has an XML mapping of an identifier in the HTTP request to an "activitylet". Just a command by another (cute) name.
    jason adam
    Chicken Farmer ()
    Ranch Hand

    Joined: May 08, 2001
    Posts: 1932
    Not sure about the whole HTTP request stuff, I don't even get to see that far into the client side. I get passed an object to my stateless bean and go from there. That object holds the "type".
    I'm still trying to look at ways to incorporate the ideas discussed here, but I've got a good beginning, I'll let y'all know how things go.
     
    I agree. Here's the link: http://aspose.com/file-tools
     
    subject: Design Mental Roadblock