1
\$\begingroup\$

I have a tree whose nodes are of type Node. Depending on the type (one of the implementations of Node) nodes differ in the set of information that is stored in them (in the demo example, the information is not different). I also have action sets the Action that should be invoked when you visit a particular node type.

Although this is not implemented, I originally tried to write the code so that the action could support multiple types of nodes simultaneously.

public final class ActionA implements Action {
    @Override
    public void act(final NodeA node) {
        System.out.println("Act action1 (" + node.value() + ")");
    }

    @Override
    public void act(final NodeB node) {
        System.out.println("Act action1 (" + node.value() + ")");
    }
}

Here's what I got actually

I have a tree whose nodes are of type Node:

import java.util.List;

public interface Node {
    String value();
    List<Node> childs();
}

I have two implementations of Node:

public final class NodeType1 implements Node {

    private final String value;
    private final List<Node> childs;

    public NodeType1(String value, List<Node> childs) {
        this.value = value;
        this.childs = childs;
    }

    @Override
    public String value() {
        return this.value;
    }

    @Override
    public List<Node> childs() {
        return this.childs;
    }
}

public final class NodeType2 implements Node {

    private final String value;
    private final List<Node> childs;

    public NodeType2(String value, List<Node> childs) {
        this.value = value;
        this.childs = childs;
    }

    @Override
    public String value() {
        return this.value;
    }

    @Override
    public List<Node> childs() {
        return this.childs;
    }
}

I have Action interface and two implementation:

public interface Action {
    void act(Node node);
}

public final class Action1 implements Action {
    @Override
    public void act(final Node node) {
        System.out.println("Act action1 (" + node.value() + ")");
    }
}

public final class Action2 implements Action {
    @Override
    public void act(final Node node) {
        System.out.println("Act action2 (" + node.value() + ")");
    }
}

I also created ActionSet class for group actions

import java.util.LinkedList;
import java.util.List;

public class ActionSet {

    private List<Action> actions = new LinkedList<>();

    public ActionSet(final List<Action> actions) {
        this.actions = actions;
    }

    public boolean add(final Action action) {
        return actions.add(action);
    }

    public void act(final Node node) {
        for (final Action action : this.actions) {
            action.act(node);
        }
    }
}

This is ActionSet1 and ActionSet2:

public final class ActionSet1 extends ActionSet {
    public ActionSet1() {
        super(new LinkedList<Action>() {{
            add(new Action1());
        }});
    }
}

public final class ActionSet2 extends ActionSet {
    public ActionSet2() {
        super(new LinkedList<Action>() {{
            add(new Action2());
        }});
    }
}

And I created TreeWalker class:

public final class TreeWalker {

    public void walk(final Node root) {
        if (root instanceof NodeType1) {
            walk((NodeType1) root);
        } else if (root instanceof NodeType2) {
            walk((NodeType2) root);
        }
    }

    public void walk(final NodeType1 root) {
        new ActionSet1().act(root);

        for (final Node child : root.childs()) {
            walk(child);
        }
    }

    public void walk(final NodeType2 root) {
        new ActionSet2().act(root);

        for (final Node child : root.childs()) {
            walk(child);
        }
    }
}

Finally this is my main Application class:

import java.util.LinkedList;

public final class Application {
    public static void main(final String[] args) {
        final Node tree = new NodeType2("hello", new LinkedList<Node>() {{
            add(new NodeType1("world", new LinkedList<Node>()));
            add(new NodeType2("bold", new LinkedList<Node>()));
        }});

        final TreeWalker walker = new TreeWalker();
        walker.walk(tree);
    }
}

If you execute this application you can see:

Act action2 (hello)
Act action1 (world)
Act action2 (bold)

How do you evaluate this design? What problems can it cause and what can be improved?

\$\endgroup\$
0
\$\begingroup\$

Using Visitor pattern can be written as:

class Node {

    protected final Node[] childs;

    public Node() {
        this(new Node[] {});
    }

    public Node(final Node[] childs) {
        this.childs = childs;
    }

    public Node[] childs() {
        return this.childs;
    }

    public void accept(final Action visitor) {
        for (final Node child : childs()) {
            child.accept(visitor);
        }
        visitor.act(this);
    }
}

interface Action {
    default void act(final Node node) {
        if (node instanceof NodeA) {
            this.act((NodeA) node);
        } else if (node instanceof NodeB) {
            this.act((NodeB) node);
        }
        else if (node instanceof NodeC) {
            this.act((NodeC) node);
        } else if (node instanceof NodeD) {
            this.act((NodeD) node);
        }
    }
    default void act(final NodeB node) {}
    default void act(final NodeA node) {}
    default void act(final NodeC node) {}
    default void act(final NodeD node) {}
}

class NodeA extends Node {
    public NodeA(final Node[] elements) {
        super(elements);
    }
}

class NodeB extends Node {}
class NodeC extends Node {}
class NodeD extends Node {}

class ActionA implements Action {

    @Override
    public void act(final NodeB node) {
        System.out.println("Action A / Node B");
    }

    @Override
    public void act(final NodeA node) {
        System.out.println("Action A / Node A");
    }
}

class ActionB implements Action {

    @Override
    public void act(final NodeC node) {
        System.out.println("Action B / Node C");
    }

    @Override
    public void act(final NodeD node) {
        System.out.println("Action B / Node D");
    }
}

public class ApplicationDemo {
    public static void main(final String[] args) {
        final Node node = new NodeA(new Node[]{
                new NodeB(), new NodeD(),
                new NodeB(), new NodeC()
        });

        node.accept(new ActionB());
        node.accept(new ActionA());
    }
}

Result:

Action B / Node D
Action B / Node C
Action A / Node B
Action A / Node B
Action A / Node A
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.