Computer Science Canada

Classes Part II - Object Interaction

Author:  Cervantes [ Tue Jan 10, 2006 10:24 pm ]
Post subject:  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. (Be sure to have read Classes Part I.) We have not had any room for interaction of objects, either because we only made one object (instance of our only class), or because there was no need for objects of the same class (our only class) to interact, such as the button objects that I hope you made.

    In this section, we will look at how objects may interact with each other. Consider a Pop Vending Machine, for example. It is an object that accepts money objects, processes your input (the buttons you push), and outputs a can of carbonated sugar. Let's write a Turing program to explore this idea, using the Pop Machine example.


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
    code:

     : pointer to Money

    or
    code:

     : pointer to Pop

    Let's write some abstract code to see if this will work.
    code:

    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.
    code:

    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.
    Posted Image, might have been reduced in size. Click Image to view fullscreen.
    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

    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 Part III, we will learn how to create a network of classes, using inheritance. We will also look at polymorphism. These are powerful concepts, and we shall use them to create the basis for working with the items of an RPG.


: