Saturday 10 January 2009

SimpleCalculator

Let's move on and implement a somewhat more complex application.
(for the impatients : you can download the source code here ; don't forget to change the project settings in XCode before you compile).

I will now implement a simple calculator (see image) which is in fact an implementation of the MVC design pattern.


Now first a few words on MVC, the Model -View-Controller design pattern is probably one of the oldest design pattern around and mostly used to develop GUI's.
The idea is that you seperate the representation (UI) from the domain model and the interactions of the GUI with the model is implemented in the controller.

This has the advantage that for example you can have several visual representations of the same model.

So step 1 is to define the model :

/* CalculatorModel */

#import

@interface CalculatorModel : NSObject
{

float value1 ;
float value2 ;
float result ;

float memory ;

int depth ;

}

- (void) push:(float) v ;
- (void) clear ;
- (float) result ;
- (void) plus ;
- (void) min ;
- (void) mul ;
- (void) div ;
- (void) memoryPlus ;
- (void) memoryMin ;
- (void) memoryClear ;
- (float) memory ;
@end


And the corresponding implementation

#import "CalculatorModel.h"

@implementation CalculatorModel

- init
{
[super init] ;
depth = 0 ;
value1 = 0 ;
value2 = 0 ;
memory = 0 ;
//NSLog(@"Init calculator model" ) ;

return self ;
}

- (void) memoryPlus
{
memory += result ;
}

- (void) memoryMin
{
memory -= result ;
}

- (void) memoryClear

{
memory = 0 ;
}

- (float) memory
{
return memory ;
}

- (void) push:(float) v
{

if ( depth == 0 )
{
depth = 1;
value1 = v ;
}
else
{
depth = 0 ;
value2 = v ;
}
}
- (void) clear ;
{
depth = 0 ;
value1 = 0.0 ;
value2 = 0.0 ;
}

- (float) result
{
return result ;
}

- (void) plus
{

result = value1 + value2 ;

}
- (void) min
{

result = value1 - value2 ;

}
- (void) mul
{
result = value1 * value2 ;


}
- (void) div ;
{
if (value2 > 0 )
{
result = value1 / value2 ;
}
else result = 9999999999.0 ;



}


@end


So now we have our CalculatorModel. As you can see a very basic calculator. You have the basic calculations and a memory . I already provided a stack (with depth 1) to extend it later to a complex calculator model.

The next step is to build the view. This view  (see picture above) is designed using the InterfaceBuilder, but before we can associate the actions we need to create the controller also.

As already explained : in the controller we define the actions and the outlets which are bound to UI objects in the view (the bindings is done via the InterfaceBuilder)

The definition of the controller looks like this :

/* SimpleCalculatorController */

#import
#import "CalculatorModel.h"

@interface SimpleCalculatorController : NSObject
{
IBOutlet NSTextField *display ;
CalculatorModel *calculator ;
bool hasDecimal ;
bool afterCalculation ;
int action ;

NSMutableString *value ;
}
- (void) genericPush:(int) digit ;
- init ;

- (IBAction) pushOne:(id) sender ;
- (IBAction) pushTwo:(id) sender ;
- (IBAction) pushThree:(id) sender ;
- (IBAction) pushFour:(id) sender ;
- (IBAction) pushFive:(id) sender ;
- (IBAction) pushSix:(id) sender ;
- (IBAction) pushSeven:(id) sender ;
- (IBAction) pushEight:(id) sender ;
- (IBAction) pushNine:(id) sender ;
- (IBAction) pushZero:(id) sender ;
- (IBAction) pushDecPoint:(id) sender ;

- (IBAction) pushPlus:(id) sender ;
- (IBAction) pushMin:(id) sender ;
- (IBAction) pushMul:(id) sender ;
- (IBAction) pushDiv:(id) sender ;


- (IBAction) pushResult:(id) sender ;

- (IBAction) pushMemoryAdd:(id) sender ;
- (IBAction) pushMemoryMin:(id) sender ;
- (IBAction) pushClearMemory:(id) sender ;
- (IBAction) pushRecallMemory:(id) sender ;

- (IBAction) pushClear:(id) sender ;
@end


The corresponding implementation is the following :

#import "SimpleCalculatorController.h"


@implementation SimpleCalculatorController

- init
{
[super init] ;



value = [ [NSMutableString alloc] init] ;
[value appendString: @"" ] ;
calculator = [ CalculatorModel alloc] ;
hasDecimal = false ;
afterCalculation = false ;

return self ;
}

- (void) pushToCalculator
{

NSNumber *number ;
number = [[[NSNumberFormatter alloc] init] numberFromString: value] ;
[calculator push: [number floatValue ]] ;

value = [ [NSMutableString alloc] init] ;

hasDecimal = false ;
}

- (void) genericPush:(int)digit
{



if ( afterCalculation )
{
afterCalculation = false ;
value = [ [NSMutableString alloc] init] ;
}

if ( value == nil ) NSLog( @"NIL") ;
[value appendString: [NSString stringWithFormat: @"%d", digit]] ;
[display setStringValue: value ] ;

}


- (IBAction) pushOne:(id) sender
{
[self genericPush:1] ;
}

- (IBAction) pushTwo:(id) sender
{
[self genericPush:2] ;
}

- (IBAction) pushThree:(id) sender
{
[self genericPush:3] ;
}

- (IBAction) pushFour:(id) sender
{
[self genericPush:4] ;
}

- (IBAction) pushFive:(id) sender
{
[self genericPush:5] ;
}

- (IBAction) pushSix:(id) sender
{
[self genericPush:6] ;
}

- (IBAction) pushSeven:(id) sender
{
[self genericPush:7] ;
}

- (IBAction) pushEight:(id) sender
{
[self genericPush:8] ;
}

- (IBAction) pushNine:(id) sender
{
[self genericPush:9] ;
}

- (IBAction) pushZero:(id) sender
{


[self genericPush:0] ;
}

- (IBAction) pushDecPoint:(id) sender
{
if (hasDecimal) return ;
hasDecimal = true ;
[value appendString: @"."] ;
[display setStringValue: value ] ;

}

- (IBAction) pushPlus:(id) sender

{
[self pushToCalculator] ;
action = 1 ;

}
- (IBAction) pushMin:(id) sender
{
[self pushToCalculator] ;
action = 2 ;
}
- (IBAction) pushMul:(id) sender
{
[self pushToCalculator] ;
action = 3 ;
}
- (IBAction) pushDiv:(id) sender
{
[self pushToCalculator] ;
action = 4 ;
}


- (IBAction) pushResult:(id) sender
{
NSNumber *number ;
number = [[[NSNumberFormatter alloc] init] numberFromString: value] ;

[calculator push: [number floatValue ]] ;
if ( action == 0 ) return ;
if ( action == 1 )
{
[calculator plus] ;
}
if ( action == 2 ) [calculator min] ;
if ( action == 3 ) [calculator mul] ;
if ( action == 4 ) [calculator div] ;


value = [ [NSMutableString alloc] init] ;
hasDecimal = false ;
afterCalculation = true ;
action = 0 ;
[value appendString: [NSString stringWithFormat: @"%f", [calculator result]]] ;
[display setStringValue: value ] ;

}

- (IBAction) pushMemoryAdd:(id) sender
{
[calculator memoryPlus] ;
}
- (IBAction) pushMemoryMin:(id) sender
{
[calculator memoryMin ] ;
}
- (IBAction) pushClearMemory:(id) sender
{
[calculator memoryClear] ;
}

- (IBAction) pushRecallMemory:(id) sender
{
value = [ [NSMutableString alloc] init] ;
[value appendString: [NSString stringWithFormat: @"%f", [calculator memory]]] ;
[display setStringValue: value ] ;
}
- (IBAction) pushClear:(id) sender
{
[self init] ;
[display setStringValue: value ] ;
}


@end


Now this controller has a simple implementation, later on I'll revisit this controller to make it more cleaner.
To show how the view is build and bound to the controller I would like to show that via video or something. But that I still need to create.

So far the simple calculator application, it was very easy to build using XCode (it took me no more than a half a day). The reader will see that I didn't take much attention to memory management and so.
I come back to this later on, because memory management is important with larger and more complex applications



No comments:

Post a Comment