"Hey there, Object. What are you?"


#1

I have a quirky question. First, here is a code snippet of what I did on challenge 18:

[code] NSMutableArray *stocksArray = [[NSMutableArray alloc] init];

    StockHolding *stock1 = [[StockHolding alloc] init];
    [stock1 setNumberOfShares:50];
    [stock1 setPurchaseSharePrice:1.90];
    [stock1 setCurrentSharePrice:2.90];
. . . 
    [stocksArray addObject:stock1];

. . .
//Create the foreign stocks.
ForeignStockHoldings *stock4 = [[ForeignStockHoldings alloc] init];
[stock4 setNumberOfShares:1000];
[stock4 setPurchaseSharePrice:1.00];
[stock4 setCurrentSharePrice:2.00];
[stock4 setConversionRate:2.00];
. . .
//Put foreign stocks in the array
[stocksArray addObject:stock4];
[stocksArray addObject:stock5];

    //fast enumeration through all stocks in the array
    for (ForeignStockHoldings *s in stocksArray) {
        float profitOrLoss = [s valueInDollars] - [s costInDollars];
        NSLog(@"You purchased %d shares of this stock at a per share cost of $%.2f. The current per share price is $%.2f, giving you a current holdings value of $%.2f.", [s numberOfShares], [s purchaseSharePrice], [s currentSharePrice], [s valueInDollars]);
        if (profitOrLoss > 0) {
            NSLog(@"Congratulations!  You made $%.2f", profitOrLoss);
        } else if (profitOrLoss < 0) {
            profitOrLoss = profitOrLoss * -1;
            NSLog(@"Bad investment.  You lost $%.2f", profitOrLoss);          
        } else NSLog(@"No profit or loss on that stock.");
    }[/code]

This doesn’t work perfectly. When the enumeration hits a foreign stock, it prints out the original purchase price without any conversion factor applied to make that purchase price “dollars”. This raises two questions in my mind.

First, is it proper to enumerate through an array of mixed items (foreign stocks and non-foreign stocks) using the ForeignStockHoldings subclass identifier?
– for (ForeignStockHoldings *s in stocksArray) { . . . .
That just feels wrong because the array is filled with objects that are both regular stocks and foreign stocks. I tried to just put “NSObject” in place of ForeignStockHoldings, but the compiler was not happy with that.

Second, how would one ask the object that you are looking at, “What are you, a foreign or non-foreign stock?” If I could ask the object that question, then I could alter the enumeration to adjust the purchase price and current price down to US dollars for those foreign holdings.

Sure, the simple solution would be to just put a conversion factor variable in the regular “StockHolding” (purchased in US dollars) and just set the conversion rate to “1”, but what fun would that be. :smiley:


#2

You can always treat a child object like its parent, but you should never(*) try to treat a parent like a child. So you should definitely perform your enumeration against StockHoldings, not ForeignStockHoldings.

Now, onto the question of how to apply the conversion factor. You’re touching on the very premise of OO encapsulation, so you’re asking an excellent question.

It’s certainly possible to write your code to ask each object as you iterate through the list whether it’s foreign or not, and apply logic within your loop, but that’s almost always the wrong approach.

Why? Because one key goal to writing software is to limit the impact of changes. If you define a TaxFreeStockHoldings class in the future, you don’t want to look for all StockHoldings-related code and add special handling for that as well.

Instead, you want the operations that StockHoldings (the parent class) defines to handle the changes transparently to your loop and any future code.

So instead of in your loop performing this pseudocode: “if s is Foreign, multiply its valueInDollars by conversionRate”…

…you want to redefine valueInDollars in your ForeignStockHoldings class to apply its conversionRate on your behalf, transparently to the loop.

So in summary, you need to:
1 Change your loop variable to be a pointer to StockHoldings instead of ForeignStockHoldings
2 In ForeignStockHoldings, define new valueInDollars and costInDollars methods that handle the conversion factor

That should be enough to make everything Just Work.

  • Footnote: it’s possible there are exceptions to my statement above about never treating a parent like a child, but I’m not aware of any.

#3

Thanks. I get it. I did have the foreign stock class methods applying the conversion factors when appropriate. That much worked. The problem I was having was when I tried to get more detailed and state the value paid per stock, which is a float value directly from the object to which no conversion factor has been applied.

I found an NSObject class method that seems to do what I wanted to do:

[code] for (StockHolding *s in stocksArray) {

        //This is an NSObject class method that asks whether the object is of a given class.  The return is a BOOL.
       BOOL check = [s isMemberOfClass:[ForeignStockHoldings class]];
        
        //Use the BOOL to find foreign stocks in the array.
        if (check == YES) {
            
            NSLog(@"This was a foreign stock purchase.  The total cost of the purchase in US dollars was $%.2f, and the current value is $%.2f.", [s costInDollars], [s valueInDollars]);
       
        // If the BOOL was NO, then you don't have a foreign stock, so continue.
        } else { 
            float profitOrLoss = [s valueInDollars] - [s costInDollars];
            NSLog(@"You purchased %d shares of this stock at a per share cost of $%.2f. The current per share price is $%.2f, giving you a current holdings value of $%.2f.", [s numberOfShares], [s purchaseSharePrice], [s currentSharePrice], [s valueInDollars]);
            if (profitOrLoss > 0) {
                NSLog(@"Congratulations!  You made $%.2f", profitOrLoss);
            } else if (profitOrLoss < 0) {
                profitOrLoss = profitOrLoss * -1;
                NSLog(@"Bad investment.  You lost $%.2f", profitOrLoss);          
            } else NSLog(@"No profit or loss on that stock.");[/code]

#4

Gotcha. I’d still recommend handling it within the class instead of outside it.

A few options:
[ul]
[li]Add an instance variable to represent the currency symbol[/li]
[li]Add an instance variable to represent the currency label[/li]
[li]Override description and rely on that to display all the details you want[/li]
[li]Add purchaseSharePriceAsString and currentSharePriceAsString methods (would need to be overridden for ForeignStockHoldings or combined with the earlier options)[/li][/ul]

In fact, you could do any combination of these.

Below are a couple of methods I added to StockHolding to experiment with this:

- (NSString *)purchasePriceAsString
{
    return [NSString stringWithFormat:@"%@%0.2f",
            [self currencySymbol], [self purchasePrice]];
}

- (NSString *)currentPriceAsString
{
    return [NSString stringWithFormat:@"%@%0.2f",
            [self currencySymbol], [self currentPrice]];
}

Then, I updated my init methods in StockHoldings to call setCurrencySymbol:@"$", and when creating foreign holdings, you could call setCurrencySymbol from outside the class.

At this point, you should be able to write a loop that handles all holdings identically, regardless of the real class.


#5

Cooooool. :sunglasses: