C. Keith Ray

C. Keith Ray writes about and develops software in multiple platforms and languages, including iOS® and Macintosh®.
Keith's Résumé (pdf)

Monday, December 8, 2014

Readable Code

Recently, I needed to change a scalar variable into a stack, as I modified my code to handle a new feature. Here's what I did.

First, I looked to see if the libraries I had access to contained a Stack class. (Apple's Foundation framework, etc.) I didn't find one. Nor did I find 'push' and 'pop' methods.

I could have changed the variable to an NSMutableArray and used these two methods instead of 'push' and 'pop':

- (void)addObject:(id)anObject;  // adds to the end of the array
- (void)removeLastObject;  // removes from the end of the array

Functionally, those two method would serve as 'push' and 'pop', but it would be easy for future-me, or another member of the developer team, to forget that the object is supposed to be used like a stack.

One option would be to add methods to the NSMutableArray class via a class category. That would allow my code to call 'push' or 'pop' but still doesn't clearly express my intent for this variable (which would still be declared as an NSMutableArray.

Since a basic stack is very easy to construct, I built one very quickly with just the API that I wanted. This is a common enough "demo" of Test-Driven-Development that I'm not going to reproduce it hear.

One thing did come up: I will sometimes want to display the contents of the entire stack, and I don't want to push/pop everything to do so. So I added another stack test and another method to my stack class.  Since I know the stack will be small (less than 30 objects), I return a copy the internal array storage, rather than returning an enumerator for the caller to access elements one-at-a-time.

-(void) testContents
    [stack push: @"a"];
    [stack push: @"b"];
    [stack push: @"c"];
    AssertEqual(3, [stack count]);
    NSArray *arr = [stack contents];
    AssertEqual(3, [arr count]); // calling contents doesn't pop.
    AssertObjectsEqual(@"a", arr[0]); // oldest
    AssertObjectsEqual(@"b", arr[1]);
    AssertObjectsEqual(@"c", arr[2]); // newest - top of stack


-(NSArray *) contents
    return [_storage copy];


Getting the data in this order (from the bottom of stack to the top of stack, is precisely what I want, since I be displaying the contents horizontally in the UI. 

But... I think 'contents' isn't clear enough. So I renamed the method:

-(NSArray *) contentsBottomUp
    return [_storage copy];


And, for symmetry, I added another method (and test) to return stack contents top-down instead of bottom-up. While not strictly needed for the current UI. I might want to display the stack contents vertically, and then I'd want the display the top first, and the bottom last.

-(NSArray *) contentsTopDown;

Since I didn't find a 'reverse' method in the Foundation framework either, I needed to implement a way to reverse the contents being returned from 'contentsTopDown'. While looking for examples of code using Apple's 'Fast Enumeration' implementation, I found this one-liner in NSHipster's blog:

-(NSArray *) contentsTopDown;
    return _storage.reverseObjectEnumerator.allObjects;

The end.

No comments:

Post a Comment