PDA

View Full Version : Question about the correct MVC layout of app and the best way to access the Modal




luckylefty01
Jul 5, 2008, 01:21 AM
I've got a situation where I can think of several ways of doing what I need to do, but I don't really have the experience to judge which is best or why as of yet, so I'd really like some suggestions as to what you guys would prefer. This is a pretty simple app, but I want to do this one right so that when I write larger apps later I'll know how to do it without going through all this again.

I've got an iPhone app using the Utility Application template (two views (a front and a back), controllers for each, a root controller that inits the two views, and an app delegate).

I have a simple modal class that I wrote which stores some counters and has some methods to increment, decrement those counters, etc.

I need both sides of the app (both views) to be able to deal with the same instance of the modal, so one side can display a value which can be changed by the other side, etc.

My question is about the best way to make sure that both sides have access to that instance of the modal, and where to store said instance of said modal.

I could make my modal a singleton, thereby letting each controller access the same instance all the time. Views could each observe the necessary values and tell the app to perform various methods when actions are invoked.

I could let the app delegate init the Modal and have the views each get a pointer to that modal when they're inited using modal = [[UIApplication sharedInstance] delegate].modal; again, views could each observe the necessary values and tell the app to perform various methods when actions are invoked. In some ways I like this one best except that it assumes that the app delegate will always return the same address/instance when it's asked for the modal, which although it should is not guaranteed.

I could let the app delegate be an intermediary between the view controllers and the modal. Any time the views needed to update something they'd send a message to the app controller which would send a message to the modal. The app controller would also observe the necessary values and tell the view controllers to update their views when those values changed. On the other hand, would this preserve MVC better since only one class (the app delegate) would need to be updated if the Modal was changed?

I'm definitely over thinking this, but I'm still trying to understand how to use MVC and best respect encapsulation in an application I'm writing myself from scratch.

So anyway, how would you guys set up such an app? Am I missing something completely obvious?

I've looked at all of the apple apps, but most of them don't really seem to have a real modal class, and the ones that do don't need that modal to be accessed from multiple places in the app.



tacoman667
Jul 5, 2008, 06:34 PM
I use MVC .NET framework as my first experience writing MVC applications but I read many papers on the MVC design from the GoF(Gang of Four). My personal unserstanding would be that the classes you write to manipulate/massage the data from whatever source are your Models. The views are the UI layer that is ONLY for displaying and allowing the user to input data into a form. The controller is the object that would decide what view to display, the data that displays in the view, as well as the link between manipulating the data using the designated classes in the Model section. The delegate portion eludes me some but at the highest level I feel that they are like an interface alloing the controllers to manipulate data based off of certain events or actions taken by the object from which the delegate is derived. I would not use the delegate to circumvent the role of the controller and model and I would also, personally, not have a static instance of your modal. The singleton should be the best route to go and I would initialize a new instance and deallocate it after every use if you can in order to keep the application small in the memory footprint.

But that's just me.. :)

AlmostThere
Jul 6, 2008, 06:43 AM
I am not familiar with iPhone development, but maybe this will help point you in the right direction (non-specific pseudo code).

class Model
{
int id;
string name;
}

class Controller
{
Model *theModel
function onUpdateModelNameButtonClick(eventArgument)
{ theModel->setName(extractNewNameFromEventArgument) }
}

class View
{
Model *theModel;
function paintView()
{ drawString(theModel->getName()) }
}


When you load a new document (or whatever) you will be setting the pointers (or references) to the model class.

Suppose you want to re-use the same Controller and View for a different Model, then you might create a delegate


class NewModel
{
int code;
string text;
}

class Delegate implements Model
{
NewModel *newModel;

void setName(string name)
{ newModel->setText(name); }

string getName()
{ return newModel->getText() }
}

This will allow you to reuse your View and Controller for different Models but, for a small app, with domain specific (i.e. only talking about your own model class) this will probably be overkill. I would say, make your app, keep going, and when it is done, look back at the flaws and learn from the experience.

There isn't really a "right" way for many of these problems, just costs and benefits that vary with each app.

Having written all that, it seems to be that your problem isn't really so much with MVC as it is initialising the model, which is more a Cocoa question rather than general MVC (different frameworks treat documents and apps differently). If so, you might want to re-phrase the question, perhaps focusing on Cocoa best practices.

luckylefty01
Jul 7, 2008, 12:52 AM
Using pseudocode to explain my problem is actually a pretty good idea. What both you you guys said helped some, but still leaves me unsure of the Cocoa best practice in this situation (especially since this is just using a template provided by Apple except for the added modal...I have to suspect I'm not the first person to try to do this). So let me see if I can do a better job of explaining it now.

This is what I have:

//It's actually a bit more complex than this,
//but this is the basic functionality
class Model
{
int counter;
int counterStartValue;

function decrementCounter {--counter}
function resetCounter {counter = counterStartValue}
}

// In Apple's apps this tends to just be used to instantiate the front and back
// and deal with flipping between the two (as in a widget).
class RootViewController
{
MainViewController *mainViewController;
FlipsideViewController *flipsideViewController;

//Runs on instantiation of this class
function viewDidLoad {//Load mainViewController}

//Runs when view is toggled
function loadFlipsideViewController {//Load flipsideViewController}
}

class MainViewController
{
Modal *modal
Outlet UILabel *label

function onButtonClickDecrementModal(sender) {modal->decrementModal()}

/*
KVO code to keep label displaying the number in counter
*/
}

class FlipsideViewController
{
Modal *modal
Outlet UILabel *label

/*
KVO code to keep label displaying the number in counter
*/

function onButtonClickResetModal(sender) {modal->resetModal()}


}

class AppDelegate
{
RootViewController *rootViewController

function applicationDidFinishLaunching {//Instantiate RootViewController}
}

class MainView
{
//Just a typical UIView, no custom drawing code here right now
}

class FlipsideView
{
//Just a typical UIView, no custom drawing code here right now
}


So the problem is getting both instances of modal to be the same. The way I'm doing it at the moment is to add the modal to the app delegate and have the two subclasses call that to get the modal upon their init as so:

class MainViewController
{
Modal *modal
Outlet UILabel *label

//Gets application through [[UIApplication] sharedInstance]
function init {modal = application->getDelegate->modal} //NEW

function onButtonClickDecrementModal(sender) {modal->decrementModal()}

/*
KVO code to keep label displaying the number in counter
*/
}

class FlipsideViewController
{
Modal *modal
Outlet UILabel *label

//Gets application through [[UIApplication] sharedInstance]
function init {modal = application->getDelegate->modal} //NEW

function onButtonClickResetModal(sender) {modal->resetModal()}

/*
KVO code to keep label displaying the number in counter
*/
}

class AppDelegate
{
RootViewController *rootViewController
Modal *modal //NEW

function applicationDidFinishLaunching {//Instantiate RootViewController}
}

I don't really like that way very much as it seems to conflict with some of the best practices I do know about and just bugs me.

One new possibility I came up with was to originally instantiate the modal in the rootViewController and then have it set the modal in the subclasses when it instantated them:

class MainViewController
{
Modal *modal
Outlet UILabel *label

function onButtonClickDecrementModal(sender) {modal->decrementModal()}
function initWithModal(aModal) {super->init; modal = aModal} //NEW

/*
KVO code to keep label displaying the number in counter
*/
}

class FlipsideViewController
{
Modal *modal
Outlet UILabel *label

function onButtonClickResetModal(sender) {modal->resetModal()}
function initWithModal(aModal) {super->init; modal = aModal} //NEW

/*
KVO code to keep label displaying the number in counter
*/
}

class RootViewController
{
MainViewController *mainViewController;
FlipsideViewController *flipsideViewController;

Modal *modal //NEW

//Runs on instantiation of this class
function viewDidLoad {//Load mainViewController}

//Runs when view is toggled
function loadFlipsideViewController {//Load flipsideViewController}
}

Which seems a bit better as far as not making weird convolutions off into delegate land and such.

The last possibility I've come up with is just making a the modal a singleton, meaning you could call sharedInstance at any point and always get the same instance. The problem I've found with this is that if you wanted multiple counters (e.g. one for something counting down from 6 and one for something counting down from 10), you really couldn't have that without going to a bunch more trouble of either creating a new class of modal or setting up the modal you have to allow for multiple counters.

class Modal {
int counter;
int counterStartValue;

function decrementCounter {--counter}
function resetCounter {counter = counterStartValue}

function sharedInstance {return self} //NEW

/*
A bunch of code for making sure that only one copy of this modal is ever in
existence and overriding init, etc.
(docemented in Cocoadev, among other places)
*/
}

class MainViewController
{
Modal *modal
Outlet UILabel *label

function init {modal = Modal->sharedInstance} //NEW

function onButtonClickDecrementModal(sender) {modal->decrementModal()}

/*
KVO code to keep label displaying the number in counter
*/
}

class FlipsideViewController
{
Modal *modal
Outlet UILabel *label

function init {modal = application->getDelegate->modal} //NEW

function onButtonClickResetModal(sender) {modal->resetModal()}

/*
KVO code to keep label displaying the number in counter
*/
}

Any thoughts?

tacoman667
Jul 7, 2008, 07:07 AM
So why aren't you having the modal instanced in the controller and then passed ot the view objects at init? This way the controller will still own the modal and it stays the same instance yet the views can access the same modal when needed? The views will only need a pointer to the modal given by the controller that is controlling the view anyways so the modal always stays in the current context. If you were to need to span this modal over multiple controllers/views, then I would say go the route of the delegate and create a global singleton of the modal.

Does this help?