开发者

mvc with multiple windows design

I am working on an application and i am using the MVC pattern. Currently my plan is to have an MVC pattern for each window that comes up. For instance, my login window has its own mvc system. Another window where they make a selection has its own mvc system. Then the mainview has its own mvc system...

This just seems a little goofy. I was wondering if that was normal, to have every window have its own mvc? If not, how could i arrange this better?

The trouble i am running into is, how do i get the log in window mvc to corre开发者_如何学Cctly call the selection window and then after they make their selection, how does the selection window call the mainview window?

Thanks for any help! If you need more info, please let me know.


Despite its simplicity the MVC pattern is many time not understood. At first you need to understand what should be held into all of those separated components and how they might talk together.

  • The View : Any UI element. A good reusable view element should be reused anywhere. Therefore the view does not know it's context nor the specific controller he will be interacting with. What the view is aware is it's own state and one (or many) generic listener which it will call once some action happen. A view element knows it state, broadcast changes but should not change its state by itself. Example: A button will broadcast an action "clicked", and you will set the button name through a controller through a method like aButton.setLabel("click me!");

  • The model : A model is able to handle a state of a dataset, it usually implements some functions in order to save and load its state into / from a file. A model is a box which should actually not change its state unless someone (a controller) asks for it. Again, a good model is not aware of the view or even the controller. It is just the way it is. A very simple model is for example a String. You can load the string from a file, save it to a file, change it to uppercase, substring and so on. A string does have a state, is unaware of it's context and just wait for someone to use it. If the model knows the controller you are fooled, the model will not be reusable unless you implement the same controller (which is a bad practice even using interfaces).

  • The controller : The controller is actually your program, this is the part where the decision part is made. Where it is ? well… your main function is already the controller. Again, all the decisions are made into a controller. When someone clicks on a button, a method within a controller is invoked (and not a view one), when you want to update a label, you do it from the controller. Also all the threads should be handled into controllers. Your model is loading a large file ? Create a thread into the controller, ask the model to load the image, then notify the main thread once done (out of topic, search for event loop)

Multiple windows, here we are!

View / Controller how do they interact to each other ? The responsibility of the view is only to provide to a controller events (text field changed, a button was clicked and so on) and to be able to be displayed the way we want. For example window.setTitle("myWindow"); We can be tempted to put stuff in the view that should be into the controller and conversely.

For example, if you are building an alert panel you will have 2 buttons (ok, cancel) and one label for the text. Do the view knows what to do once you click "ok" of "cancel" ? certainly not… but once clicked the panel will have to disappear by itself. Conversely, if you do create a panel for entering a password, the View will not be able to hold the validation process. This will be the controller's job to agree or not on the provided password.

Okay I guess you understood all the MVC, now it's time to talk about the right way to let all those components talk together:

How should the view be notifying the controller ? Actually it depends on the language you are using. At very first think of the following:

  • Avoid the view to know the controller.
  • The view should not be fetching information from the controller, nor invoking methods to it. How it will tell the controller something happened ? Through observers (or listeners)

Little parenthesis: A view can invoke methods of another view if it is just a matter of keeping it visually correct. For example resizing a window will ensure all the components (subviews) of this windows are still displayed correctly, but the controller might not be notified the window changed its size.

Communication between the MVC is the key! So, how does it work ?

  • Who will create and display a view ? The controller!

  • Who should be registering to view events ? The controller too… after creating

  • How ? The view's job is to broadcast events when they happen. Typically through ActionListener in java, observers in objective-c, lambda functions c++, function callback in javascript. And the controller to listed to it implementing the right "callback" function actionPerformed in java, a selector, or even directly the callback function if you provided a function callback to the view.

  • So when a view created from the controller is firing an event, the controller is notified and will have to handle this event. If the controller wants to change something into the view, it's quite easy cause the controller always own and knows the view.

  • To each window a controller : it sounds good, you are not obliged to have only one controller (neither one model or view…) while creating a login controller object instance, that one could create and display a login window as well.

  • One instance of model per service. Here comes the singleton design pattern, you will probably need only one "authentication model" because you are either logged in or logged out within the same application. So instead of creating several login model instance for each window dependent on the login state, you actually want to have only one. You can do this through a static instance of the login model object. This is called a singleton.

Now how to ensure that what we are changing into a model will be replicated to all the controllers ? The simple case is one controller and one model. The controller is changing the model and waits the model to be changed to then use the change (for example to display it into a view). What happen if the model is shared through several controllers ? For example, the model is a login service, you have 2 states : logged in and logged out. 2 controllers are using this model, each of those controller do have it's own view (window), one is just displaying a password field and the second one is a window with an image which is displayed only if the user is logged in. There is not one solution to this case but several. One is using the listening / observing pattern. Several controllers can be listening for the model's changes. Once one of those is changing it, all the controllers will be notified by the change and therefore will update the view. Another way is to keep a notification center (it's another pattern) and to let either the controller or the model to broadcast what happened, all the controllers interested in this event will then be notified by the change and process the event. This last solution is especially interesting for cases where the model can change at anytime (of course through a controller hidden somewhere). The notification center is using event loops and tries most of the time to notify into the main event loop (which should be your UI controllers's thread)

So for our example, if the login process is offline I would be using a simple listener. The 2 controllers are listening to the model changes. One controller after entering the password will call the method's model login.loginWithPassword(String some password) if the password is fine, the model will broadcast an event "loginStateChanged". All the listeners (including our 2 controllers) will then get this event and tell the view to get updated with whatever we want (for example display the image which can be shown only while logged).

In the example above, our controller was asking the model and the model directly fire the event, this within the thread of the controller, which is fine cause there is no risk of concurrency access (same thread = no problem) however:

If the login was a remote one, for example an online service authentication,we would be knocking at the server's door and wait for its answer. As this process might take some seconds the controller would keep stuck waiting for the server's answer. To avoid this case we should then send the request into another thread and just modify the view with a message (waiting for server) until we get the answer from the server. Once we get the answer (or after the timeout if no answer) the thread we created before will get the model's answer being true or false. A very bad idea is to update the view directly with the result. The problem is the thread who got the answer, is not running into the main controller's thread, and therefore if we do update the view, we do face a concurrency access (if the view object is changed by 2 threads exactly in the same time you encounter a crash). One way of insuring we are not messing with the main thread, is simply to send the model's answer to the main thread through a notification which will be dispatched in the main event loop. In this case especially I would be using a notification center instead of a simple broadcast from the model. It is interesting to note that some framework are providing broadcast within the main event loop. There is even better solution using block or lambda functions but it is but out of this question's scope.

To summarise:

  • Neither the view nor the model should know the controller
  • Only the controller do know both of them
  • Pay attention to the way they are talking to each other, use listeners or notification if there is more than 1 controller for the same model.
  • The process is always as follow:
    • The controller create the view and listen it
    • The view tells the controller an action happened
    • The controller process the action might ask the model to change something
    • The model gives a result (either returning the function, through a listener or callback)
    • The controller process the result
    • The controller update the view with the result.


How do I call that selection window from the login window correctly?

Use the observer pattern. If any view changes the model's state, then all registered listeners will be notified, and each can update itself to reflect the change. This example mentions three common ways to implement the observer pattern.


Actually i believe that what i am going to do is use a Mediator pattern to control the interaction between these views. This way the coupling is very limited and it gets the exact job done that i was wanting. Let me know what you think.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜