PDA

View Full Version : Newbie question: MVC interconnectivity




Qaanol
Jul 14, 2013, 08:24 AM
I am working on my first real Mac program, and I’m having troubling figuring out the right way to get my controller and UI to “see” my model. (Note: I am running Xcode 3.2.6 on OS X 10.6.8.)

Essentially, my program has a few text fields, a button, and a progress indicator. I made a class which acts as a controller, that subclasses NSObject (not NSController).

My controller has outlets for the UI elements, an instance variable for my model object, and the button sends an action to my controller. When the button is pressed, the controller reads the data from the text fields and tells the model object, “Here is the data, do your thing.” So far so good.

The model will take a long time to process the data, so I plan to have it make an NSOperation and put it in an NSOperationQueue. I think I can figure that part out though.

Here is what I need help with:

As the model does its work, it keeps keeps track of how far along it is. I want to show that in the progress bar. Numerically, this is simple. The model knows how many steps it will take, and which step it is on. But I do not understand what I have to do to make the progress bar display it.

I think I am supposed to use Cocoa Bindings, but I have only ever done that through a subclass of NSController before, and I am still quite shaky on setting it up. I could also use notifications, but that seems like a roundabout way to get the job done.

Also, my controller needs to know when the model finishes its work, so it can do things like update the UI, and allow the user to save the results. Same problem: I don’t know how to get my controller to know when the job is done. I suppose I could give the model object a “delegate” instance variable, set it as the controller, and just message it when the work is done, but that seems like it would violate MVC independence. I feel like my model object should not need to know anything about my controller.

So, what exactly is the correct/best solution for getting the UI and controller to know the state of the model?



ArtOfWarfare
Jul 14, 2013, 09:11 AM
I believe your issue is your model is does too much. As I see it, the three parts of MVC are:

View - Your UI. Whatever you put in a XIB or Storyboard (iOS only for now) file, and/or your drawing routines.

Model - Things that only store data. IE, only things that Core Data model files could handle. (You don't have to use Core Data, for many apps that would be overkill, but if Core Data can't handle it, it probably shouldn't be part of your model.)

Controller - Everything else. If you decided to use Core Data for your entire model and didn't write drawing routines, your controller would be the only portion of your app that involves writing code.

There's some disagreement - some people say the model should do more than I say, but here's the most important part:
MVC is a suggestion. It is not a rule. It is a way of encouraging good programming habits and breaking your code into multiple, easy to understand and maintain, modules.

If your code isn't simple and maintainable, you've likely wasted your time as you're going to have to throw it out and start over when you decide to change things, because you won't understand it anymore.

Qaanol
Jul 14, 2013, 09:47 AM
I believe your issue is your model is does too much. As I see it, the three parts of MVC are:

View - Your UI. Whatever you put in a XIB or Storyboard (iOS only for now) file, and/or your drawing routines.

Model - Things that only store data. IE, only things that Core Data model files could handle. (You don't have to use Core Data, for many apps that would be overkill, but if Core Data can't handle it, it probably shouldn't be part of your model.)

Controller - Everything else. If you decided to use Core Data for your entire model and didn't write drawing routines, your controller would be the only portion of your app that involves writing code.

There's some disagreement - some people say the model should do more than I say, but here's the most important part:
MVC is a suggestion. It is not a rule. It is a way of encouraging good programming habits and breaking your code into multiple, easy to understand and maintain, modules.

If your code isn't simple and maintainable, you've likely wasted your time as you're going to have to throw it out and start over when you decide to change things, because you won't understand it anymore.

My understanding of object-oriented programming is that each object has methods which represent actions that the object knows how to perform.

In my case, the action is to simulate certain types of events, using several different simulation systems. My model objects represent the types of simulations. They each know how to perform their own simulation type. If you want to call these “controllers”, that is fine, but I do not see how it solves the problem I am facing (namely, my lack of knowledge about how to get information back from these objects.)

What will happen in my program is, all these simulator objects take the same input, then each one runs its own simulation. When they all finish, I want the progress bar to indicate that the first round of simulation has taken place. This process is repeated a large number of times, so the statistical performance of each simulated system can be compared.

The simulations will take a long time to perform, so I cannot keep them in the main thread. They must be sent elsewhere, and I believe NSOperation is the simplest way to do so. The number of loop iterations is likely to be so large that I do not want to spawn all the NSOperations at the beginning because they would take up way too much memory. I want the loop to take place within an NSOperation. I need the progress indicator to know what iteration the loop is on, and I need my primary controller to know when the loop has finished. And I don’t know how to make that happen elegantly.

But even that is more complexity than needed to help me:

Suppose my main controller simply has an instance variable which is another object. That second object has an instance variable storing a number, and another instance variable storing a boolean, which both will change programmatically. This second object does not and should not have a reference to my main controller, nor to any part of the GUI.

How do I get the progress bar to show the number (I think this should be a Binding, but I do not know how to implement it) and how do I get the main controller to know when the boolean becomes true so the controller can execute a method at that time?

Edit: Okay, I think I get how to do the bindings now, by having an Object Controller bound to the object referenced by the instance variable within my controller.

So that just leaves me wondering how to get my main controller to perform a method when a certain instance variable of the model object reaches a certain value. Is a Notification the way to do that, or perhaps KVO, or what?

ArtOfWarfare
Jul 14, 2013, 12:56 PM
I believe your answer lies in protocols and delegates.

One of your controllers should define a protocol, which all of the simulations should conform to.

chown33
Jul 14, 2013, 04:37 PM
Also, my controller needs to know when the model finishes its work, so it can do things like update the UI, and allow the user to save the results. Same problem: I don’t know how to get my controller to know when the job is done. I suppose I could give the model object a “delegate” instance variable, set it as the controller, and just message it when the work is done, but that seems like it would violate MVC independence. I feel like my model object should not need to know anything about my controller.

Please explain how you came to this conclusion (underlined).

Refer to Cocoa Core Competencies, the "Model-View-Controller" article:
http://developer.apple.com/library/mac/#documentation/General/Conceptual/DevPedia-CocoaCore/MVC.html%23//apple_ref/doc/uid/TP40008195-CH32-SW1
Notice the diagram shows the Model sending Notifications to the Controller.

In most languages and libraries, there are two common ways of sending notifications: an instance variable (the delegate relationship), or a global variable. Using a global variable for this relationship is generally discouraged, because it creates a larger visible coupling (http://en.wikipedia.org/wiki/Coupling_(computer_programming)) surface. Thus, a direct delegate relationship would be preferred.

In the Cocoa libraries, there is another way to do this: sending an actual NSNotification. This also has an increased coupling surface, because it creates a third-party dependency: recipients must register with NSNotificationCenter (the third party) in order to receive information. So unless there's a reason for broadcasting notifications to multiple recipients, the direct delegate relationship would be a better choice.

Also note that "sending notifications" doesn't necessarily mean a literal NSNotification. If the Controller object is a registered delegate of the Model object, via a defined protocol, then the "notifications" are simply message sends (method invocations) by the Model object to an assigned delegate that implements a protocol.


People new to MVC often think that the Controller is the central object, and the others are peripheral. The modern view of MVC is that Controller acts as a mediator or coordinator:
http://en.wikipedia.org/wiki/Model–view–controller#History

Since Mediator (http://en.wikipedia.org/wiki/Mediator_Design_Pattern) is the name of a different design pattern, and is more in line with a "central object" relationship, I prefer the term "Coordinator" whne designing a Controller object. Also, it starts with "C", so it's simpler to think of the pattern as Model-View-Coordinator. Both Model and View have two-way communication with the Coordinator.
http://developer.apple.com/library/mac/#documentation/General/Conceptual/DevPedia-CocoaCore/ControllerObject.html%23//apple_ref/doc/uid/TP40008195-CH11-SW1
A controller object acts as a coordinator or as an intermediary between one or more view objects and one or more model objects.

You can define the Model-Controller relationship in a way that's closer to the Mediator pattern, but that's not necessary. A delegate relationship using a simple protocol will suffice.

If you want suggestions on how to define that protocol, ask again. Be sure to tell us specifically what information needs to flow from the Model objects to the Controller/Coordinator. From what you've posted so far, it seems like one or two methods would suffice, but that's just a guess.


My model objects represent the types of simulations. They each know how to perform their own simulation type. If you want to call these “controllers”, that is fine, but I do not see how it solves the problem I am facing (namely, my lack of knowledge about how to get information back from these objects.)
They are not Controller objects. They are Model objects.

Qaanol
Jul 14, 2013, 05:43 PM
I believe your answer lies in protocols and delegates.

One of your controllers should define a protocol, which all of the simulations should conform to.
Aha, thank you. That makes sense.


Please explain how you came to this conclusion (underlined).
Because I was mistaken (and forgot about protocols.)

Refer to Cocoa Core Competencies, the "Model-View-Controller" article:
http://developer.apple.com/library/mac/#documentation/General/Conceptual/DevPedia-CocoaCore/MVC.html%23//apple_ref/doc/uid/TP40008195-CH32-SW1
Notice the diagram shows the Model sending Notifications to the Controller.

In most languages and libraries, there are two common ways of sending notifications: an instance variable (the delegate relationship), or a global variable. Using a global variable for this relationship is generally discouraged, because it creates a larger visible coupling (http://en.wikipedia.org/wiki/Coupling_(computer_programming)) surface. Thus, a direct delegate relationship would be preferred.

In the Cocoa libraries, there is another way to do this: sending an actual NSNotification. This also has an increased coupling surface, because it creates a third-party dependency: recipients must register with NSNotificationCenter (the third party) in order to receive information. So unless there's a reason for broadcasting notifications to multiple recipients, the direct delegate relationship would be a better choice.

Also note that "sending notifications" doesn't necessarily mean a literal NSNotification. If the Controller object is a registered delegate of the Model object, via a defined protocol, then the "notifications" are simply message sends (method invocations) by the Model object to an assigned delegate that implements a protocol.


People new to MVC often think that the Controller is the central object, and the others are peripheral. The modern view of MVC is that Controller acts as a mediator or coordinator:
http://en.wikipedia.org/wiki/ModelĖviewĖcontroller#History

Since Mediator (http://en.wikipedia.org/wiki/Mediator_Design_Pattern) is the name of a different design pattern, and is more in line with a "central object" relationship, I prefer the term "Coordinator" whne designing a Controller object. Also, it starts with "C", so it's simpler to think of the pattern as Model-View-Coordinator. Both Model and View have two-way communication with the Coordinator.
http://developer.apple.com/library/mac/#documentation/General/Conceptual/DevPedia-CocoaCore/ControllerObject.html%23//apple_ref/doc/uid/TP40008195-CH11-SW1
A controller object acts as a coordinator or as an intermediary between one or more view objects and one or more model objects.

You can define the Model-Controller relationship in a way that's closer to the Mediator pattern, but that's not necessary. A delegate relationship using a simple protocol will suffice.
Thanks


If you want suggestions on how to define that protocol, ask again. Be sure to tell us specifically what information needs to flow from the Model objects to the Controller/Coordinator. From what you've posted so far, it seems like one or two methods would suffice, but that's just a guess.
Thanks, if and when I have more questions, itís good to know I can ask here.

Iím going to have to re-read about protocols, but from what I recall I think it is something I will be able to figure out.

When my simulations are done (either by going through all the iterations, or being stopped partway through by the cancel button) the results will be in a nice array, which I can send back to the controller. I think Iíll just need one protocol method at this point, to take the array and ask the user if they want to save the results to a text file.

Of course, then Iím going to have to read up on save dialogs and writing to text files, but I expect there should be ready-made solutions in the frameworks already.