Wednesday, October 14, 2009

Section 5.1.  Plan of Action










5.1. Plan of Action



5.1.1. Program Tasks


We'll use Linrg unchanged for the computational part of the program. Our front-end program will therefore have the tasks of


  • Storing a list of x and y data pairs

  • Allowing the user to enter and edit the data-pair list

  • Passing the data-pair list to Linrg

  • Reading the results from Linrg and displaying them to the user



Keeping Linrg as a separate executable is admittedly a strange decision: The code is so simple, it would be easier to fold it into the main application. We're doing it this way in order to illustrate a few additional points. If Linrg were a large-address-space application, using a 64-bit address space under Mac OS X 10.4 or later, it might make sense to build it as a 64-bit utility, with a 32-bit application running its human interface.






5.1.2. Model-View-Controller


Cocoa applications are built around the design pattern called Model-View-Controller (MVC). The pattern asserts that three kinds of things comprise an interactive program.


  1. Model objects embody the data and logic of a particular problem domain. Models tend to be unique to each application.

  2. View objects handle user interaction, presenting information and enabling the user to manipulate data or otherwise influence the behavior of the program. Views are usually drawn from a repertoire of standard elements, such as buttons, tables, scrollers, and text areas. Views tend to have no intelligence about any particular problem domain: A button can display itself and report button clicks without having to know what clicking would mean in your application.

  3. Controller objects mediate between the pure logic of the model and the pure mechanics of the views. A controller object decides how model content will be displayed by the views and how user actions translate into model events.



The Model

It seems plain that the first task of our programstoring a list of data pairsis the sort of task a model object performs. Similarly, the task of calculating the linear regression is purely a function of the data we present to the calculation and would be the same no matter how we managed the data points beforehand or presented the results afterward.


What we want, then, is a document containing a set of data points and the results of calculating a linear regression on them. This simple design is shown in Figure 5.1.



Figure 5.1. The minimal data model for a program that manages linear regressions. The document contains a list of data points and the results of the regression done on them. The regression refers to the list of data points.







We will be working in Objective-C, which provides the easiest access to Cocoa. From the figure, it's natural to imagine the interface for a DataPoint object:


@interface DataPoint : NSObject <NSCoding> {
double x;
double y;
}

- (id) init;
- (id) initWithX: (double) xValue Y: (double) yValue;

- (double) x;
- (void) setX: (double) newValue;
- (double) y;
- (void) setY: (double) newValue;
@end


This code segment declares DataPoint to be a subclass of the basic NSObject class, promises that DataPoint will be able to read and write itself in data streams according to the NSCoding protocol, and says that its data consists of two double-precision numbers: x and y. The code then declares a default initializer (init) and an initializer that sets the instance values (initWithX:Y:). After that come accessors for reading and setting the x and y values. Simple.


The interface for the Regression class is mostly the same concept, applied to the four data members instead of two:


@interface Regression : NSObject <NSCoding> {
NSMutableArray * dataPoints;
double slope;
double intercept;
double correlation;

NSTask * linrgTask;
}

- (id) init;

- (double) slope;
- (void) setSlope: (double) aSlope;
- (double) intercept;
- (void) setIntercept: (double) anIntercept;

- (double) correlation;
- (void) setCorrelation: (double) aCorrelation;

- (NSMutableArray *) dataPoints;
- (void) setDataPoints: (NSMutableArray *) aDataPoints;

- (BOOL) canCompute;
- (void) computeWithLinrg;
@end


Once again, we see the descent from NSObject, the promise of NSCoding, the four data members, and accessor methods for those members. We make dataPoints an NSMutableArray, which is a Cocoa class that keeps ordered lists of objects. There are two additional public methods.


  1. The method canCompute returns YESthe Objective-C equivalent of trueonly if at least two data points are available. This isn't a comprehensive test for whether the regression would be valid, but it's a start.

  2. The method computeWithLinrg calculates the slope, intercept, and correlation coefficient.


One data member, linrgTask, didn't figure in our sketch model. This data member is an NSTask, an object used for running command line tools from Cocoa applications. We'll be using an NSTask to run Linrg on our data points.




The Controller

The controller object we create will be an instance of MyDocument, a subclass of Cocoa's NSDocument. Xcode's template for a new Cocoa document-based application automatically includes a MyDocument class with skeleton code in the project.


NSDocuments are automatically placed in the command-response chain of the application and bring with them the basics of loading and storing a document's contents in the file system. We can expect our documents to be told to load and store themselves, to compute the linear regression, and to add, edit, and remove data points. Therefore, we will want to provide methods for loading and storing and for computing. These tasks can be done through Regression, our top-level model object, so we conclude that MyDocument need add a data member only for a Regression object.



Accessorizer


Note that for every property named propertyName of type type in a class, the key-value coding (KVC) protocol has us writing a pair of methods:


  • (type) propertyName;

  • (void) setPropertyName: (type) aValue;


The individual methods are not difficult to writethey are almost identicalbut they are tedious. Can't we automate the task of generating property accessors?


We can. The first resort is to Xcode's Script menuit appears in the menu bar as a scrollin the Code submenu. Select the four lines that declare Regression's instance variables, and select Script Code Place Accessor Decls on Clipboard. You can now paste declarations for setter and getter methods for each of the three instance variables into the header. Reselect the instance-variable declarations, and select Place Accessor Defs on Clipboard so you can paste the complete methods into the implementation file.


Kevin Callahan's Accessorizer application is a more comprehensive tool for generating accessors and many other kinds of boilerplate code for Cocoa. (Visit his site at http://www.kevincallahan.org/software/accessorizer.html) The application can be configured extensively to fit almost any style of code layout and philosophy of architecture for accessors. If you do a lot of Cocoa programming, Accessorizer will make your life easier and be worth the donation the author asks.




Strangely, we won't be providing any methods for managing the data points. More on this later.


@class Regression;

@interface MyDocument : NSDocument
{
Regression * model;
}
- (IBAction) compute: (id) sender;
@end


By the vagaries of the Objective-C language, it is not necessary to declare in the interface every method a class implements. One method we do need to declare is compute:, the method that triggers calculation of the regression. As a method that responds to commands from the application's human interface, compute: follows a strict signaturetaking one anonymous object (type id), the sender of the command, and returning IBAction, which is #defined as void but serves to inform Interface Builder that this command may be issued to this class by the human interface.




The View(s)

The view layer of our program is best specified by how we want it to look and behave. Figure 5.2 shows what we're aiming for.



Figure 5.2. Planned layout of the main window of our linear-regression program. The Add and Remove buttons insert and delete points in the data set, which can be edited directly in the table at left. The Compute button passes the points to Linrg and causes the results to be displayed in the read-only fields below it.



















No comments:

Post a Comment