Objective C Builder Pattern play

by

So I read two posts on the Builder Pattern from Java today that got linked to off of Twitter.

This by Klaas Pieter which referenced this one by Uli Kusterer.  Both good articles.

I haven’t done Java much at all for the last 10 years and so am not used to this pattern, but I thought about how I might do something similar and while I’m not sure my first thought is any better, it seems to meet the requirements and/or benefits noted by the two blog posts in question and the tweet discussion referencing them.

Instead of:

 Pizza pizza = new Pizza.Builder()
     .size(12)
     .pepperoni(true)
     .mushrooms(true)
     .build();

(from Uli’s post), or

  Image* theImage =
    (new Image.Builder)->SetWidth(100)
    ->SetHeight(80)->SetDepth(8)->Build();

(from Klaas’ post)

I tried something like:

Foo * aFoo = [Foo fooWithData: @{
     @"width" : @21, @"height" : @22 }];

Here’s one way to implement that – there are multiple, clearly
(pardon the formatting, trying to fit into our narrow blog them is annoying):

// .h file
#import <Foundation/Foundation.h>

@interface Foo : NSObject

+ (instancetype) fooWithData: (NSDictionary*) initParams;

@end

// .m file
@interface Foo ()
@property (nonatomic, assign) long width;
@property (nonatomic, assign) long height;
@property (nonatomic, assign) long depth;
@end

@implementation Foo

+ (instancetype) fooWithData: (NSDictionary*) initParams
{
  Foo *result = [[Foo alloc] init];

  // If one uses setValue:forKey: in a loop as
  // as Uli notes then we can't support integral
  // properties like ints.
  // Also doing it explicitly as below
  // means we don't have to have the same name
  // for our private internal properties as we
  // document for our public parameters because
  // we can map them here.
  // e.g.,
  //  result.imageWidth = [initParams[@"width"] longValue];

  if ( initParams[@"width"] != nil )
    result.width = [initParams[@"width"] longValue];

  if ( initParams[@"height"] != nil )
    result.height = [initParams[@"height"] longValue];

  if ( initParams[@"depth"] != nil )
    result.depth = [initParams[@"depth"] longValue];

  return result;
}

- (instancetype) init
{
  self = [super init];
  if ( self )
  {
    // init with defaults
    _width = 10;
    _height = 10;
    _depth = 1;
  }
  return self;
}

- (NSString*) description
{
  return [NSString stringWithFormat:
           @"Foo: (%p), width: %ld, height: %ld, depth: %ld",
           self, self.width, self.height, self.depth];
}
@end

// main.m
#import <Foundation/Foundation.h>

int main(int argc, char *argv[]) {
  @autoreleasepool {
      // note no depth specified, taking default of 1
    Foo * aFoo =
      [Foo fooWithData: @{ @"width" : @21, @"height" : @22 }];
    NSLog(@"aFoo: %@", aFoo );
  }
}

Paste the above into CodeRunner and run it and you get:

2014-04-12 16:22:38.918 Untitled 7[71144:707]
aFoo: Foo: (0x7f8842c0af00), width: 21, height: 22, depth: 1

In the above I only typed in the class method as taking the full parameter list but normally I would have made the init method take the same parameter and do the initialization & mapping there. Something I’d likely add if I was going to actually use this, which I’m not likely to. Why not? Because to use the construct above you’d have to document the parameter keys available to use in the dictionary and what type of value each takes. This is where ObjectiveC’s named parameters comes in handy: they are self documenting.

Anyway, an interesting procrastination from what I was supposed to be doing this Saturday afternoon… 🙂

Advertisements

One Response to “Objective C Builder Pattern play”

  1. gschandler Says:

    The comment in your class constructor method isn’t quite correct.

    KVC handles integral properties just fine. Try it!

    Given that, you could use a loop based on [initParams allKeys], a subset of keys created locally (i.e. NSArray *keys = @[@”width”,…etc];, *or*, better yet, [result setValuesForKeysWithDictionary:initParams];

    In each case you need to handle invalid keys, but that’s trivial.

    Finally, defining the supported keys in the header, would be sufficient for documenting as an API to the method (i.e. extern NSString * const kFooWidthKey; // NSNumber ).

    But, alas, if only setters would return the object, instead of being a – (void), you could chain your setter calls, much like Java.

    Foo *foo = [[[[[Foo new]
    setWidth:10]
    setHeight:100]
    setDepth:42]
    build];

    That would be kind of neat, albeit, a lot of brackets.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


%d bloggers like this: