Search code examples
c++design-patternsmvppassive-view

Using MVP, how to create a view from another view, linked with the same model object


Background

We use the Model-View-Presenter design pattern along with the abstract factory pattern and the "signal/slot" pattern in our application, to fullfill 2 main requirements

  • Enhance testability (very lightweight GUI, every action can be simulated in unit tests)
  • Make the "view" totally independant from the rest, so we can change the actual view implementation, without changing anything else

In order to do so our code is divided in 4 layers :

  • Core : which holds the model
  • Presenter : which manages interactions between the view interfaces (see bellow) and the core
  • View Interfaces : they define the signals and slots for a View, but not the implementation
  • Views : the actual implementation of the views

When the presenter creates or deals with views, it uses an abstract factory and only knows about the view interfaces.

It does the signal/slot binding between views interfaces. It doesn't care about the actual implementation. In the "views" layer, we have a concrete factory which deals with implementations.

The signal/slot mechanism is implemented using a custom framework built upon boost::function.

Really, what we have is something like that : http://martinfowler.com/eaaDev/PassiveScreen.html

Everything works fine.

The problem

However, there's a problem I don't know how to solve.

Let's take for example a very simple drag and drop example.

I have two ContainersViews (ContainerView1, ContainerView2). ContainerView1 has an ItemView1. I drag the ItemView1 from ContainerView1 to ContainerView2.

ContainerView2 must create an ItemView2, of a different type, but which "points" to the same model object as ItemView1.

So the ContainerView2 gets a callback called for the drop action with ItemView1 as a parameter. It calls ContainerPresenterB passing it ItemViewB

In this case we are only dealing with views. In MVP-PV, views aren't supposed to know anything about the presenter nor the model, right ?

How can I create the ItemView2 from the ItemView1, not knowing which model object is ItemView1 representing ?

I thought about adding an "itemId" to every view, this id being the id of the core object the view represents.

So in pseudo code, ContainerPresenter2 would do something like

itemView2=abstractWidgetFactory.createItemView2();
this.add(itemView2,itemView1.getCoreObjectId())

I don't get too much into details. That just work. The problem I have here is that those itemIds are just like pointers. And pointers can be dangling. Imagine that by mistake, I delete itemView1, and this deletes coreObject1. The itemView2 will have a coreObjectId which represents an invalid coreObject.

Isn't there a more elegant and "bulletproof" solution ?

Even though I never did ObjectiveC or macOSX programming, I couldn't help but notice that our framework is very similar to Cocoa framework. How do they deal with this kind of problem ? Couldn't find more in-depth information about that on google. If someone could shed some light on this.

I hope this question isn't too confusing ...


Solution

  • Ok the technique I found is actually coming from Cocoa, so it's objective-C but you can definitely do the same in C++.

    The solution is simply to use a PasteBoard (developer.apple.com documentation).

    In the hope it helps someone ...