2

I have implemented a simple tree structure in C# (or Java, doesn't matter much) where everything revolves around the abstract class Node and some subclasses of it. Node offers methods and properties related to the mathematical graph theory. For example Node.Parent references the parent Node and Node.Children the child nodes

class Node {  
    Node Parent;  
    Node[] Children;

    void appendNode(Node node) { ... }
}  

I am using the tree to perform calculations. The calculations involve a lot of recursions and I also need to store intermediate values for each node. For each calculation I have introduced additional properties and methods to the Node class, such as

class Node {  
    Node Parent;  
    Node[] Children; 

    // Calculate weight
    int weight; // current weight
    void recalculateWeight() {
        // perform some heavily recursive stuff
        // involving Parent.recalculateWeight()
        // and update the value of the variable weight
    }

    int price; // current price 
    void recalculatePrice() {
        // perform some heavily recursive stuff
        // involving Parent.recalculatePrice()
        // and update the value of the variable price
    }

    void appendNode(Node node) {
        // ...
        recalculateWeight();
        recalculatePrice();
    }
} 

but now I have to give up this approach since the calculated values should be added dynamically without changing the Node class. Dynamically means that somebody else should be able to implement his own calculations on a given tree relying only the "graph theoretical methods" of the Node class.

Do you have any idea what would be a good design pattern for that?

JCvanDamme
  • 621
  • 5
  • 20
  • It's called subclasses – zapl Nov 26 '14 at 20:45
  • @zapl subclassing involves inheriting which involves statically adding methods to a class - meaning at compile time. Moreover, inheritage is not a design pattern - it is a means to achieve them. The decorator pattern might solve the problem – univise Nov 26 '14 at 20:52
  • 4
    OO design happens at compile time, or actually before that in the designers mind. If you actually want to add methods at runtime, you're at least not in the OO pattern world. – zapl Nov 26 '14 at 20:54
  • It could be possible using reflection?? – hcarrasko Nov 26 '14 at 20:57
  • 3
    @zapl do you even know what the decorator pattern is? The decorator pattern does not add methods to a class at compile time. The decorator pattern allows us to add responsibilities to an object at run-time; it is obvious and needless to say though that those operations added must be compiled first. The operations are added dynamically though. – univise Nov 26 '14 at 20:57
  • possible duplicate of [Can a Java class add a method to itself at runtime?](http://stackoverflow.com/questions/6680674/can-a-java-class-add-a-method-to-itself-at-runtime) – hcarrasko Nov 26 '14 at 20:58
  • Couldn't you just have a delegate function? – Icemanind Nov 26 '14 at 21:20
  • @univise I may have misunderstood you but if decoration can solve the problem here, then can inheritance. Decoration can as the name indicates only decorate existing responsibilities, it won't help if you need entirely new ones. And that's what OP's problem is here. Some nodes need a "price" property, some don't. – zapl Nov 26 '14 at 21:26
  • 1
    @zapl you are right. Inheritance could solve this problem too. Let me leave it at this quotation: "For every complex problem there is an answer that is clear, simple, and wrong." - H. L. Mencken – univise Nov 26 '14 at 22:10

2 Answers2

6

This screams the Visitor pattern.

interface Visitor{

    visit(Node node);

}

class Node{

   //...


   void accept(Visitor v){
       //feel free to change visit order to viist children first
       v.visit(this);
       for(Node child : children){
          v.visit(child);
       }

   }
}

Then you can make all your different calculations different Visitors. Creating new types of calculations or traversals does not change the Node class at all. You just make a new Visitor implementation.

class WeightVisitor implements Visitor{

   int weight = 0;

   void visit(Node n){
        weight += ...
   }

}

Then everytime you want to calculate weight

WeightVisitor visitor = new WeightVisitor();

rootNode.accept(visitor);

int weight = visitor.getWeight();
dkatzel
  • 31,188
  • 3
  • 63
  • 67
  • You're missing the important double dispatch part of the visitor pattern: https://gist.github.com/anonymous/03ddd63b83566bfd1412 – zapl Nov 26 '14 at 21:49
  • @zapl Thanks for spotting the typo. I meant to type `v.visit(this)` instead of `visit(this)`. I've fixed my answer thanks! – dkatzel Nov 26 '14 at 21:53
  • That's not actually what I meant. Compare http://en.wikipedia.org/wiki/Visitor_pattern - there are multiple methods in the visitor interface. The problem the visitor pattern solves is when only subclasses of `Node` have a weight property. E.g. your `WeightVisitor` would need a `visit(WeightNode n)` or similar method so it can access that property. You're currently just passing `Node`s around in a more fancy way but don't actually solve the problem. The pattern you're showing is more of a strategy pattern or so where you can apply different implementations to your entities. – zapl Nov 26 '14 at 22:03
  • @zapl depending on what `weight` means in the OP's context, it doesn't have to be a property. It seems the OP only had a property because the recursive solution needed a place to put the data. Weight could be something like "number of descendent Nodes" which only requires counting visit calls. You are correct that the different implementations are different strategies. However the mechanism for how these strategies do their thing is by the `Visitor` pattern. Often real world projects require multiple patterns working together. – dkatzel Nov 26 '14 at 22:19
  • It's IMO not *the* visitor pattern because you don't need the accept, then visit part, you can control that entirely from the outside https://gist.github.com/anonymous/a32a2c33cbb988b8bf99 - key difference to *the* visitor pattern IMO is that "the visitor pattern is a way of separating an algorithm from an object structure on which it operates" means that the algorithm is fixed, the data-structure is the one that changes. While here, the node structure is fixed and the visiting algorithm changes. Also the "Visitors and Iterators" section here http://www.oodesign.com/visitor-pattern.html – zapl Nov 26 '14 at 23:38
  • Maybe it's just an opinion, the longer I think about it the less convinced am I :) There is certainly an aspect in use that appears in every description of the visitor pattern and that's "visiting" – zapl Nov 26 '14 at 23:45
0

It depends on what you're trying to achieve. There are two different ways to use a graph of nodes:

  • The nodes are just a container for data and a user of your node library can use them to store data and calculate all sorts of things. This is similar to a LinkedList which allows you to store data in a node, to traverse the list, remove nodes, etc. Even recursively. But that does not need any change of the nodes themselves. Those methods are entirely in the responsibility of the client to implement externally. Your Nodes should just provide methods to iterate them in all sorts of ways. And probably offer a typesafe (i.e. generic like List<Type>) way of storing custom data.

  • You're providing a graph for user defined Nodes. The user would extend the Node class in a custom way to do custom things. The user would still use the graphy functions you provide, like for example a Tree.rebalance(Node root) method you could provide. What your node class and other methods should allow is to make extension easy.

For example you should consider making the class and other methods generic so the user can use the custom subtypes fully

// That's what you provide. Has all the base functionality of a node
class Node<T extends Node<T>> {
    private T parent;
    private List<T> children;
    public void setParent(T parent) {
        this.parent = parent;
    }
    public T getParent() {
        return this.parent;
    }
    // ...
}

// that's what the user can do with it
class WeightedNode extends Node<WeightedNode> {
    public int weight = 5;

    public void update() {
        WeightedNode parent = getParent();
        // ^^ Typesafe access to WeightedNode!
        this.weight = parent.weight + 1;
        parent.update();
    }
}

class User {
    void use() {
        WeightedNode node = new WeightedNode();
        node.update();
    }
}
zapl
  • 63,179
  • 10
  • 123
  • 154