
-----------------------------------
Cervantes
Tue Jan 10, 2006 10:24 pm

Classes Part II - Object Interaction
-----------------------------------
Classes Part II:
Interaction of Objects

At the end of this tutorial, you will find an attached .zip.  This file contains pretty much all the code we are using in this tutorial, as well as the image that outlines the PopMachine's input/output.

An Introduction

So far, we've created programs that have a single class.  (

A bit of Theory

We want our Pop Machine to accept Money objects and dispense Pop objects.  We will give the Pop Machine a purchase method, and it will take two parameters: 1) a Money object and 2) a Pop object.  It will accept the Money object, make change if necessary, and dispense the Pop object if enough Money has been given.

Allow me to highlight an idea from the last paragraph.  A method in the PopMachine class will accept objects as parameters.  How do we do that?  Recall from the last section what our objects were represented as: pointers to a certain class, in this case the Money or Pop class.  So, our parameters will have the variable type

 : pointer to Money

or

 : pointer to Pop

Let's write some abstract code to see if this will work.

class Foo
    export message, set_message

    var message := ""
    proc set_message (value : string)
        message := value
    end set_message

end Foo

class Bar
    export message_from_Foo

    fcn message_from_Foo (obj : pointer to Foo) : string
        result Foo (obj).message
    end message_from_Foo

end Bar


var f : ^Foo
new Foo, f
Foo (f).set_message ("Hello from the Foo class.")

var b : ^Bar
new Bar, b

put Bar (b).message_from_Foo (f)

When you run this code, you will get two warnings, since "Foo" appeared twice in the Bar class, but Foo was not imported.  We have to import Foo into the Bar class.  Let's do that.

class Foo
    export message, set_message

    var message := ""
    proc set_message (value : string)
        message := value
    end set_message

end Foo

class Bar
    import Foo
    export message_from_Foo

    fcn message_from_Foo (obj : pointer to Foo) : string
        result Foo (obj).message
    end message_from_Foo

end Bar


var f : ^Foo
new Foo, f
Foo (f).set_message ("Hello from the Foo class.")

var b : ^Bar
new Bar, b

put Bar (b).message_from_Foo (f)

Note that things like import and export always come at the beginning of the class (or module, or other structures), and that import always comes before export.

This may seem like an awfully long, complex way to put something to the screen, but this is just the beginning.  If you made a Button or TextField class, or any other class, as the exercise to the last section, you should have realized the power of object oriented programming by now.  It is neat, highly structured and organized, and repetition is easy--we can easily create many instances of the same class, and we therefore save ourselves coding.

With this initial example in mind, let's make our PopMachine, Money, and Pop classes.  There's really nothing new in it than in the above code, but it shows this in a more practical light, so it is worth examining.


The Pop Machine Outline

In case we're still not clear on how the Pop Machine will work, here is a graphical representation.  
http://www.compsci.ca/v2/download.php?id=3610
We have two inputs: The Money object that we drop into the coin/bill slot, and our pop request.  This is usually in the form of hitting a button, but in this example we will be more blunt: we will directly pass a Pop object (such as a Coke) into the purchase function of the PopMachine object.


The Pop Machine Code


class Money
    export set_money, amount

    var amount : real
    proc set_money (value : real)
        amount := value
    end set_money

end Money

class Pop
    export set_name, set_cost, name, cost

    var name : string
    var cost : real

    proc set_name (value : string)
        name := value
    end set_name

    proc set_cost (value : real)
        cost := value
    end set_cost

end Pop

class PopMachine
    import Money, Pop
    export purchase

    fcn make_change (amount, cost : real) : real
        result amount - cost
    end make_change

    fcn purchase (money_given : ^Money, pop_requested : ^Pop) : string
        var change := make_change (Money (money_given).amount, Pop (pop_requested).cost)
        var pop_name := Pop (pop_requested).name
        if change < 0 then
            result "You have not given enough money for a " + pop_name + ".  Please insert $" + realstr (abs (change), 0) + " more."
        elsif change = 0 then
            Money (money_given).set_money (0)
            result "Thank you for your purchase.  Enjoy your " + pop_name + "."
        else
            Money (money_given).set_money (Money (money_given).amount - Pop (pop_requested).cost)
            result "Thank you for your purchase.  Your change is $" + realstr (change, 0) + ".  Enjoy your " + pop_name + "."
        end if
    end purchase

end PopMachine

var caf_vendor : ^PopMachine
new PopMachine, caf_vendor

var cash : ^Money
new Money, cash
Money (cash).set_money (1.50)

var coke : ^Pop
new Pop, coke
Pop (coke).set_name ("Coke")
Pop (coke).set_cost (1.25)

put PopMachine (caf_vendor).purchase (cash, coke)
put "You now have $", Money (cash).amount, "."



The Pop Machine Options

There are a few ways the purchase method could work.  It could result (read: return) a Pop object, which seems logical.  But then there's the question of your change.  If spitting out a can of pop is an example of "resulting" an object, so too is spitting out your change.  Thus, if we want to "result" the Pop and Money, we'd need to "result" an array that contains two objects: the Pop and the Money.

Alternatively, we could define a procedure in the PopMachine class to drop the Pop object to the bottom "bin", and another procedure to drop the Money objects (the change) to the pick-up area.  The purchase procedure would call these two procedures (or perhaps only call one of them, if exact change was given; or perhaps call none of them, if not enough change was given).


Further Studies

In this section of the tutorial we have learned how objects may interact.  The key idea to remember is that to integrate a type of object into the framework of another object (ie. into that object's class), the variable type is pointer to ClassName, where ClassName is the name of the class of the object we wish to interact with our current object.  To do this, we have to import ClassName.

In 
