In a recent design we needed the client to add various types of orders into a back-end database through EJB.
Each order type is a subclass of Order. Examples of subclasses include AddOrder, MoveOrder and ConnectOrder.
So what we wanted the cliet code to look like was this:
public boolean addOrder(Order ord);
The above allows you to add all kinds of orders through a single ejb method.
In the ejb, we would like the call to be forwarded to a method handling a specific type of order.
private boolean addConcreteOrder(AddOrder order)
{...}
private boolean addConcreteOrder(MoveOrder order)
{...}
And so on.
But how do we get from addOrder to addConcreteOrder without resorting to a hard-to-maintain chain of instanceof calls?
This is what we did:
public boolean addOrder(Order ord)
{
Class paramTypes[] = {ord.getClass()};
Object params[] = {ord};
Method m = this.getClass().getDeclaredMethod("addConcreteOrder", paramTypes);
// invoke the appropriate addOrder version
boolean isOK = (boolean)m.invoke((Object)this, params);
return isOK;
}
Seems to work very well.
-
RTTI killer pattern. (46 messages)
- Posted by: han theman
- Posted on: April 22 2002 03:17 EDT
Threaded Messages (46)
- RTTI killer pattern. by Bibi Bibi on April 22 2002 09:36 EDT
- RTTI killer pattern. by Java Architect on April 22 2002 12:17 EDT
-
RTTI killer pattern. by han theman on April 22 2002 02:01 EDT
-
RTTI killer pattern. by Gal Binyamini on April 22 2002 05:24 EDT
-
RTTI killer pattern. by han theman on April 22 2002 05:40 EDT
-
RTTI killer pattern. by Gal Binyamini on April 22 2002 07:14 EDT
-
RTTI killer pattern. by han theman on April 23 2002 02:20 EDT
-
RTTI killer pattern. by Gal Binyamini on April 23 2002 05:01 EDT
-
RTTI killer pattern. by han theman on April 23 2002 06:00 EDT
-
RTTI killer pattern. by Gal Binyamini on April 24 2002 03:56 EDT
-
RTTI killer pattern. by han theman on April 24 2002 04:13 EDT
-
RTTI killer pattern. by Gal Binyamini on April 24 2002 05:04 EDT
-
RTTI killer pattern. by han theman on April 25 2002 05:30 EDT
- One confusion Han by Java Architect on April 26 2002 08:45 EDT
-
RTTI killer pattern. by han theman on April 25 2002 05:30 EDT
-
RTTI killer pattern. by Gal Binyamini on April 24 2002 05:04 EDT
-
RTTI killer pattern. by han theman on April 24 2002 04:13 EDT
-
RTTI killer pattern. by Gal Binyamini on April 24 2002 03:56 EDT
-
RTTI killer pattern. by han theman on April 23 2002 06:00 EDT
-
RTTI killer pattern. by Gal Binyamini on April 23 2002 05:01 EDT
-
RTTI killer pattern. by han theman on April 23 2002 02:20 EDT
-
RTTI killer pattern. by Gal Binyamini on April 22 2002 07:14 EDT
-
RTTI killer pattern. by han theman on April 22 2002 05:40 EDT
-
Use the command pattern by Tom Davies on April 28 2002 01:26 EDT
-
Use the command pattern by han theman on April 28 2002 02:00 EDT
- Use the command pattern by Tiberiu Fustos on April 28 2002 03:29 EDT
- Use the command pattern by Tom Davies on April 28 2002 11:35 EDT
-
Use the command pattern by han theman on April 28 2002 02:00 EDT
-
RTTI killer pattern. by Vlad Ender on April 28 2002 03:57 EDT
-
RTTI killer pattern. by Vlad Ender on April 28 2002 07:28 EDT
-
RTTI killer pattern. by han theman on April 29 2002 03:20 EDT
-
RTTI killer pattern. by Vlad Ender on April 29 2002 03:41 EDT
-
Strategy, Not Visitor by Adam Young on April 30 2002 05:36 EDT
-
Strategy, Not Visitor by Sakesun Roykiattisak on May 01 2002 09:06 EDT
-
Visitor solves RTTI by Jean-Christian Gagne on May 03 2002 03:41 EDT
-
Visitor solves RTTI by Dave Lorde on May 03 2002 06:12 EDT
- Visitor solves RTTI by Sakesun Roykiattisak on May 03 2002 10:31 EDT
- Visitor solves RTTI by Bob Lee on May 09 2002 01:58 EDT
-
Acyclic Visitor by Roger Cauvin on May 17 2002 09:50 EDT
- RTTI Killer /Acyclic Visitor by Angelo Schneider on May 29 2002 09:50 EDT
-
RTTI Killer /Acyclic Visitor by Angelo Schneider on May 29 2002 10:04 EDT
- RTTI Killer /Acyclic Visitor by Shankaran Krishnaswamy on May 30 2002 12:57 EDT
-
Visitor solves RTTI by Dave Lorde on May 03 2002 06:12 EDT
- Visotor by Bernd Eckenfels on March 08 2003 12:03 EST
-
Visitor solves RTTI by Jean-Christian Gagne on May 03 2002 03:41 EDT
-
Strategy, Not Visitor by Sakesun Roykiattisak on May 01 2002 09:06 EDT
-
RTTI killer pattern. by han theman on May 01 2002 03:31 EDT
- RTTI killer pattern. by Vlad Ender on May 02 2002 04:57 EDT
- RTTI killer pattern. by p huber on May 03 2002 07:54 EDT
-
Strategy, Not Visitor by Adam Young on April 30 2002 05:36 EDT
-
RTTI killer pattern. by Vlad Ender on April 29 2002 03:41 EDT
-
RTTI killer pattern. by han theman on April 29 2002 03:20 EDT
-
RTTI killer pattern. by Vlad Ender on April 28 2002 07:28 EDT
-
RTTI killer pattern. by Gal Binyamini on April 22 2002 05:24 EDT
-
RTTI killer pattern. by han theman on April 22 2002 02:01 EDT
- RTTI killer pattern. by Java Architect on April 22 2002 12:17 EDT
- RTTI killer pattern. by David Xu on April 22 2002 18:58 EDT
- RTTI killer pattern. by Sakesun Roykiattisak on April 23 2002 07:17 EDT
- RTTI killer pattern. by han theman on April 23 2002 07:50 EDT
- RTTI killer pattern. by Sakesun Roykiattisak on April 24 2002 06:28 EDT
- RTTI killer pattern. by han theman on April 23 2002 07:50 EDT
- RTTI killer pattern. by Tiberiu Fustos on April 24 2002 10:22 EDT
- RTTI killer pattern. by Brian Sayatovic on May 02 2002 07:00 EDT
- RTTI killer pattern. by Brian Sayatovic on May 02 2002 07:10 EDT
- RTTI killer pattern. by Lyndon Samson on May 03 2002 00:21 EDT
- RTTI killer pattern. by Jim Cakalic on May 09 2002 11:26 EDT
- Use a modification of the Command Pattern by Agusti Sanchez on December 27 2002 16:51 EST
-
RTTI killer pattern.[ Go to top ]
- Posted by: Bibi Bibi
- Posted on: April 22 2002 09:36 EDT
- in response to han theman
Since Order is the base class for all the orders, why don't you let the order to perform the add operation? In this way you eliminate the all the overhead that comes when using reflection. Of course I assume at this point the order is initialized.
Something like:
public void addOrder(Order order) throws OrderAddException
{
order.add();//override this mth for each order type
} -
RTTI killer pattern.[ Go to top ]
- Posted by: Java Architect
- Posted on: April 22 2002 12:17 EDT
- in response to Bibi Bibi
What you are proposing sounds fine from a pure "polymorphic" perspective. However, the original poster did not make it clear what 'Order' was. I have to assume an "Order" instance came from the client as a value object. In this event, one would not want to make one's value objects dependent on my EJB interface. No?
-
RTTI killer pattern.[ Go to top ]
- Posted by: han theman
- Posted on: April 22 2002 14:01 EDT
- in response to Java Architect
"Java Architect ", you are right. The idea is that the client should be allowed to do this:
void some_client_func()
{
if(some criterion)
{
MoveOrder order = new MoveOrder();
orderEjb.addOrder((Order)order);
}
else
if(some other criterion)
{
AddOrder order = new AddOrder();
orderEjb.addOrder((Order)order);
}
}
The EJB remote interface only exposes addOrder(Order); but this is fine due to the idiom described in the original post. So the idea is to simplify the EJB remote interface - instead of having to add an overloaded version each time a new order type is introduced to the system.
-
RTTI killer pattern.[ Go to top ]
- Posted by: Gal Binyamini
- Posted on: April 22 2002 17:24 EDT
- in response to han theman
This pattern uses RTTI. I don't know why the author describes it as eliminating the use of it. The instanceof operator is not worse, in terms of design and performance, than Reflection. I think this is an anti-pattern, because it promotes bad OO design and incorrect use of polymorphism. This design centralizes functionality to a single class instead of decentralizing it. As new types of orders are added, the bean allways needs to be changed.
I'm not sure about whether or not Order is a value object, but I do think that according to the way it is used in this pattern, it shouldn't be. The Order objects do not represent data: they represent the type of action that the client wishes to perform. Different subclasses of Order may themselves contain data that is required for the execution of these actions, but as far as the bean is concerned they all represent actions to be taken. This seems to me like a classic usecase for the Command pattern, which truely eliminates RTTI and decentralizes functionality to appropriate subclasses.
Gal -
RTTI killer pattern.[ Go to top ]
- Posted by: han theman
- Posted on: April 22 2002 17:40 EDT
- in response to Gal Binyamini
Gal,
Good points. It's not an RTTI killer, it's an instanceof killer ;-)
But the goal is merely to prevent this:
Bean:
public void addOrder(Order ord)
{
if(ord instanceof MoveOrder)
{ addSpecificOrder((MoveOrder)ord) ;}
else
if(ord instanceof AddOrder)
... and so on ...
}
Of course, you could add an overloaded version of addOrder for each order type on the bean, but in this case we want a single method handling all cases.
As for the Command pattern. Yes, I tend to employ it except in cases - like the above - where the EJB remote interface acts as an API for external systems. In an exposed API, the Command pattern is not very friendly.
Users of the server-side functionality (people in other projects, customers, etc) typically prefer straightforward interfaces rather than more complex frameworks like the Command, MVC, etc.
-
RTTI killer pattern.[ Go to top ]
- Posted by: Gal Binyamini
- Posted on: April 22 2002 19:14 EDT
- in response to han theman
Why do you think the Command pattern is less user friendly? In my view, as far as the client is concerned, this *is* a command pattern. The client wishes to perform a certain command (AddOrder, MoveOrder, etc) and in order to do so it calls addOrder(Order order). Order is, for all intents and purposes, a Command object: it specifies functionality. How you actually implement the mechanism of running these commands doesn't seem that important for the client: if you don't want to put server side code in the client side you can make the command object expose a way to get a handler for the command, or even maintain a mapping between command objects and handlers in some seperate class. IMO this will still isolate the functionality of specific commands, and will require less changes to the central bean code.
Gal -
RTTI killer pattern.[ Go to top ]
- Posted by: han theman
- Posted on: April 23 2002 02:20 EDT
- in response to Gal Binyamini
Gal.
No, this is not the Comman pattern. Look it up in GoF or whatever. It is more like a facade.
-
RTTI killer pattern.[ Go to top ]
- Posted by: Gal Binyamini
- Posted on: April 23 2002 17:01 EDT
- in response to han theman
I disagree, and I do have GoF's book right in front of me. Would you care to elaborate about why you think this is like a facade?
Gal -
RTTI killer pattern.[ Go to top ]
- Posted by: han theman
- Posted on: April 23 2002 18:00 EDT
- in response to Gal Binyamini
Facades hides the complexity of one or more subsystems, subcomponents/interfaces and provides to a (logical) client a simpler view of the world. A session facade, as defined by the J2EE blueprint catalogue, combines this with value objects to increase calls on the server.
So this is much more like a facade than the Command pattern which is about decoupling BOTH clients and suppliers completely.
Makes sense? -
RTTI killer pattern.[ Go to top ]
- Posted by: Gal Binyamini
- Posted on: April 24 2002 15:56 EDT
- in response to han theman
No. A facade pattern provides a unified interface to a set of distinct systems. This has nothing to do with this pattern. The bean where you have the addOrder method may itself be a facade, but that is not what this pattern is about. IMO it's like saying the steering-wheel is what makes your car go: yes, there is a steering-wheel in your car, but no, it's not what makes it go.
I also don't think that an object such as "AddOrder" can be considered a value object. It does not speficy value: it specifies functionality. It specifies what should happen: in particular, an order should be added. This is GoF's definition of a Command object, and I think it makes a lot of sense.
Gal -
RTTI killer pattern.[ Go to top ]
- Posted by: han theman
- Posted on: April 24 2002 16:13 EDT
- in response to Gal Binyamini
Gal, I think you misunderstand. AddOrder is a kind of order, it does not specifiy functionality. It's a kind of order in a telco, just like a MoveOrder is (an order to move, say, a subscriber from one central to another.)
My choice of example was terribly bad. Sorry about that.
I didn't say it was a facade, I said it was more like a facade than a command pattern. I know see that you interpreted AddOrder to be a Command due to its name when it actually is a value object.
No? -
RTTI killer pattern.[ Go to top ]
- Posted by: Gal Binyamini
- Posted on: April 24 2002 17:04 EDT
- in response to han theman
I understand. My comments about the Command pattern above were based on my understanding of your example, which were obviously wrong. I think now we both agree that this is neither a Command nor a Facade pattern. Right?
Gal -
RTTI killer pattern.[ Go to top ]
- Posted by: han theman
- Posted on: April 25 2002 05:30 EDT
- in response to Gal Binyamini
Right. -
One confusion Han[ Go to top ]
- Posted by: Java Architect
- Posted on: April 26 2002 08:45 EDT
- in response to han theman
In your example you said :
public void addOrder(Order order) throws OrderAddException
{
order.add();//override this mth for each order type
}
Please tell me that the 'order' instance of the value object does not make use of any EJBs period. I ask because I'm trying to understand what "adding the order" means regardless of whether it is a "AddOrder" or "MoveOrder".
Sorry for the late question.
Thanks
-
Use the command pattern[ Go to top ]
- Posted by: Tom Davies
- Posted on: April 28 2002 01:26 EDT
- in response to han theman
The point of this discussion is to find a way to move an invocation from the client side (where you can't invoke the code because you need server side resources) to the server side, in a way which doesn't mean changing the client/server interface every time you find a new bit of code to invoke.
The code below works.
The constructor takes the parameters of the operation (which must be serializable) and the execute method performs it on the server.
Simple.
Tom
interface Order implemets Serializable
{
void execute();
}
class AddOrder implements Order
{
public AddOrder(... information needed to execute...);
public void execute()
{
... put order in DB or whatever...
}
}
class XOrder ...
{
...
}
Client:
Order o = ... create some type of order ...
bean.do(o);
Server:
class xxxEJB {
public void do(Order o)
{
o.execute();
}
} -
Use the command pattern[ Go to top ]
- Posted by: han theman
- Posted on: April 28 2002 14:00 EDT
- in response to Tom Davies
The command pattern has been mentioned before, but it's actually not solving the problem.
The problem is to present a method to the client taking as a parameter the base class (or interface) of a hierarchy. What actions to take on the EJB-side should be based on the actual parameter (typically a subclass.)
What the command pattern does is to move the invocation on the actual parameter - actions inside the execute method of the value object. Which is NOT what we want.
-
Use the command pattern[ Go to top ]
- Posted by: Tiberiu Fustos
- Posted on: April 28 2002 15:29 EDT
- in response to han theman
I was just wondering if you tried simply overloading the addOrder method in another EJB? Did you try it and it did not work ? I mean you have one EJB to be called from the client, with only one
void addOrder(Order order)...;
method and another EJB with overloaded addOrder(...) methods for each subclass of the Order value object. Your client-facing EJB would only have one interface, and the other EJB (only invoked from the server) would have to be changed when adding new order types. The first one would just delegate the call to the other one.
Did you try this and it did not work because of serialisation or other problems? It is essentially what you are doing, just that the runtime could optimise it and you don't need to use reflection.
Tibi -
Use the command pattern[ Go to top ]
- Posted by: Tom Davies
- Posted on: April 28 2002 23:35 EDT
- in response to han theman
What the command pattern does is to move the invocation on the
>actual parameter - actions inside the execute method of the
>value object. Which is NOT what we want.
Can you explain why you don't want this?
If each type of order has some unique behaviour associated with it, the concrete Order subclass seems like a good place to have that behaviour.
Tom -
RTTI killer pattern.[ Go to top ]
- Posted by: Vlad Ender
- Posted on: April 28 2002 15:57 EDT
- in response to han theman
Well, while the Order might be a pure value Object, for your code to work your concrete orders can't - at least they have to implement the method you are trying to call.
So what's wrong with having this method on order (say as an abstract method), and override them in a subclass?
In fact, your code does more or less what Java's polymorphism would (more efficiently) do. Except for type safety, because if your Order class does not declare your method, your code could, if extended, throw an exception when the subclass would not implement the method.
From maintenace point of view, it's also quite ugly - someone comming in in two years might be quite confused.
Regards,
Vlad -
RTTI killer pattern.[ Go to top ]
- Posted by: Vlad Ender
- Posted on: April 28 2002 19:28 EDT
- in response to Vlad Ender
Oops - I've noticed only now that the methods handling the objects are on the bean - sorry about that & I take some of the points back.
You still can just call your addOrder method (as you have both OrderX and OrderY param ones) and the correct one will be called. So you still have a language feature you can use.
-
RTTI killer pattern.[ Go to top ]
- Posted by: han theman
- Posted on: April 29 2002 03:20 EDT
- in response to Vlad Ender
Vlad,
The entire idea is to present to the EJB client a simpler interface than overloading will present. The *entire* objective is to present a function taking a base class or interface (such as IOrderType), but the functionality is based on the actual parameter.
Facade nor the Command pattern nor overloading achieves this, but the code in the original posting actually achives this.
Your point about lower type safety is valid (potential error when the actual-parameter-hierarchy is extended), but this is one of those trade-offs...
I would normally not do it, but in this case "third-party people" will be using the bean and they want it as simple as possible.
-
RTTI killer pattern.[ Go to top ]
- Posted by: Vlad Ender
- Posted on: April 29 2002 15:41 EDT
- in response to han theman
Well, you don't have to overload the public bean method. In fact, you already have two overloaded private methods
"
private boolean addConcreteOrder(AddOrder order)
{...}
private boolean addConcreteOrder(MoveOrder order)
{...}
"
What I think you should do then have your add order as:
public boolean addOrder(Order ord)
{
// this calls the appropriate private method
// depending on the actual class of ord
return addConcreteOrder(ord);
}
to catch extending w/o adding methods, you could have also
private boolean addConcreteOrder(Order order) {
throw IllegalArgumentException("Order class " + order.class + " not supported.");
}
which would be called if the JRE would fail to find an overloaded method.
An entirely different way of doing this would be to have a factory that for each class of orders (as value objects) creates the appropriate business object that can validate it etc.. If you have more than a couple of orders and/or will extend a lot, I would think this to be more appropriate solution as it would decouple the logic of each order to its own class -> better maintenability and could have easier extensibility (if the factory is externaly configurable).
Regards,
Vlad -
Strategy, Not Visitor[ Go to top ]
- Posted by: Adam Young
- Posted on: April 30 2002 17:36 EDT
- in response to Vlad Ender
Sakesun: You would use visitor when you want to be able to modify the operation, or add new operations. In this case, he wants to be able to perform the same operation on multiple objects. The strategy pattern is the appropriate pattern.
Definition: Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independantly from clients thgat use it.
interface OrderProcessor{
void process (Order o);
}
class AddOrderProcessor{
void process (Order o){
}
}
class MoveOrderProcessor{
void process (Order o){
}
etc.
Simplest implementation is to create a Map
Map dispatchMap = new HashMap();
dispatchMap.put(MoveOrder.class,new MoveOrderProcessor());
dispatchMap.put(AddOrder.class,new AddOrderProcessor());
etc
and to call from EJB
OrderProcessor op = (OrderProcessor)dispatchMap.get(order.class);
op.processOrder(order);
A little more elegant, and enforces programming with interfaces. Should be significantly more effecient as well.
However, if all orders have basically the same information in them, they can contain the name of the Strategy implementation to use themselves:
order.getProcessorName("com.theserverside.AddOrderProcessor");
In this case, I agree with Sakesun. Have a generic order object, which accepts a visitor. Now the visitor will implement whether it is an Add, Move, Connect, and, once you decide you need it, disconnect. But I am assuming the orders are fundamentally different.
-
Strategy, Not Visitor[ Go to top ]
- Posted by: Sakesun Roykiattisak
- Posted on: May 01 2002 21:06 EDT
- in response to Adam Young
I'm not very good in English. But, after I've reread the topics, I still believe visitor is appropriate for this situation. The author want to route from generic "addOrder(Order o)" to specific "addConcreteOrder1,2,3,...". The Visitor pattern can avoid rtti(instanceof) usage by providing a method <accept()> for client to ask for the object type at the base-class. The Visitor comsume this type-information and act upon. Any "Processor" for this object family can perform as a Visitor by extending Visitor class either directly or using anonymous adapter as being used in my example. -
Visitor solves RTTI[ Go to top ]
- Posted by: Jean-Christian Gagne
- Posted on: May 03 2002 03:41 EDT
- in response to Sakesun Roykiattisak
I agree with Sakesun. Visitor is just fine to solve RTTI issue. Example :
public abstract Order {
public void abstract acceptVisitor(Visitor visitor);
}
public MoveOrder extends Order {
public void abstract acceptVisitor(Visitor visitor) {
visitor.visitMoveOrder(this);
}
}
public interface Visitor {
public void visitAddOrder(AddOrder order);
public void visitMoveOrder(MoveOrder order);
...
}
public class MyStatelessBean implements OrderVisitor {
public boolean add(Order order) {
order.acceptVisitor(this);
}
public void visitAddOrder(AddOrder order) {
// code specific to AddOrder
}
public void visitMoveOrder(MoveOrder order) {
// code specific to MoveOrder
}
...
}
The Visitor interface could be implemented in another class or private inner class if you don't want those public methods in your remote bean.
Note that the goal was to solve "instanceof" issue. The Visitor pattern does it nicely and simply, without external configuration.
If you expect more and more Order types to be defined in the future, Visitor may not be appropriate for maintenance reasons (especially in a J2EE environment) but this is another issue, solved by another pattern. In this case, a configurable map associating "order type" to "order processor" looks like a good choice.
JCG
-
Visitor solves RTTI[ Go to top ]
- Posted by: Dave Lorde
- Posted on: May 03 2002 06:12 EDT
- in response to Jean-Christian Gagne
I agree that Visitor is the solution of choice here. It simply and cleanly resolves the type without RTTI, which was the original problem. By delegating the type resolution to the visited object and keeping the type-specific functionality in the Visitor, it also retains the value-only nature of the visited object.
It does have disadvantages where new types are regularly being added to the system, but that doesn't seem to be the case here.
Dave -
Visitor solves RTTI[ Go to top ]
- Posted by: Sakesun Roykiattisak
- Posted on: May 03 2002 10:31 EDT
- in response to Dave Lorde
I think adding new order-type to the system show another advantage of Visitor pattern. If the Visitor is interface or pure abstract class, whenever a new order-type is added the compiler can point out all the effected class at compile time. That is my favorite feature of Visitor. -
Visitor solves RTTI[ Go to top ]
- Posted by: Bob Lee
- Posted on: May 09 2002 13:58 EDT
- in response to Jean-Christian Gagne
I have implemented a reflective visitor that works quite nicely, especially if you don't have control over the implementation of the classes you are visiting. It also makes maintenance a little easier.
Taking from previous examples, you would implement something like this:
class OrderVisitor extends ReflectiveVisitor {
public void visit(AddOrder order) {...}
public void visit(MoveOrder order) {...}
public void visit(DeleteOrder order) {...}
...
}
Clients can invoke visit(Object) (implemented by ReflectiveVisitor), and the invocation will automatically delegate to the proper method.
This is also *very* useful if you have no way of knowing what type of object you are working with. You can use ReflectiveVisitor to automatically upcast the object and delegate to the proper implementation.
The code is GPLed, you can get it @ http://crazybob.org/visitor.zip.
Thanks,
Bob
-
Acyclic Visitor[ Go to top ]
- Posted by: Roger Cauvin
- Posted on: May 17 2002 09:50 EDT
- in response to Jean-Christian Gagne
If you expect more and more Order types to
> be defined in the future, Visitor may not
> be appropriate for maintenance reasons
You can use Acyclic Visitor to avoid the maintenance problems. See http://www.objectmentor.com/resources/articles/acv.pdf. It actually uses instanceof, but in a clever manner that avoids the typical problems associated with runtime type checking.
-
RTTI Killer /Acyclic Visitor[ Go to top ]
- Posted by: Angelo Schneider
- Posted on: May 29 2002 21:50 EDT
- in response to Roger Cauvin
Hi,
IMHO the solution involves basicly two patterns:
a) Command Patter, yes, it is a command :-) but the Command Pattern does not solve the problem the requester has.
b) Visitor Pattern, yes, its also the Visitor Pattern, however if we hear "visitor" we think about a set of polymorphic data, an iterator over that data and a "manipulation object" visiting each data item.
The Command Pattern is allready (incompletly)applied. By making the Order class a value Object or Data Transfer Object, it is basicly a Command.
However the Command is interpreted externaly via RTTI or instance_of, both should be avoided.
There is a method public boolean addOrder(Order ord); desigend to interprete the Command. In a pure Command Pattern the addOrder(Order ord) method would call a method: "execute()" of the Command.
Indeed that execute method is the accept method of the visitor!
What is needed?
At first I give the class with the addOrder(Order ord) method a name. OrderManager sounds good to me.
Now below you see the original handling code, I jsut add a class name to it:
class OrderManager {
private boolean addConcreteOrder(AddOrder order)
{...}
private boolean addConcreteOrder(MoveOrder order)
{...}
public boolean addOrder(Order ord) {
Class paramTypes[] = {ord.getClass()};
Object params[] = {ord};
Method m = this.getClass().getDeclaredMethod("addConcreteOrder", paramTypes);
// invoke the appropriate addOrder version
boolean isOK = (boolean)m.invoke((Object)this, params);
return isOK;
}
} // class
Now we can add a method to the class Order! If we would insist on the namings of the Command Pattern that method would be called execute. If we would insist on the naming of the Visitor Pattern we would call that method accept. In this case I like to use a different name to reflect that we have not a "pure" pattern here. So I call it perform.
class Order {
abstract boolen perform(OrderManager mg);
// ... your stuff here
}
For a concrete Order like AddOrder, the implementation would look like this:
class AddOrder {
boolen perform(OrderManager mg) {
}
-
RTTI Killer /Acyclic Visitor[ Go to top ]
- Posted by: Angelo Schneider
- Posted on: May 29 2002 22:04 EDT
- in response to Roger Cauvin
Hi,
IMHO the solution involves basicly two patterns:
a) Command Pattern, yes, it is a command :-) but the Command Pattern does not solve the problem the requester has.
b) Visitor Pattern, yes, its also the Visitor Pattern, however if we hear "visitor" we think about a set of polymorphic data, an iterator over that set/graph and a "manipulation object" visiting each data item. This is not the case in this problem! But still applies to the solution!
The Command Pattern is allready (incompletly)applied. By making the Order class a value Object or Data Transfer Object, it is basicly a Command.
However the Command is interpreted externaly via RTTI( instance_of or reflection), that should be avoided.
There is a method public boolean addOrder(Order ord); desigend to interprete the Command. In a pure Command Pattern the addOrder(Order ord) method would call a method: "execute()" of the Command.
Indeed that execute method is the accept method of the visitor!
What is needed?
At first I give the class with the addOrder(Order ord) method a name. OrderManager sounds good to me.
Below you see the original handling code, I just add a class name to it:
class OrderManager {
private boolean addConcreteOrder(AddOrder order)
{...}
private boolean addConcreteOrder(MoveOrder order)
{...}
public boolean addOrder(Order ord) {
Class paramTypes[] = {ord.getClass()};
Object params[] = {ord};
Method m = this.getClass().getDeclaredMethod("addConcreteOrder", paramTypes);
// invoke the appropriate addOrder version
boolean isOK = (boolean)m.invoke((Object)this, params);
return isOK;
}
} // class
Now we can add a method to the class Order! If we would insist on the namings of the Command Pattern that method would be called execute. If we would insist on the naming of the Visitor Pattern we would call that method accept. In this case I like to use a different name to reflect that we have not a "pure" pattern here. So I call it perform.
class Order {
abstract boolen perform(OrderManager mg);
// ... your stuff here
}
For a concrete Order like AddOrder, the implementation would look like this:
class AddOrder {
boolen perform(OrderManager mg) {
mg.addConcreteOrder(this);
}
}
The above OrderManager would be changed like this:
class OrderManager {
private boolean addConcreteOrder(AddOrder order)
{...}
private boolean addConcreteOrder(MoveOrder order)
{...}
public boolean addOrder(Order ord) {
return order.perform(this);
}
} // class
Conclusion:
We use a Command to be sent from the client to the server.
The server likes to interprete the command basing on the concrete type of the command. The pure Command pattern would isolate the "action" into the command, but we have the action on the server side.
Solution:
The reciever of the Commands is a Visitor!!! Yes, the visitor is passivly iterating over the incomming Commands. So lets use double dispatch from the CommandManager(the Visitor) via the Order(The Command) back to the CommandManager. The addConcreteOrder(XYZOrder ord) method is the visit method of the Visitor Pattern.
Drawbacks:
In the sketched solution all Commands need to implement the same Order interface or inherit from an abstract Order base class.
The use of 'this' may not work under all conditions in an EJB environment.
Regards,
angel'o'sphere -
RTTI Killer /Acyclic Visitor[ Go to top ]
- Posted by: Shankaran Krishnaswamy
- Posted on: May 30 2002 12:57 EDT
- in response to Angelo Schneider
See
https://www.theserverside.com/resources/article.jsp?l=ClassLoading
Especially note the statements :
<br>
>Note that a class loaded at a given level in the hierarchy
>may not reference any classes loaded at a lower level in
>the hierarchy. Stated another way, a class loader has no
>visibility into classes loaded by its descendants.
<br>
>In figure 1, if class Foo is loaded by class loader B, and
>Foo depends on class Baz, then class Baz must be loadable
>by either class loader A or B. If Baz is only visible to
>class loader C or D, then a ClassNotFoundException will
>occur. If the class Bar is visible to two sibling class
>loaders (e.g., C and D in Figure 1) but not to their
>parent class loaders, and if a request for Bar is sent to
>both sibling class loaders, then each class loader will
>load its own version of the class. Instances of Bar based
>on class loader C will not be type compatible with
>instances based on class loader D.
<br>
<br>
The Visitor pattern in the static case makes parameters like - (Addorder order) - dependent on a class like the Handlers.
<br>
The parameters are value objects and could potentially be passed from a web container within the same JVM (in many App servers).
<br>
If the parameter classes are to be visible to both EJBs and Servlets, they need to be loaded by a parent ClassLoader to both of these or by a ClassLoader visible to both.
<br>
In an EAR deployment as explained in the above link, this is automatically taken care in the case where EJBS are deployed in the same ear as the dependent Servlets and Jsps.
<br>
If Servlets and JSPs are in different JVMS and use RMI/CORBA to talk to EJB, I expect this to be not an issue.
<br>
But in cases where this is not true , shared objects DTO value objects will need to be deployed in a System or a library class loader path that is a parent all ear class loaders. Then all the EJBs in various ears and Servlets in various wars will use these DTO objects from a single ClassLoader and it will be type compatible. Otherwise if there are multiple deployments of DTO objects one in an ear and one in war, the Servlets/JSPs won't be able to cast the Value Objects returned by EJBs and EJBs won't be able to cast the Value Objects passed by Servlets.
<br>
BUT,
This causes a problem, if visitor handler classes are deployed within an ear. DTO value objects deployed in a parent class Loader path CANNOT refer to them.
<br>
How dow we get around this without the ear file restriction , i.e all dependent Servlets need to be in the same ear. We are having Enterprise level EJBS
to be shared by many clients who may be deployed in the same JVM. -
Visotor[ Go to top ]
- Posted by: Bernd Eckenfels
- Posted on: March 08 2003 00:03 EST
- in response to Sakesun Roykiattisak
A Visitor is a single method applied to multiple data objects.
Multiple Commands applied to the same controller is a command pattern. -
RTTI killer pattern.[ Go to top ]
- Posted by: han theman
- Posted on: May 01 2002 15:31 EDT
- in response to Vlad Ender
"Well, you don't have to overload the public bean method. In fact, you already have two overloaded private methods
[snip]"
That doesn't work. -
RTTI killer pattern.[ Go to top ]
- Posted by: Vlad Ender
- Posted on: May 02 2002 16:57 EDT
- in response to han theman
yep, you're right - I forgot that Java does all the bindings at the compile time :( (shouldn't be getting spoiled). Sorry about that.
Vlad -
RTTI killer pattern.[ Go to top ]
- Posted by: p huber
- Posted on: May 03 2002 07:54 EDT
- in response to Vlad Ender
The "overwrite only private method" approach won't work.
The public method which takes an BaseClass object as argument sets the compiletime-type to BaseClass, so allways the the wrong method is going to be called -
RTTI killer pattern.[ Go to top ]
- Posted by: David Xu
- Posted on: April 22 2002 18:58 EDT
- in response to han theman
I would recommend another approach based on the broker pattern or COR pattern.
In the manager constructor, it will create the order handler queue. When the add order request comes, it will iterate through the queue to look up an appropriate handler for the order. We can build the handler queue dynamiclly based on a small configuration file.
Interface Handler {
public boolean add(order);
}
Mgr {
public Mgr() {
//Step 1. create the instance of the handlers
//Step 2. Place the instance in the handling queue.
}
public boolean addOrder(Order ord) {
while(it.hasNext()) {
Handler handler = (Handler)it.next();
if (handler.add(order))
break;
}//while
}
}//Mgr
public class XXXHandler implements Handler {
public boolean add(order) {
if (order instanceof XXX)
//do operation.
return true;
else
return false;
}
}//XXXHandler
If we want multiple handling for the order, simply remove the break statement.
The major benefit of this approach is that it can expend easily.
-
RTTI killer pattern.[ Go to top ]
- Posted by: Sakesun Roykiattisak
- Posted on: April 23 2002 07:17 EDT
- in response to han theman
What's wrong with "visitor" or "multiple dispatching" ? Those patterns was supposed to solve this problem.
-
RTTI killer pattern.[ Go to top ]
- Posted by: han theman
- Posted on: April 23 2002 07:50 EDT
- in response to Sakesun Roykiattisak
Could you elaborate, Sakesun ? -
RTTI killer pattern.[ Go to top ]
- Posted by: Sakesun Roykiattisak
- Posted on: April 24 2002 06:28 EDT
- in response to han theman
Something like this :-
public class OrderSystem {
public static void main(String[] args) {
OrderProcessor processor = new OrderProcessor();
processor.saveOrder(new FirstKindOrder());
processor.saveOrder(new SecondKindOrder());
processor.saveOrder(new ThirdKindOrder());
processor.deleteOrder(new FirstKindOrder());
processor.deleteOrder(new SecondKindOrder());
processor.deleteOrder(new ThirdKindOrder());
}
static abstract class Order {
abstract void accept(Visitor visitor);
static abstract class Visitor {
abstract void visit(FirstKindOrder order);
abstract void visit(SecondKindOrder order);
abstract void visit(ThirdKindOrder order);
}
}
static class FirstKindOrder extends Order {
void accept(Visitor visitor) {visitor.visit(this);}
public String toString() {return "<FirstKind>";}
}
static class SecondKindOrder extends Order {
void accept(Visitor visitor) {visitor.visit(this);}
public String toString() {return "<SecondKind>";}
}
static class ThirdKindOrder extends Order {
void accept(Visitor visitor) {visitor.visit(this);}
public String toString() {return "<ThirdKind>";}
}
static class OrderProcessor {
public void saveOrder(Order order) {
order.accept(new Order.Visitor() {
void visit(FirstKindOrder order)
{ System.out.println("Save FirstKind : " + order); }
void visit(SecondKindOrder order)
{ System.out.println("Save SecondKind : " + order); }
void visit(ThirdKindOrder order)
{ System.out.println("Save ThirdKind : " + order); }
});
}
public void deleteOrder(Order order) {
order.accept(new Order.Visitor() {
void visit(FirstKindOrder order)
{ System.out.println("Delete FirstKind : " + order); }
void visit(SecondKindOrder order)
{ System.out.println("Delete SecondKind : " + order); }
void visit(ThirdKindOrder order)
{ System.out.println("Delete ThirdKind : " + order); }
});
}
}
}
-
RTTI killer pattern.[ Go to top ]
- Posted by: Tiberiu Fustos
- Posted on: April 24 2002 10:22 EDT
- in response to han theman
So what we wanted the client code to look like was this:
>public boolean addOrder(Order ord);
>The above allows you to add all kinds of orders through a
>single ejb method.
>In the ejb, we would like the call to be forwarded to a method
>handling a specific type of order.
>private boolean addConcreteOrder(AddOrder order)
>private boolean addConcreteOrder(MoveOrder order)
>{...}
Is everybody missing the point here, or it's just me?
You have to overload your method addOrder like this:
public boolean addOrder (ConcreteOrderA order);
public boolean addOrder (ConcreteOrderB order);
You client just calls:
bean.addOrder(new ConcreteOrderA());
The rest is basic Java polymorphism. You need to add a new order type, add a new addOrder() method overload. No instanceOf, no RTTI.
The only problem I see with this is that if you change your remote interface, you have to redeploy the client code as well. But again, if you add a new order type, don't you have to update both the client and the server? Alternatively, you could forward the call to a second EJB, which implements the polymorphic interface, so that the dispatch takes place only on the server, so you don't need to redeploy everything to the client when you add a new type of Order.
Cheers,
Tibi -
RTTI killer pattern.[ Go to top ]
- Posted by: Brian Sayatovic
- Posted on: May 02 2002 07:00 EDT
- in response to han theman
What about loading handler's reflectively?
public interface OrderHandler {
public boolean add(Order order);
}
public class Bean {
} -
RTTI killer pattern.[ Go to top ]
- Posted by: Brian Sayatovic
- Posted on: May 02 2002 07:10 EDT
- in response to han theman
What about loading handler's reflectively?
public interface OrderHandler {
public boolean add(Order order);
}
public interface Order {
}
public class Bean {
Map handlers = new HashMap();
public void addOrder(Order order) {
Class orderClazz = order.getClass();
if(Order.isAssignableFrom(orderClazz) && !Order.class.equals(orderClazz)) {
OrderHandler handler = getHandler(orderClazz);
if(handler != null) {
handler.add(order);
} else {
// Error!
}
} else {
// Error!
}
}
protected OrderHandler getHandler(Class clazz) {
OrderHandler handler = (OrderHandler)handlers.get(clazz);
if(handler == null) {
handler = getHandlerReflectively(clazz);
if(handler == null) {
Class superClazz = clazz.getSuperclass();
if(Order.isAssignableFrom(superClazz) && !Order.class.equals(superClazz)) {
handler = getHandler(superClazz);
}
}
}
if(handler != null) {
handlers.put(clazz, handler);
}
return handler;
}
protected OrderHandler getHandlerReflectively(Clazz clazz) {
String handlerClassName = clazz.getName() + "Handler";
Handler handler = null;
try {
Class handlerClazz = Class.forName(handlerClassName);
if(OrderHandler.isAssignableFrom(handlerClazz) && !handlerClazz.isInterface()) {
handler = (OrderHandler)handlerClazz.newInstance();
}
} catch(Exception e) {
}
return hadler;
}
} -
RTTI killer pattern.[ Go to top ]
- Posted by: Lyndon Samson
- Posted on: May 03 2002 00:21 EDT
- in response to han theman
You've avoided a hard to maintain chain of instanceof with a hard to maintain group of methods in the one class?
As someone mentioned you should have each ordertype handled by its own class and use something dynamic ( like a properties file ) to associate AddOrder->AddOrderProcessor.class etc.
String proc = p.getProperty(order.getClass().getName()+".processor");
Class pc = Class.forName(proc);
OrderProcessor op = (OrderProcessor)(pc.newInstance());
op.invoke(Order);
-
RTTI killer pattern.[ Go to top ]
- Posted by: Jim Cakalic
- Posted on: May 09 2002 11:26 EDT
- in response to Lyndon Samson
I'm inclined to agree with Lyndon's approach of dynamically associating a handler/processor class with each concrete subclass/implementor or Order.
Perhaps the design pattern we're talking about applying is Strategy? You have a number of related 'algorithms' that can be selected and interchanged as appropriate. As a starting point, your EJB could be the 'Context' which chooses the appropriate strategy. 'Strategy' itself is the interface; something like 'OrderHandler' maybe. Finally, the 'ConcreteStrategy' implementations perform the real work -- AddOrderHandler, MoveOrderHandler, etc. If the handlers need access to EJB resources, the EJB could pass an instance of itself or EJBContext or whatever is appropriate. I think it would be ok for the handlers to be dependent on the EJB environment.
Using this design, the EJB could lookup the name of the ConcreteStrategy class to be used given the class name of the Order instance supplied. An instance could be instantiated and then the handler method invoked. I think this provides for 'true' polymorphism. It should be easier to extend for new Order subclasses because it _only_ involves writing new code -- a ConcreteStrategy for the new Order implementation -- and modification of deployment options. No existing EJB code needs to change.
A refinement to using property files would be to use the EJB environment-naming context (for EJB 1.1) or the EJBContext (EJB 1.0) to find this mapping. This is a 'better' deployment option than using property files.
Also, instead of using the simple Class.forName(classname) method, my understanding is that the Class.forName(classname, initialize, classloader) method should be used with classloader being Thread.currentThread().getContextClassLoader().
Best regards,
Jim Cakalic -
Use a modification of the Command Pattern[ Go to top ]
- Posted by: Agusti Sanchez
- Posted on: December 27 2002 16:51 EST
- in response to han theman
I was faced recently with the same problem as yours.
I needed to:
- process a collection of subclasses
using a common interface avoiding
overloading
- the action code should be in the server-side
classes not in the client-side value object subclasses
The solution turned out to be rather cumbersome (but it works!):
CLIENT-SIDE VALUE OBJECTS:
These classes follow the Command pattern but delegating
the action to a server-side object.
public abstract class Order
{
....
abstract void processOrder(OrderProcessor op);
}
public final class AddOrder extends Order
{
....
void processOrder(OrderProcessor op)
{
op.processOrder(this);
}
}
public final class MoveOrder extends Order
{
....
void processOrder(OrderProcessor op)
{
op.processOrder(this);
}
}
SERVER SIDE CLASSES:
The server side classes consiste of a package-scope interface
that declares de OrderProcessor interface.
The OrderProcessor implementation is a private object
inside the OrderBroker (maybe an EJB).
The OrderBroker presents an "immutable" interface out to the client.
(package-scope)
interface OrderProcessor
{
void processOrder(AddOrder o);
void processOrder(MoveOrder o);
}
public final class OrderBroker
{
private OrderProcessor op = new OrderProcessor()
{
public void processOrder(AddOrder o)
{
System.out.println("Processing "+o);
}
public void processOrder(MoveOrder o)
{
System.out.println("Processing "+o);
}
};
// IMMUTABLE INTERFACE METHOD
public void processOrders(Order[] o)
{
for (int i = 0; i < o.length; i++)
{
o[i].processOrder(op);
}
}
}
CLIENT CODE:
public class Client
{
public static void main(String[] args)
{
Order o1 = new AddOrder();
Order o2 = new MoveOrder();
OrderBroker broker = new OrderBroker();
broker.processOrders(new Order[]{o1, o2});
}
}