Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unsure how approach design of application

Tags:

java

I am trying to create an application in Java which allows for generation of large Provenance graphs from small seed graphs but I am having a little trouble figuring out the best way to design my classes.

To begin with, Provenance essentially has a graph structure, nodes and edges. I have created a Java library which acts as a mapping of the Provenance Data Model to Java Objects. This allows me to abstract my application specific information from Provenance model.

My class structure looks a little like this:

  • Graph (containing Sets of Node and Edge)
  • abstract Node (Just a String name for now)
    • Agent
    • Activity
    • Entity
    • Other subclasses of Node
  • abstract Edge
    • Generation
    • Association
    • Other subclasses of Edge

Now, what I would like to do is provide weighting on the nodes and edges which act as multipliers/saturation levels. There are a couple of ways I could use the library to achieve this aim but I'm not clear on what is the best from a development and maintainability perspective.

Firstly, I defined a Weighable interface with some simple methods such as get and set minimum and maximum weights.

Now, I could either extend each subclass Node e.g.

class WeighableAgent extends Agent implements Weighable

But then this requires an extended class for every type of node available, and required me to implement the Weighable interface at every level.

Alternatively, I could provide a mixin but it would still rely on me implementing the same functionality across each subclass.

Alternatively I could have a class that composes upon Agent, but that still requires me to implement Weighable across every composition class.

Alternatively, I could compose solely on the Node class e.g.

class WeighableNode implements Weighable {

    private Node node;

    public WeighableNode(Node node) {
        this.node = node;
    }

    etc etc...

And this would allow me to only implement Weighable in one place. However, then I lose some important information about the concrete class type from anything using WeighableNode and the only way around this that I see is to provide a method:

Node getNode();

Which I'm concerned about due to the Law Of Demeter. Furthermore, this WeighableNode will be composed upon by a PresentationNode, to help with Swing which would mean I would end up chaining calls such as:

presentationNode.getWeighableNode().getNode() instanceof Agent

Which seems very unpleasant.

One final solution which I just thought of is to compose upon Node as above and then extend WeighableNode with WeighableAgent, etc etc. This means I do not need to reimplement Weighable each time and if I know I have

instanceof WeighableAgent

then the wrapped node is an Agent.

I appreciate this is quite long but I hope that an experienced developer will very quickly see the correct design practice.

like image 557
WilliamMartin Avatar asked Nov 03 '22 20:11

WilliamMartin


1 Answers

Too bad Java has neither real mixins nor multiple inheritance…

Generic wrapper

I guess I'd use WeighableNode<NodeType extends Node> as a generic wrapper type. That way, all places which require nodes of a specific type could clearly state that fact. Its getNode method could return the correct class. And you wouldn't end up with too many classes all over the place.

Conversion idiom

Even if you don't use the above approach, the following idiom might be interesting:

class Node {
    public T asNode(Class<T extends Node> clazz) {
        return clazz.cast(this);
    }
}

class NodeWrapper<N extends Node> {
    private N realNode;
    public T asNode(Class<T extends Node> clazz) {
        try {
            return super.asNode(clazz);
        }
        catch (ClassCastException e) {
            return realNode.asNode(clazz);
        }
    }
}

You could change the above to return null instead of throwing an exception if you prefer. The idea is that the calling code doesn't have to worry about how to convert your node to various types. You'd e.g. simply write

presentationNode.getNode(Agent.class)

This separates implementation from interface, giving you much freedom to change things later on if the need arises. The basic Node implementation would know how to convert itself, taking care of derived classes. The NodeWrapper would add conversion using composition. You could use that as the base class for WeighableNode and your presentationNode.

Fat interface

You could implement the weighing stuff in Node itself, and use WeighedNode just as a tagging interface, or not at all. Not the nicest solution, but will help keep your number of classes down, and shouldn't do any real harm except for the bit of memory consumed by the extra data. The levels of indirection required by the other schemes will probably outweight that memory requirement by far.

like image 122
MvG Avatar answered Nov 10 '22 06:11

MvG