Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nested MVC communication patterns

This is entirely a best practices type question, so the language is irrelevant. I understand the basic principles of MVC, and that there are different, subtle flavors of it (i.e. views having a direct reference to models vs. a data delegate off the controller).

My question is around cross MVC communication, when those MVCs are nested. An example of this would be a drawing program (like Paint or something). The Canvas itself could be an MVC, but so could each drawn entity (e.g. Shapes, Text). From a model perspective, it makes sense for the CanvasModel to have a collection of entities, but should the CanvasView and CanvasController have corresponding collections of entity views and controllers respectively?

Also, what's the best/cleanest way to add a new drawn entity? Say the user has the CircleTool active, they click in the Canvas view and start drawing the shape. The CanvasView could fire relevant mouse down/move/up events that the CanvasController could listen to. The controller could then basically proxy those events to the CircleTool (state pattern). On mouse down, the CircleTool would want to create a new Circle. Should the Tool create a new CircleEntityController outright and call something like canvasController.addEntity(circleController)? Where should the responsibility of creating the Circle's model and view then lie?

Sorry if these questions are somewhat nebulous :)

--EDIT--

Here's a pseudo-codish example of what I'm talking about:

CircleTool {
    ...
    onCanvasMouseDown: function(x, y) {
        // should this tool/service create the new entity's model, view, and controller?
        var model = new CircleModel(x, y);
        var view = new CircleView(model);
        var controller = new CircleController(model, view);

        // should the canvasController's add method take in all 3 components
        // and then add them to their respective endpoints?
        this.canvasController.addEntity(model, view, controller);
    }
    ...
}


CanvasController {
    ...
    addEntity: function(model, view, controller) {
        // this doesn't really feel right...
        this.entityControllers.add(controller);
        this.model.addEntityModel(model);
        this.view.addEntityView(view);
    }
    ...
}
like image 989
chinabuffet Avatar asked Feb 22 '13 14:02

chinabuffet


1 Answers

Wow, well I have perhaps a surprising answer to this question: I have a long-standing rant about how MVC is considered this beatific symbol of perfection in programming that no one sees any issues with. A favorite interview question is 'what are some problems, or challenges that you might encounter in MVC?' It's amazing how often the question is greeted with a puzzled, queasy look.

The answer is really quite simple: MVC relies on the notion of multiple consumers having their needs met from a single shared model object. Things really start to go to hell when the various views make different demands. There was an article a few years ago where the authors advanced the notion of Hierarchical MVC. Someone else came on in the comments and told them that what they thought they were inventing already existed: a pattern called Presentation-Abstraction-Controller (PAC). Probably the only place you see this in the pattern literature is in the Buschmann book, sometimes referred to as the Gang of Five, or POSA (Pattern-Oriented Software Architecture). Interestingly, whenever I explain PAC, I use a paint program as the perfect example.

The main difference is that in PAC, the presentation elements tend to have their own models, that's the A of the PAC: for each component, you don't have to, but can have an abstraction. Per some of the other responses here, then what happens is you have a coordinating controller, in this case, that would rule over the canvas. Let's say we want to add a small view to the side of the canvas that shows the count of various shapes (e.g. Squares 3, Circles 5, etc.). That controller would register with the coordinating controller to listen on a two events: elementAdded and elementRemoved. As it received each notification, it would simply update a map it had in its own Abstraction. Imagine how absurd it would be to alter a shared model that a bunch of components are using to add support for such a thing. Furthermore, the person who did the ShapesSummary component didn't have to learn about anything, but the event protocols, and of course all its interactions with collaborators are immutable.

The hierarchical part is that there can be layers of controllers in PAC: for instance, the coordinating one at the Canvas level would not know about any of the various components with specialized behaviors. We might make a shape that can contain other things, which would need logic for accepting drags, etc. That component would have its own Abstraction and Controller, and that Controller would coordinate its activities with the CanvasController, etc. It might even become necessary at some point for it to communicate with its contained components.

Here's the POSA Book.

like image 195
Rob Avatar answered Oct 04 '22 17:10

Rob