views:

63

answers:

3

So I'm working on rewriting a program for a professor, and I have some questions related to the Model-View-Controller pattern. The program is called GraphViewer and is used to design and view graphs (as in Graph Theory, not Statistics). So far I have planned the structure thus:

  • Models
    • VertexModel - Has id, location, color, and a collection of edges it is coincident to
    • EdgeModel - Has the two vertices it spans, weight, color, and a couple other things
    • GraphModel - Primarily, but not just, a collection of vertices and a collection of edges
  • Views
    • VertexView - Paints its vertex, and has its own properties
    • EdgeView - Paints its edge, and also has some of its own properties
    • GraphView - Basically a JPanel that has a collection of vertex and edge views, when it gets a paint command, it also iterates through each collection of views and issues a paint command to them
  • Controllers
    • GraphController - Takes care of interpreting user gestures for adding vertices, edges, etc. and updates the model.

Now the first question I have is related to this plan: Should each model have a view--even though only the graphmodel is a JComponent? Now part of me says yes--because each vertex and edge might be drawn differently. But another part of me screams no to the kind-of parallel-list structure of a graphview having collections of views that correspond to models in the collections of the graphview's graphmodel (boy that's complicated). And somehow, I have to work in that the whole graphview must repaint if a vertex or edge model changes. I suppose the answer to this is also related to whether each model gets a controller. (Am I just approaching this wrong?)

Somewhat tied to the answer of the first question is my second: how will I know to notify the graphview if a vertexmodel is added to the graphmodel's collection? If a whole new collection is set, it'll notify the view when it enters the setEdges() method. But what if I have to write getEdges().add(...)? Now the graphview needs to be notified of an update, but setEdges was never called...

Thanks a bunch, as you can tell I'm kinda new to design patterns!

A: 

Don't expose the private Collection member of the model, thus not allowing a call such as getEdges().add(...)

Rather, expose an API to allow adding/removing edges, and you can then make use of the Observer pattern to notify the view when the model changes.

Simplistically, and only dealing with Edges...

public class EdgeModel extends AbstractCollection<Edge> implements Observable {
    private List<Edge> edges = new ArrayList<Edge>();
    @Override public Iterator<Edge> iterator() { return edges.iterator(); }
    @Override public int size() { return edges.size(); }
    @Override public boolean add(Edge edge) {
        if (edges.add(edge)) {
            this.notifyObservers();
            return true;
        }
        return false;
    }

    // other stuff for Observable
}

public class EdgeView implements Observer {
    public void setModel(EdgeModel model) {
        model.addObserver(this);
    }
    public void update(Observable o, Object arg) {
        // model o has changed... update something visual I guess
    }
}
ptomli
Ah, interesting. So you would have a GraphModel with an EdgeModel that has Edges and a VertexModel that has Vertices? Then does the GraphView have an EdgeView and a VertexView? Also, how would I then make sure the GraphView updates if, say, a Vertex's location is changed? That seems like a lot of unnecessary code for all the overridden methods that merely get a value (like iterator() and size()). Does this mean I can never expose mutable objects (like Point2D in Vertex and Arc2D in Edge)?
0x24a537r9
Also, don't I run into the same problem with implementing iterator()? Then you can edit one of the elements and nothing will update. Now this isn't a problem for a collection of observables, but what about when I wrap the Arc2D class for Edge? I has a getPathIterator method. I'm just afraid I'll get stuck wrapping everything in the Java API to implement observable if every part of every class has to notify for updates.
0x24a537r9
A: 
  1. Attach a view to a specific model if and only if you need to display that model differently. To me, view should be independent of each other, and a hierarchy of views sounds wrong. If you are to show your model in only one view, a single GraphView should suffice.

  2. You may write a wrapper for your collection(s) which intercepts all modification calls to handle necessary modifications towards its listeners. Alternatively, you may implement GraphModel as a Facade which wraps the whole graph model inside to achieve the same effect.

Péter Török
Yeah, the hierarchy of views did sound suspect. And I guess #2 makes me ask the same question I asked ptomli, namely does MVC mean I can never expose mutable objects (like Point2D in Vertex and Arc2D in Edge)? Writing a wrapper for every mutable object just doesn't seem like the best way to go about it.
0x24a537r9
@0x24a537r9 Since you want to know whenever your model data changes, you must have wrap your code around your model data somehow to handle this. I agree that the Facade approach is simpler.
Péter Török
A: 

First, take a look at this question (and its answers). It discusses similar issues.

Second, here are some thoughts regarding your problem

  • I'm feeling you're a bit too dogmatic about MVC. Don't necessarily introduce a per-entity view if one view (namely: GraphView) answers your needs.
  • It seems that your second question makes the assumption that getEdges() returns a good-old java.util.List (or something similar). Why don't you introduce your own list that is capable of firing notification (to the view/controller) whenever add() is called?
  • Finally, I would go with a different approach: Centralized data. Instead of encapsulating the data inside individual Vertex, Edge, Graph classes have a central data structure (impl. for example as a list of edges). This data structure will fire notifications when it is changed. You can still have Edge and Vertex classes, but this will no longer be persistent entities. They will be simple services wrapped around the central data structure that allow you to manipulate a vertex/edge using some higher-level interface.
Itay
I thought I might be too dogmatic about MVC--it's good to know I can loosen up a bit. So then should I have the GraphView manage drawing each vertex and edge, and saving each one's individual view properties (for instance, whether one is selected)?As to your third bullet, I'm not sure I understand. Are you saying scrap the VertexView and EdgeView, and downgrade VertexModel and EdgeModel to simply Vertex and Edge classes, respectively?
0x24a537r9
I just fixed the missing link from the 1st sentence. Having "GraphView manage drawing ..." sounds reasonable. It can indeed manage porperties such as isSelected, color, etc.Regarding 3rd bullet. I am not speaking about the names. I am saying that a possible approach would be to have a GraphModel that managed every piece of data (about edges, vertices and the graph). Each vertex will have an id. You can then create a vertex class that has two fields: vertexId, GraphModel. Such a class will allow you manipulate the vertex via a vertex-oriented interface.
Itay