JCvanDamme
JCvanDamme

Reputation: 671

OO design pattern: How to add methods dynamically?

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?

Upvotes: 2

Views: 2078

Answers (2)

zapl
zapl

Reputation: 63955

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();
    }
}

Upvotes: 0

dkatzel
dkatzel

Reputation: 31648

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();

Upvotes: 6

Related Questions