开发者

Typesafe delegation without instanceof

I have a service routine:

filter(List<Criterion> criteria);

Is there a good way to internally dispatch the method call to a typesafe implementation of a specific Criteria without involving instanceof and without cluttering the API.

I'd like something like the following (which naturally doe开发者_StackOverflowsn't work though):

filter(List<Criterion> criteria) {
   for (Criterion c : criteria) {
       dispatch(c);
   }
 }

dispatch(FooCriterion c) {...}
dispatch(BarCriterion c) {...}


Although it might be viewed as cluttering, something like the Visitor pattern can be used (using the principle of double-dispatch):

public class Dispatcher {
    void dispatch(FooCriterion foo) { .. }
    void dispatch(BarCriterion bar) { .. }
}

class FooCriterion implements Criterion {
   void visit(Dispatcher d) {
      d.dispatch(this);
   }
}

Dispatcher d = new Dispatcher();
for (Criterion c : criteria) {
   c.visit(d);
}


Sounds like you want to use the visitor pattern.

The wikipedia article contains a Java example.


Visitor pattern is the preferred solution if you want one and only one instance of the method to be called according to the object's type.

However, if you have C inherits B inherits A and you want your objects to call the method for their class type and all inherited (for example), you must use isAssignableFrom(...).

For example, if your object is if type B and you want it to call dispatch(B obj) and dispatch(C obj), you need to enclose those calls with respectively:

if (A.isAssignableFrom(obj.getClass())) { dispatchA(obj); }
if (B.isAssignableFrom(obj.getClass())) { dispatchB(obj); }
if (C.isAssignableFrom(obj.getClass())) { dispatchC(obj); }

Don't ever use instanceof, because it would not work in this case.


I have only this to suggest with some duplication.
private void filter(List<Criterion> criteria) 
{
 for(FooCriterion c : fetchFooCriterias(criteria))
 {
  dispatch(c);
 }
}
private List<FooCriterion> fetchFooCriterias(List<Criterion> criteria) 
{
 List<FooCriterion> fc = new ArrayList<FooCriterion>();
 for(Criterion c:criteria)
 {
  if(c instanceof BarCriterion)
  {
   fc.add((FooCriterion)c);
  }
 } 
 return fc;
}

Another solution could be using following commons classes but it might clutter your code. import org.apache.commons.collections.Predicate; import org.apache.commons.collections.iterators.FilterIterator;

Try it out.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜