BroadaxProof

Please verify my entries by your thinking like broad ax.

Dispatcher Pattern (the resolving for Expression problem of Visitor pattern by Java).

Introduction

Some software engineers considers that Visitor Pattern is the most difficult in design pattern. I got understood Visitor Pattern on beginning of May 2018. The understanding is as follow:

Visitor Pattern is used when we want to switch the expression for a collection that contains different elements without branching by if statement.

Expression Problem

I learned that Visitor Pattern has an unresolved problem known as Expression Problem by the article linked below.

Rethink of Visitor Pattern (in Japanese language)

I understood that the main point of Expression Problem is the following points.

When an element is added, the existing class has to be rewritten, in order to add expression.

I thought "It would be great if I could design Visitor Pattern that can add elements and expressions without rewritten the existing class". After little thinking, I found a design. It is the Dispatcher Pattern introduced in this article.

Design of Dispatcher Pattern

The protagonist of the Dispatcher Pattern is the Dispatcher class. All expression classes implements the Expression interface and is delegated to the Dispatcher class. The Dispatcher class dispatch all the elements passed from the client using "polymorphism by delegation".

Class and Interface

Item class

  • These are any object.
class ItemA
{
    String getAnimalName() {
        return "cat";
    }
}

class ItemB
{
    String getColorName() {
        return "purple";
    }
}

class ItemC
{

}

class ItemD
{

}

class ItemE
{

}

Expression class

  • All expression classes used in the Dispatcher pattern implement the Expression interface. Common methods of all expression classes are defined on the interface that inherits Expression. Exp is defined in this example.
interface Expression<T>
{

}

interface Exp<T> extends Expression<T>
{
    void print(T item);
}

class ExpA implements Exp<ItemA>
{
    @Override
    void print(ItemA item) {
        Log.d(
                "DEMO",
                "I am ExpressionA, treat ItemA only. Animal name is " + item.getAnimalName()
        );
    }
}

class ExpB implements Exp<ItemB>
{
    @Override
    void print(ItemB item) {
        Log.d(
                "DEMO",
                "I am ExpressionB, treat ItemB only. Color name is " + item.getColorName()
        );
    }
}

class ExpX<T> implements Exp<T>
{
    @Override
    void print(T item) {
        Log.d(
                "DEMO",
                "I am ExpressionX, treated " + item.getClass().getSimpleName() + "."
        );
    }
}

Dispatcher class

  • AbstractDispatcher is generic superclass in Dispatcher pattern. Dispatcher inheriting AbstractDispatcher implements Exp inheriting Expression.
abstract class AbstractDispatcher<X extends Expression>
{
    private Map<Class<?>, X> expressions = new HashMap<>();

    @SuppressWarnings("unchecked")
    public <T> void set(Class<T> clazz, Expression<T> expression) {
        expressions.put(clazz, (X) expression);
    }

    @Nullable
    protected final X dispatch(@NonNull Object item) {
        return expressions.get(item.getClass());
    }
}

class Dispatcher<I, X extends Exp<I>>
        extends AbstractDispatcher<X>
        implements Exp<I>
{
    @Override
    void print(I item) {
        X exp = dispatch(item);
        if (exp == null) {
            Log.d(
                "DEMO",
                "Unknown item: " + item.getClass().getSimpleName()
            );

            return;
        }

        exp.print(item);
    }
}

Example and Logging

// setup expressions.
Dispatcher<Object, Exp<Object>> dispatcher = new Dispatcher<>();
{
    dispatcher.set(ItemA.class, new ExpA());
    dispatcher.set(ItemB.class, new ExpB());
    dispatcher.set(ItemC.class, new ExpX<>());
    dispatcher.set(ItemD.class, new ExpX<>());

    // dispatcher.set(ItemA.class, new ExpB());     // error
}

// setup elements.
List<Object> list = new ArrayList<>();
{
    list.add(new ItemB());
    list.add(new ItemB());
    list.add(new ItemC());
    list.add(new ItemA());
    list.add(new ItemB());
    list.add(new ItemC());
    list.add(new ItemA());
    list.add(new ItemD());
    list.add(new ItemE());
}

// execute.
for (Object it : list) {
    dispatcher.print(it);
}
I am ExpressionB, treat ItemB only. Color name is purple
I am ExpressionB, treat ItemB only. Color name is purple
I am ExpressionX, treated ItemC.
I am ExpressionA, treat ItemA only. Animal name is cat
I am ExpressionB, treat ItemB only. Color name is purple
I am ExpressionX, treated ItemC.
I am ExpressionA, treat ItemA only. Animal name is cat
I am ExpressionX, treated ItemD.
Unknown item: ItemE

Example of Adding elements and expressions

In Dispatcher Pattern, can optionally add elements and expressions to elements as in the example below. We can see that there is no need to rewrite the existing code.

class ItemF
{

}

class ExpF implements Exp<ItemF>
{
    @Override
    void print(ItemF item) {
        Log.d(
                "DEMO",
                "I am new ExpressionF, treat ItemF only."
        );
    }
}

// setup expressions.
Dispatcher<Object, Exp<Object>> dispatcher = new Dispatcher<>();
{
    ...
+++    dispatcher.set(ItemF.class, new ExpF());
}

// setup elements.
List<Object> list = new ArrayList<>();
{
    ...
+++    list.add(new ItemF());
}

Feature of Dispatcher Pattern

non-instusive (non-invasice)

The accept method used in Visitor Pattern is unnecessary. Therefore, We can use any classes as an element class, even for classes that can not be changed, as used in libraries.

Sharing Expression.

Element classes and expression classes are not a one-to-one relationship, and it is possible for a single expression class to handle multiple elements. Of course, it has not to rewrite existing classes. See ExpX in the above example. If the element classes in charge of ExpX inherit a common superclass, expression can be aggregated into ExpX.

Polymorphism by Delegation

Dispatcher Pattern correspond element and expression with Map. Set the key as class of element and the value as instance of expression class in Map instance, can dispatch expresion for each element. I named it "Polymorphism by Delegation".

Comparison with Visitor Pattern

Checking Visitor Pattern Dispatcher Pattern
accept method of element class necessary unnecessary (non-intusive)
polymorphism Polymorphism by method overloading Polymorphism by Delegation
dispatching Double Single (Haha)

Multiphase return value

The return value obtained from Dispatcher can not be multiphase. However, if we consider the return value as a new element group, can apply Dispatcher Pattern to the return value.

Future Verification

I thought that Dispatcher Pattern was resolved to type safety, but possible that an advanced programmer will find some holes. Even if this design was failure, I believe that Dispatcher Pattern is wider than Viditor Pattern, because it can handle any elements such as classes included in the library.

If this new pattern is established in type safety, Dispatcher Pattern will be written instead of Visitor Pattern in future design pattern related books. At that time, I hope that my name "Stew Eucen" will be introduced in the books. :)