views:

72

answers:

2

I have a BankAccount model class that contains data like account number, bank name, transactions, etc. The transactions are each instances of the Transaction class and contained in the transactions NSArray.

I also have a BankAccountView that displays all this in a single view. Right now I'm passing all the data as separate variables (ie. bankAccountView.accountNumber = bankAccount.accountNumber, bankAccountView.bankName = bankAccount.bankName).

The problem/dilemma arises when I need to pass the transactions to the view. I learned to keep the model and view classes separated, so I assume it's not a good thing to just pass the transactions array of BankAccount to BankAccountView, since that array contains instances of the Transaction model class.

So what I'm doing now is this: I take each Transaction instance, translate it into an NSDictionary, then set bankAccountView.transactions to an NSArray containing those dictionaries. It works and I feel like it's keeping my model and view classes separate, but it also feels overly complex, like I'm writing a lot of code for someting that could be much simpler.

Is there a better way to do this? Thanks in advance.

+3  A: 

I think you took it way too far already and should pass the whole BankAccount object directly. No conversion, no surroundings, just pass it all along to the view. The kind of separation you did (to me) feels a bit like shooting with cannons at flies...

My arguments:

  • your view is capable of displaying BankAccounts only, why not pass the object it's displaying?
  • this makes your interface very clear, only one property is needed: @property(...) BankAccount *bankAccount;
  • (EDIT) passing an object encapsulates the dependencies between all your properties. They ain't independent, they form a bank account. This should be visible.
  • no conversion is needed, the interface does not need to be changed if you extend the model
  • the very very strong division between model and view was needed only if your view was independent from the data it's displaying. There is no point in anonymizing a view that can display only one very specific type of data: a bank account.
  • All the conversion would only make sense if you compose your view of several reusable components. You don't seem to so this is simply unnecessary work.
  • MVC is still preserved: Model does not know controller or view, view does not directly know controller, controller passes data from view to model and reacts to actions.

If you do not want to pass the class directly, design a protocol that encapsulates all properties the view needs from a bank account object and have the class BankAccount implement it. However, this only makes sense if you (plan to) pass different kinds of classes as data to the view. So different that these classes don't share a common superclass capable of all these properties. If there is only this simple class or inherited classes, stay with the simplest possible solution: pass the account directly.

In order to be able to trigger redraw on changes, I suggest you use Key-Value-Observation. It's a very clean way to keep the anonymity and having to write very few code. In your init method in the view do the following for each property you'd like to observe:

[self addObserver:self forKeyPath:@"bankAccount.<property>" withOptions:0 context:@"redraw"];

Then you implement observeValueForKeyPath...:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if (context == @"redraw") {
        [self setNeedsDisplay];
    } else {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}

I find this to be a very clean method with very little overhead, which I happen to heavily use in my own projects.

Max Seelemann
Well that answers my question completely. Thanks!
Rits
Oh, I have one extra question regarding this: previously, I added a setNeedsDisplay call to each of my setters in the BankAccountView class, so that when I changed one of the properties the view would be redrawn.But now, I don't change any properties, the passed BankAccount object changes internally. What is the best way for BankAccountView to keep track of these changes?BankAccount is a Core Data generated class.
Rits
Personally, I'd user Key-Value-Observation. I'll adjust my answer accordingly in a second...
Max Seelemann
+1 I wish I could upvote this more. Max, this is an excellent discussion of things that are rarely talked about with such clarity.
quixoto
+1  A: 

The way I look at the MVC paradigm (and remember, it's just a paradigm — it's open to interpretation) is the Model knows nothing about the view and the view knows nothing about the model. They should not directly interact; your view should not have an instance of your model, strictly speaking.

This is where the controller becomes important. I'm going to pretend your BankAccountView has some labels to display the account info, and maybe a tableview to show all account transactions (let's pretend, just for illustration). So then you would have something like this:

MyBankAccountViewController is a UIViewController subclass (the C in MVC), whose view is an instance of BankAccountView. Your view controller also has an instance of a BankAccount. At an appropriate time (such as -viewDidLoad), you'll want to populate your view (and its subviews) with the information in your BankAccount model (self.bankAccountView.accountNameLabel.text = self.myBankAccount.name or something).

MyBankAccountViewController would also act as the delegate and datasource to the tableview in your view, providing it with cells listing information about the account's transactions.

When something happens in your view that needs to change your model (for example, the user presses a "Close this account" button), the "event" would be sent from the view to the controller (either through delegation, target action, or some other mechanism you choose). The controller then decides what to do, such as [self.bankAccount closeBankAccount];.

Like I said, this is just how I interpret MVC (I view it strictly and pedantically), and it might complicate your code more than just passing the model in directly. If you're just going to be using this view in exactly one place, and never plan on reusing it, then passing in directly would likely be simpler. Just keep in mind that comes at the expense of making it more difficult to reuse in the future (which is one of the key selling-points of MVC: your models and your views should be reusable; your controller won't be).

jbrennan