Wednesday, August 24, 2011

Decorator, Builder and Factory Design Patterns in Objective C

Some people get confused with these 3 patterns (Like me), for you objective c programmers I've rewritten the 3 patterns in example implementations and uploaded them to github, if you can do better, by all means tell me and I'll use your link instead.

Decorator
Builder
Factory Method

All 3 patterns are similar in their achievement of decoupling code, but they have unique purposes and it's necessary to understand these differences, and for those with a visual brain, it's best to see the code alongside reading about them.

If you're a learning cocoa/objective c programmer and you know the time savings incurred from good use of design patterns then you'll gain a lot of key notes from this book: Cocoa Design Patterns



http://en.wikipedia.org/wiki/Decorator_pattern
http://en.wikipedia.org/wiki/Builder_pattern
http://en.wikipedia.org/wiki/Factory_method_pattern

But to see the value in these patterns, it's necessary to consider the alternatives and Anti-Patterns, and even make some real world mistakes...

Decorator Pattern
https://github.com/mrdavenz/DVDecorator
For those without git, the Decorator Pattern code is copy and paste-able at the bottom of this post.

Builder Pattern
https://github.com/mrdavenz/DVBuilder

Here is a decent enough UML diagram of the Builder Pattern (Found here http://www.dofactory.com/Patterns/PatternBuilder.aspx)

Also a pretty good description is found here: http://www.codeproject.com/KB/architecture/Builder_Design_Pattern.aspx

In the source example, you have a Shop (Director), which has a suite of Custom Builders available, they can be interchanged at run time. If the user asks for a Motorcycle, the Shop owner can simply tell the code to build one using the Builder, and the custom attributes for a motorbike are built.

Without the builder pattern, a developer would simply have more code, and a higher dependence, which over time can reduce readability and discourage the reuse of the code.

So depending on the context, the benefits of the Builder pattern are subtle, but worth using when applicable.

Also don't confuse Builder with Factory:
http://en.wikipedia.org/wiki/Factory_method_pattern

Factory Method
https://github.com/mrdavenz/DVFactoryMethod

The Factory pattern can almost be seen as a simplified version of the Builder pattern.

In the Factory pattern, the factory is in charge of creating various subtypes of an object depending on the needs.

The user of a factory method doesn't need to know the exact subtype of that object. An example of a factory method createCar might return a Ford or a Honda typed object.

In the Builder pattern, different subtypes are also created by a builder method, but the composition of the objects might differ within the same subclass.

To continue the car example you might have a createCar builder method which creates a Honda-typed object with a 4 cylinder engine, or a Honda-typed object with 6 cylinders. The builder pattern allows for this finer granularity.
Often, designs start out using Factory Method (less complicated, more customizable, subclasses proliferate) and evolve toward Abstract Factory, Prototype, or Builder (more flexible, more complex) as the designer discovers where more flexibility is needed.
Sometimes creational patterns are complementary: Builder can use one of the other patterns to implement which components get built. Abstract Factory, Builder, and Prototype can use Singleton in their implementations.

A great comparison on stackoverflow found here:

what-is-the-difference-between-builder-design-pattern-and-factory-design-pattern

The Builder class is often used to build complex objects step by step, perhaps ordering matters. And is frequently used in conjunction with the Composite pattern, http://en.wikipedia.org/wiki/Composite_pattern, the common composite example in Cocoa UIKit is the use of UIView, where UIView has can have many subViews, that all have a common set of attributes, such as the Frame Size.

In iOS this pattern is analogous to the


Further reading:

builder-vs-decorator-pattern


#import 

@interface Car : NSObject

- (NSString *) getDescription;
- (double) getPrice;

@end

#import "Car.h"

@implementation Car

- (id)init
{
    self = [super init];
    if (self) {
        // Initialization code here.
    }
    
    return self;
}

- (NSString *) getDescription{
    return @"Car";
}

- (double) getPrice{
    return 99999.0;
}

@end



#import "Car.h"

@interface FamilyCar : Car{
    
@private
    Car *car;
}

- (id)initWithCar : (Car *) theCar;

@property (assign) Car *car;

@end

#import "FamilyCar.h"

@implementation FamilyCar

@synthesize car;

- (id)initWithCar : (Car *) theCar{
    self = [super init];
    if (self) {
        // Initialization code here.
        self.car = theCar;
    }
    
    return self;
}

- (NSString *) getDescription{

    return [NSString stringWithFormat: @"%@::FamilyCar", self.car.getDescription];
}

- (double) getPrice{

    return  (self.car.getPrice + (self.car.getPrice * 0.1));
}

@end



#import "Car.h"

@interface Sunroof : Car{

@private
    Car *car;
}

- (id)initWithCar : (Car *) theCar;
- (NSString *) getDescription;
- (double) getPrice;

@property (assign) Car *car;

@end

#import "Sunroof.h"

#pragma mark -
#pragma mark Private Methods go here.

@interface Sunroof()

@end

#pragma mark -
#pragma mark Implementation

@implementation Sunroof

@synthesize car;

- (id)initWithCar : (Car *) theCar{
    self = [super init];
    if (self) {
        self.car = theCar;
    }
    
    return self;
}

- (NSString *) getDescription{
    
    return [NSString stringWithFormat: @"%@::Sunroof", self.car.getDescription];
}

- (double) getPrice{
    
    return  (self.car.getPrice + (self.car.getPrice * 0.2));
}

@end



#import "Car.h"

@interface Airbag : Car{
    
@private
    Car *car;
}

@property (assign) Car *car;

- (id)initWithCar : (Car *) theCar;
- (NSString *) getDescription;
- (double) getPrice;

@end
#import "Airbag.h"

@implementation Airbag

@synthesize car;

- (id)initWithCar : (Car *) theCar{
    self = [super init];
    if (self) {

        self.car = theCar;
    }
    return self;
}

- (NSString *) getDescription{
    
    return [NSString stringWithFormat: @"%@::Airbags", self.car.getDescription];
}

- (double) getPrice{

    return  (self.car.getPrice + 220.0);
}

@end


I chose to test the code in my App Delegate didFinishLaunchingWithOptions method:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Override point for customization after application launch.
    
    Car *standardCar = [[Car alloc] init];
    
    NSLog(@"standardCar: %@ for $%.2f", standardCar.description, standardCar.getPrice);
    
    Car *carWithAirbags = [[Airbag alloc] initWithCar : standardCar];
    
    NSLog(@"carWithAirbags: %@ for $%.2f", carWithAirbags.description, carWithAirbags.getPrice);
    
    Car *carWithEverythingElse = [[Sunroof alloc] initWithCar : [[FamilyCar alloc] initWithCar: carWithAirbags]];
    
    NSLog(@"carWithEverythingElse: %@ for $%.2f", carWithEverythingElse.description, carWithEverythingElse.getPrice);
     
    self.window.rootViewController = self.viewController;
    [self.window makeKeyAndVisible];
    
    return YES;
}

This example was inspired from the Java implementation here : http://java.dzone.com/articles/design-patterns-revisited-2-de

For some further language agnostic discussion on the matter read here:
http://stackoverflow.com/questions/4768349/builder-vs-decorator-pattern



Sunday, August 7, 2011

Net Insight Web Analytics on native iPhone

If anyone needs the code, to post to their web analytics data store, Its on github.
https://github.com/mrdavenz/DVDNetInsightTracking

Tuesday, June 21, 2011

Multiple UIButtons on the UINavigationBar

I needed multiple buttons on the rightBarButtonItem in my navigation bar.

Below is the code I used, notice that I use a subclass of UIToolbar, but using the UIToolbar directly will render the same result.

I originally got the idea from HERE but it wasn't exactly what I needed, so I modified it to fit my needs.




.
.
.
.

// Comment
-(void)viewWillAppear:(BOOL)animated;
{
 [super viewWillAppear:animated];
    
    self.navigationController.navigationBar.topItem.title = @"Contact";
    
    // create a toolbar to have two buttons in the right
    UICustomToolbar* tools = [[UICustomToolbar alloc] initWithFrame:CGRectMake(0.0, 0.0, 113.0, 44.01)]; 
    
    tools.alpha = 0.5;
    
    [tools setBarStyle: UIBarStyleBlackTranslucent];
    
    [tools setTranslucent: YES];
    
    [tools setTintColor: self.navigationController.navigationBar.tintColor];
    
    // create the array to hold the buttons, which then gets added to the toolbar
    NSMutableArray* buttons = [[NSMutableArray alloc] initWithCapacity:3];

    UIBarButtonItem *callUs =  [[UIBarButtonItem alloc] initWithTitle:@"Call"
                                                                style:UIBarButtonItemStylePlain
                                                               target:self
                                                               action:@selector(dial:)];
    
    callUs.style = UIBarButtonItemStyleBordered;
    
    [buttons addObject:callUs];
    [callUs release];
    
    // create a standard "send" button
    UIBarButtonItem *send =  [[UIBarButtonItem alloc] initWithTitle:@"Send"
                                                              style:UIBarButtonItemStylePlain
                                                             target:self
                                                             action:@selector(send:)];
    
    send.style = UIBarButtonItemStyleBordered;
    
    [buttons addObject: send];
    [send release];
    
    // stick the buttons in the toolbar
    [tools setItems:buttons animated:NO];
    
    [buttons release];
    
    // and put the toolbar in the nav bar
    self.navigationController.navigationBar.topItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:tools];
    [tools release];

Sunday, May 8, 2011

Optibay for the Macbook pro (Early 2011) 15" 2.2Ghz I7

Was bored on Sunday, so made this plan for a minor upgrade... which I seem to find myself doing every year around this time (When winter starts) a complete overhaul of my current setup, which is a Dual boot, Macosx 10.6.3/winxpsp3 set up, running a 2009 macbookpro 13" for gaming (Portal 2, Alien Swarm, Company of Heroes, Crysis2, etc.) and personal development with iPhone using xCode/Photoshop CS5/Blender, which runs a Core 2 Duo 2.26Ghz with 4 Gigs of RAM, and a NVIDIA GeForce 9400M GPU with 256MB of dedicated video memory...

Which is pretty good, but then again it's not good enough to play Crysis 2 on a decent resolution, and I needed this fixed...wanted this fixed. So what started out as a bit of a browse on the apple store, which in turn a browse on ebay/amazon/alternative mac reseller stores, which in turn an entire evening reading hardware reviews and watching unboxing videos on youtube, in turn a purchase of a 2011 Macbook Pro 15" 2.2 ghz model New for $2169.99 from a reseller with a warehouse in Sydney to ship from.

After a few beers, I successfully pushed the guilt and logical/rational thinking from my head that I don't need it and it's a waste of time and money, and pushed on, researching SSD's and further upgrades for the beast I'd found myself buying.

I learnt of a Optibay, a add on to use your redundant optical Bay in the MBP, for a second HD spot where my 750GB HD could reside, while the Intel 120GB SSD would take the main spot and be used for the booting of the operating system and running of mission critical applications like Call of Duty: MW 2 and xCode4. After reading the 'how-to guides' for performing this addition, I set out the following layout for my new setup, to for perhaps at least 12 months provide me with an edge over my peers and friends, and allow me some bragging opportunity at LANs, of my grunty SSD and reasonably powerful macbook pro.

Partitions

HD (700GB) (Optibay, replaces the Optical Disc).
NTFS 100GB
FAT32 100GB
MAC FS 500GB

HD (100GB) (Replaces the 750GB from MBP 15") Intel 120
FAT32 32GB BOOTCAMP + WINDOWS XPSP3
Mac FS 68GB MACOSX 10.6.3

Directory Structure of SSD:

MAC FS
- MACOSX
- Portal 2
- CS:S
- XCODE 4
- Development Folder (Includes Build Directories).
- iTunes.
- Blender
- Firefox
- Photoshop FAT32:
- Windows
- Crysis 2
- Dark Space 2
- Alien Swarm

Directory Structure of HD

NTFS
- Windows Installs.

FAT32
- Files to r/w between OS'

MAC FS
- Movies
- Videos
- Installers
- XCode Installs
- Game Installs
- Downloads
- Pictures
- Documents




Parts
1. (Early 2011) Macbook Pro 15” I7 2.2 Quad Core, AMD Radeon Uber Card. New $2169.00 AUD Free Delivery. EBay (Sydney Supplier)
2. Opti-bay (Fenzi Brand) $20.00 Free Delivery (Hong Kong).
3. SSD (Intel 320) 120GB, $300.00, Free Delivery iibuy.com.au.

Sites for reference to use the Optical Drive as a secondary 2.5” HD slot:
http://osxdaily.com/2010/05/19/install-an-ssd-into-the-optical-drive-slot-on-a-macbook-pro
http://lifehacker.com/5541774/how-to-install-a-solid+state-drive-in-your-macbook
http://www.mactalk.com.au/24/98605-installing-ssd-optibay.html

I now wait for the goods to arrive, and look forward to a successful custom set up of my new Macbook Pro. More posts to come

Thursday, April 28, 2011

iOS Contact Details in App

Made an update to a game I wrote ages ago, and added my contact details: Such that the user can add your details to their Contacts Address book, aswell as call and email, and URL links to my Twitter and mobile Linkedin... Some handy code examples in here, that took me a while to make sense of..That ABRecordRef stuff is weird.

Just add #import
#import

Suss out the syntax for a ABRecordRef and then present the contact thing modally.. done.

#import "CreditsViewController.h"
#import "WebViewController.h"

@interface CreditsViewController()

- (void) sendMail;

@end

@implementation CreditsViewController

// The designated initializer. Override if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad.

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization.
self.navigationItem.title = @"Credits";
}
return self;
}


/*
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
}
*/

/*
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations.
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
*/

- (void) viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
}

- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];

// Release any cached data, images, etc. that aren't in use.
}

- (void)viewDidUnload {
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}


- (void)dealloc {
[navigationPicker release];
[super dealloc];
}

#pragma mark -
#pragma mark IBAction

- (IBAction) btnClickContact : (id) sender{
UIAlertView *alertBox = [[UIAlertView alloc] initWithTitle: @""
message:@""
delegate:self
cancelButtonTitle:@"Cancel"
otherButtonTitles:@"Call", @"Email", @"Add to Contacts", @"Linkedin", @"Twitter", nil ];
[alertBox show];
[alertBox release];
}

#pragma mark -
#pragma mark UIAlertViewDelegate

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{

switch (buttonIndex) {
case 0:

break;
case 1:
[[UIApplication sharedApplication] openURL:
[NSURL URLWithString:@"tel:+61413393190"]];
break;
case 2:
[self sendMail];
break;
case 3:
[self addNewContact];

break;
case 4:
NSLog(@"");
WebViewController *aWebView = [[WebViewController alloc] initWithNibName: nil
bundle: nil
webURL: @"http://m.linkedin.com/members/48410032/profile"
webTitle: @"LinkedIn"];
[self.navigationController pushViewController: aWebView animated:YES];
[aWebView release];
break;
case 5:
NSLog(@"");
WebViewController *webViewTwitter = [[WebViewController alloc] initWithNibName: nil
bundle: nil
webURL: @"http://www.twitter.com/daveNZdeveloper"
webTitle: @"Twitter"];
[self.navigationController pushViewController: webViewTwitter
animated: YES];
[webViewTwitter release];
break;
default:
break;
}
}

- (void)alertViewCancel:(UIAlertView *)alertView{
NSLog(@"cancel");
}

#pragma mark -
#pragma mark Private Methods

- (void) sendMail{

NSString * to_s = @"dvd.nzl@gmail.com" ;
NSString * subject_s = @"Contact from using Green Block Game" ;
NSString * body_s = @"" ;

NSString *mailString = [NSString stringWithFormat:@"mailto:?to=%@&subject=%@&body=%@",
[to_s stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding],
[subject_s stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding],
[body_s stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding]];

[[UIApplication sharedApplication] openURL:[NSURL URLWithString:mailString]];
}

- (void)newPersonViewController:(ABNewPersonViewController *)newPersonViewController
didCompleteWithNewPerson:(ABRecordRef)person{
NSLog(@"Complete");
[navigationPicker dismissModalViewControllerAnimated: YES];
}

- (void) addNewContact{

ABRecordRef aRecord = ABPersonCreate();

CFErrorRef anError = NULL;

ABRecordSetValue(aRecord, kABPersonFirstNameProperty, CFSTR("Dave"), &anError);
ABRecordSetValue(aRecord, kABPersonLastNameProperty, CFSTR("van Dugteren"), &anError);
ABRecordSetValue(aRecord, kABPersonOrganizationProperty, CFSTR("Senior iPhone Developer"), &anError);

ABMutableMultiValueRef multiHome = ABMultiValueCreateMutable(kABMultiDictionaryPropertyType);

NSMutableDictionary *addressDictionary = [[NSMutableDictionary alloc] init];

[addressDictionary setObject:@"Sydney" forKey:(NSString *)kABPersonAddressCityKey];

[addressDictionary setObject:@"NSW" forKey:(NSString *)kABPersonAddressStateKey];

[addressDictionary setObject:@"2007" forKey:(NSString *)kABPersonAddressZIPKey];

[addressDictionary setObject:@"Australia" forKey:(NSString *)kABPersonAddressCountryCodeKey];

[addressDictionary setObject:@"Australia" forKey:(NSString *)kABPersonAddressCountryKey];

ABMultiValueAddValueAndLabel(multiHome, addressDictionary, kABHomeLabel, NULL);

ABRecordSetValue(aRecord, kABPersonAddressProperty, multiHome, &anError);

ABMutableMultiValueRef homePage = ABMultiValueCreateMutable(kABMultiStringPropertyType);
ABMultiValueAddValueAndLabel(homePage, CFSTR("www.kiappsta.com"), kABPersonHomePageLabel, NULL);
ABMultiValueAddValueAndLabel(homePage, CFSTR("www.davevandugteren.net"), kABPersonHomePageLabel, NULL);
ABMultiValueAddValueAndLabel(homePage, CFSTR("http://www.linkedin.com/in/davidvandugteren"), kABPersonHomePageLabel, NULL);
ABRecordSetValue(aRecord, kABPersonURLProperty, homePage, NULL);
CFRelease(homePage);

ABMutableMultiValueRef phoneNumber = ABMultiValueCreateMutable(kABMultiStringPropertyType);
ABMultiValueAddValueAndLabel(phoneNumber, CFSTR("+6104 133 93190"), kABPersonPhoneMobileLabel, NULL);
ABMultiValueAddValueAndLabel(phoneNumber, CFSTR("+6104 133 93190"), kABPersonPhoneIPhoneLabel, NULL);
ABRecordSetValue(aRecord, kABPersonPhoneProperty, phoneNumber, NULL);
CFRelease(phoneNumber);

ABMutableMultiValueRef multiEmail = ABMultiValueCreateMutable(kABMultiStringPropertyType);
ABMultiValueAddValueAndLabel(multiEmail, CFSTR("dvd.nzl@gmail.com"), kABWorkLabel, NULL);
ABRecordSetValue(aRecord, kABPersonEmailProperty, multiEmail, &anError);
CFRelease(multiEmail);

UIImageView *imageView = [[UIImageView alloc] initWithImage: [UIImage imageNamed: @"me.png"]];


ABPersonSetImageData(aRecord, (CFDataRef) (UIImageJPEGRepresentation([imageView image], 1.0f)), &anError);

if (anError != NULL) {
NSLog(@"error while creating..");
}

ABNewPersonViewController *picker = [[ABNewPersonViewController alloc] init];
picker.newPersonViewDelegate = self;

picker.displayedPerson = aRecord;

if (navigationPicker == nil)
navigationPicker = [[UINavigationController alloc] initWithRootViewController:picker];

[self presentModalViewController:navigationPicker animated:YES];

[picker release];
}


And the interface:


#import
#import
#import

@interface CreditsViewController : UIViewController {
UIWebView *webView;
UINavigationController *navigationPicker;
}

- (IBAction) btnClickContact : (id) sender;
- (void) addNewContact;
@end