I don’t think either of them represent classical MVC.
Model
The model is the data and the business logic. A model contains the state of the running application. It should report data and state. It should validate input. It should update data and state based on input.
The model should be independent of use. In iOS, this generally means it should derive from core Objective-C classes and use only core Objective-C objects. A good test for this would be to see if the model would work for other compilers like GCC Objective-C, or if it will work in other environments like OS X.
The model should be externally controllable. The model should be able to be driven by unit-tests or driven by a completely separate set of controllers.
View
A view is anything that is displayed to a user. In iOS, this generally means anything derived from UIView (UILabel, UIButton, …) Views are dumb: meaning that they know nothing of the model, business logic, or context in which they are created.
Controller
The controller binds model data to views so it can be displayed to users, provides context to views so they are shown correctly (disabled, highlighted, …), and handles user input to update the model. In iOS, this generally means anything derived from UIViewController. A controller may need to process model data in some small way to get it into a view. A controller may need to process user input in some small way to make it suitable for the model.
Data Store
The data store is the permanent repository for model data. This can be a database or a web service. Usually, data stores stand outside of MVC or are attached to the model.
My Answer
Now that I got that out of the way, my answer to your question: the MVC described above is the approach I use. This has implications, some of which are difficult to deal with.
A view must know nothing of the model. If you have a property in your view named price
or total
, then you’re doing it wrong.
A controller must not contain business logic. If you have a controller calculate sales tax, then you’re doing it wrong.
Addressing concerns in the comments
Warning: I'm not an Android developer and have limited understanding of how
Android works.
Like data stores, long running async processing sits outside of MVC. If you need a service that updates the model, then think of it as method of the model. If you have a service that transforms data to show it to a user, then think of it as a method of a controller.
Novel Input
Like all input, broadcast receivers, orientation changes and task switching are tasks that should be handled by a controller. I think of iOS’s application delegate as a controller for the whole application: handling input which effects the whole application (like system notifications and application lifecycle changes).
Business Logic and Model Data
I see the Model composed of two parts, model data and business logic. I've learned it’s best to keep them separate. In practice, this means I wrap model data with business logic or add business logic on as a category of the model data.
Who I Am
To put it bluntly, it doesn’t matter who I am. I don’t think I’ve had a good original thought in my whole life. I’ve based my work on understanding patterns that other developers have created and applying them as best I can to the work I need to get done.
Since you asked, I’ve never worked in academia. I’ve been a software developer for the past 14 years. I’ve worked on code at all levels from drivers and debuggers to shiny custom views and silly apps. I’ve worked on iOS for the past 2½ years and have written many apps which have been released in the app store. I use my understanding of patterns to get my work done on time and on budget.
Update More questions in comments.
Models
I try to keep my data models as clean as possible. Usually they are mostly properties, sometimes conforming to NSCopying
, NSCoding
, and/or a JSON mapping. More and more, I include Key-Value Validation. This can be part of the data model or in a business logic category.
- (BOOL)validateValue:(inout MyValue **)value error:(out NSError **)error
{
if (![self complexTestWithValue:*value]) {
if (error != NULL) {
// Report error to caller.
*error = [self errorInValidation];
}
return NO;
}
return YES;
}
Business Logic
In the past, I've written lots of code which looks like
[dataSource fetchObjectsWithInput:input completion:^(NSArray *objects, NSError *error) {
// Model objects are in objects
}];
or
[dataSource fetchObjectsWithInput:input completion:^(MyClassResult *result, NSError *error) {
// Model objects are in result.objects
}];
Recently, I've gotten into promises (I'm using PromiseKit).
[dataSource fetchObjectsWithInput:input].then(^(MyClassResult *result) {
// Model objects are in result.objects
});
This has been a good general purpose factory pattern for Objective C.
I tend to write business logic computations in categories.
@interface MyClass (BusinessLogic)
- (NSString *)textForValue; // A string to be used as label text.
- (MyResult *)computeOperationWithInput:(MyInput *)input error:(out NSError **error);
- (MyOtherClass *)otherObjectByTransformingValues;
@end
The only pattern I've seen, that works is "When the code gets to hard to figure out what's going on, it's time to stop using an anonymous class.".
In your example, the method body is the anonymous class. I might move the "new SelectionAdapter" onto it's own line, to make the opening paren a little more obvious, but otherwise it's fine. Where you get into trouble is when a random person can no longer figure out where the anonymous class and method code are.
I've also create a private variable, or method to hold the anonymous class. I put that kind of thing at the bottom of the class, out of the way. It kinda sorta bridges the gap between a whole new class, and keeping it in the middle of the code. Some people consider it bad style though, but my general rule of thumb is to code for readability first.
Best Answer
As with many things, the correct approach depends on what you are trying to do for the specific button and what else you are doing with the activity.
Activity class implements interface:
This is a good option when you only have one type of task to execute when this listener is called. An example of this would be a simple form with a number of fields and a save button. I prefer to not have my event listener check the source of the event in order to decide what actually needs to be done. I know that some may say this is a style thing, but I believe that by not requiring the listener to do this check makes the code easier to follow as you will know exactly what is being called for each event.
A different class implements interface:
As I said above, I prefer this option for when I have multiple items that can fire the same event. Extending the above example, lets add a clear button that also requires a click listener. Create one listener that that preforms the save actions and one that preforms the clear actions. Each listener is only added to the components that will produce that action.
This implementation has an additional benefit that you can utilize if you care. The benefit is that it prevents other classes from triggering the event inside of your activity class. Since the interface method must be public, anyone with a reference to the class can fire the event. If you want fine grained control over who can do what in the application, a separate class prevents anyone with a reference to the activity to trigger your form to be cleared or saved (or potentially breaking the code if the listener utilizing the source, but does not handle bad input).
An anonymous inner class implements interface:
This is really just a specific way to construct the second option of using a different class as the implementation. This option can even further restrict who has access to trigger the event as no one else can create an instance of the class. However, I think the more important factor between the two options is how much work is being done. Clearing a few text fields is a simple and straight forward operation. However, the process of saving the for can involve a number of task is you are validating the input (which you should be doing), writing to a database to store the values and triggering some post save action. In this case, making a separate class with its own file will provide a clearer divide between the input form and the data processing. This in turn keeps the form code instead of a larger file with multiple inner classes nested inside.