Gold Challenge - concise solution


#1

It took some thinking about but I came up with a concise solution to the Gold Challenge in chapter 2. The BNRContainer is a sub class of BNRItem and also contains an array of sub items where each object in the array may be a BNRItem or another BNRContainer (which itself may contain other BNRItems/BNRContainers ad infinitum). Any feedback is welcome.

The BNRItem class isn’t changed for the purposes of the challenge.

BNRContainer.h

#import "BNRItem.h"

@interface BNRContainer : BNRItem
{
    NSMutableArray *subItems;
}

- (void)addItem:(id) item;
@end

BNRContainer.m

#import "BNRContainer.h"

@implementation BNRContainer

- (id)initWithItemName:(NSString *)name valueInDollars:(int)value serialNumber:(NSString *)sNumber
{
    self = [super initWithItemName:name valueInDollars:value serialNumber:sNumber];
    subItems = [[NSMutableArray alloc] init];   
    return  self;
}

- (void)addItem:(id)item
{
    [subItems addObject:item];
}

- (int)valueInDollars
{
    int totalValue = valueInDollars;
    for (id item in subItems)
    {
        totalValue = totalValue + [item valueInDollars];
    }
    return totalValue;
}

- (NSString *)description
{
    return [[NSString alloc] initWithFormat:@"Item name: %@  Value: %d  Sub items: %@", itemName, [self valueInDollars], subItems];    
}
@end

main.m (within main function) - example code to demonstrate new BNR Container class

[code]BNRContainer *container1 = [[BNRContainer alloc] initWithItemName:@“Container1” valueInDollars:10 serialNumber:@“abcde”];

BNRItem *item1 = [[BNRItem alloc] initWithItemName:@“Item1” valueInDollars:5 serialNumber:@“a”];
BNRItem *item2 = [[BNRItem alloc] initWithItemName:@“Item2” valueInDollars:10 serialNumber:@“b”];
BNRItem *item3 = [[BNRItem alloc] initWithItemName:@“Item3” valueInDollars:10 serialNumber:@“c”];

BNRContainer *container2 = [[BNRContainer alloc] initWithItemName:@“Container2” valueInDollars:15 serialNumber:@“fghij”];
BNRItem *item4 = [[BNRItem alloc] initWithItemName:@“Item4” valueInDollars:10 serialNumber:@“d”];

[container2 addItem:item4];
[container1 addItem:container2];

[container1 addItem:item1];
[container1 addItem:item2];
[container1 addItem:item3];

NSLog(@"%@", [container1 description]);
[/code]


#2

Looks good but next time, please post your code between the code tags in order to make it easier for others to read.

Also, the BNRContainer interface can be simplified by hiding the details (the declaration of instance variables), by moving them from the interface file to the implementation file as follows.

BNRContainer.h

#import "BNRItem.h"

@interface BNRContainer : BNRItem
- (void)addItem:(id) item;
@end

BNRContainer.m

#import "BNRContainer.h"

// Declare our instance variables
@interface BNRContainer ()
{
   NSMutableArray *subItems;
}

@end

@implementation BNRContainer
...
@end

#3

Ah OK I’ve fixed that now. I’m new to Objective C and I don’t understand why you would have an @interface in the implementation file? I understand that it is good to hide the array but I thought that the .h file was for the @interface and .m file for the @implementation.


#4

Objective-C allows the presence of a second @interface@end in the implementation file. We can take advantage of this to hide details that should not really be part of the public interface of the class.


#5

Thank you so much for this post. I was having difficulty using an the NSMutableArray in Container to store items. Unless I did the addobject in main I had real scope issues. I was trying to use random to generate the item and add it to the NSMutableArray within my Container. Then my valueTotals enumeration was failing! I wasn’t using id and instead was using a BNRItem or BNRContainer!

I thought it shouldn’t have been taking me days! Many thanks!


#6

I am following your solution.
What puzzles me is the architecture of this challenge. Why make a container a subclass of the item it contains? To me, this should be a composition. I don’t get it. Why use a subclass relation?