Doing Object-Oriented
Programming in SiMPLE

[Note: This webpage is intended for users of SiMPLE.]

Click Here to Read About SiMPLE

Do you think that you need an object-oriented language in order to program in an object-oriented style? Well, you're wrong. It seems to be a common myth that you need an object-oriented language (such as C++) to implement an object-oriented design. But you can also benefit from the concepts of objects in a procedural language such as SiMPLE.

Object-Oriented Programming (OOP) in itself does NOT allow programs to do things that they could not do before. OOP is more of a program organizational philosophy rather than a set of new external solutions or operations. (For further discussion, see: OOP Criticism.)

What Is an Object?

You can look around you and see many examples of tangible objects: a computer, a television set, a bicycle, etc. These objects all have two characteristics in common: They all have certain physical properties (or "states" in which they currently exist), and they all have a given set of "functionalities" that they provide (i.e., things that they will let you do). For example, the current state of a TV might be: "On" (or "Off"), tuned to channel 3 (or tuned to some other channel), sound muted (or sound audible), etc. And the functionalities that a TV provides might include are: a channel selector (for changing channels), a volume control (for adjusting the sound level), a brightness control (for changing the brightness of the picture), etc.

Software objects are intangible, but they are modeled after tangible objects in that they too have "state" and "functionality". A software object holds its state information in one or more internal data containers (sometimes referred to as "properties" in the professional jargon). And a software object implements its functionality with internally contained tasks (or "methods", as they are referred to in the OOP community).

Everything that a software object knows (its state) and can do (its functionalities) is expressed by the data and tasks that exist inside the object. Because all of the object's data is contained inside the object, it can be protected from accidental corruption or misuse by users of the object. Packaging an object's data inside this kind of "protective shell" is called encapsulation. Because objects encapsulate data and functionality, the user of an object can view the object as a black box that merely does whatever it's supposed to do. The user doesn't need to know how the black box does its job.

As an almost trivial illustration of the advantages of encapsulating functionality, suppose that you want to write a game that involves the use of rockets. Somewhere in the game, the player will launch a series of rockets (one at a time) from the bottom of the screen. When the y-coordinate of a rocket reaches 200, it should explode.

The "traditional" (i.e., non-object-oriented) approach to this problem might be to create two separate tasks - rocket (x, y) and explode (x, y). The rocket task would draw the image of a rocket at a prescribed location, and the explode task would create an explosion at a prescribed location. Then, in the appropriate section of the game's source listing, you would merely invoke the rocket task inside a loop. And when the loop terminates, you would then invoke the explode task:

         - - -
Do y = 480 To 200
   rocket (320, y)
         - - -
   (other statements)
         - - -
Loop y
explode (320, 200)
         - - -
Ok, now you've written that section of the game and everything works just fine. But at some future date, you decide to change the design of the game slightly. Instead of allowing each rocket to reach only a fixed y-coordinate on the screen, you now want them to reach random altitudes before exploding. So you go back and find the Do-Loop that you made, and you change the 200 to a random number. But you forget to also change the y-coordinate that you've given to the explode task!  Now each rocket flies up to a random altitude ok, but all the explosions occur in the wrong place!

If you had created a rocket object with a built-in explosion task (instead of creating a rocket task and a separate explosion task), you could have avoided this kind of problem. Since the rocket object would always know its own location on the screen, you could have simply commanded it to "self-destruct" without you having to specify the coordinates of where the explosion should occur.

How Do I Invoke A SiMPLE Object?

Let's suppose that you've created a SiMPLE object named radio (we'll show you how to create an object shortly), and now you want to invoke its setVolume task.

In other programming languages (such as C++, JavaScript, etc.), a standard "something-dot-something" convention is used for invoking an object's methods. For example, if radio had been a C++ object, you would have written:


in your C++ program. But SiMPLE doesn't allow periods ( . ) to be used in the fashion that C++ uses them. So we use an underscore character ( _ ) instead. Therefore, to invoke the radio object's setVolume task, you would write:


What Is A SiMPLE Object?

(Tasks & Data)
A SiMPLE Object

A SiMPLE object is a collection of interrelated tasks (and their associated Common statements) that have been written with an object-oriented goal in mind. In other words, a SiMPLE object is merely a task module that contains:

  • A constructor that initializes the object.

  • A destructor that removes the object from further use. [No, a destructor doesn't "explode" the object! ]

  • A set of public tasks that are callable from outside the object (and which provide limited outside access to the object's data).

  • A set of private tasks which are callable only from inside the object. [Requires Pro-SiMPLE to be truly private]

  • A set of private data (Common statements) which cannot be accessed from outside the object. (All data in a SiMPLE object is intrinsically private.)

The syntax and structure of a SiMPLE object is modelled after the implementation of objects in C++. However there are many major differences. For one thing, objects in C++ are created by using a special kind of "template" (called a class). A class, in effect, allows you to create a new data type (i.e., an object) that has specific properties. And because C++ can work with classes (and subclasses), the kinds objects that it can create have more features (such as inheritance, etc.) than do SiMPLE objects. But C++ programmers may notice many parallels between SiMPLE objects and C++ objects.

How Do I Create A SiMPLE Object?

Perhaps the best way to illustrate how to create a SiMPLE object is to just go ahead and make one. So let's create a smiley object that simply lets you draw a colored "happy face" on the screen at a prescribed location:

            * * * * * * * * * * * * * * *
            *                           *
            *   SiMPLE Object: smiley   *
            *                           *
            * * * * * * * * * * * * * * *

Task smiley_create (Int id)
   Common Int active[16]={0}
   Do k=1,15
      If active[k] Continue
      id=k; Return
   Output ("Error: More than 15 object instances")
End constructor

Task smiley_destroy (Int id)
   Common Int active[16]
   active[id]=0; id=0
End destructor

//-----------------(Public tasks)-----------------//

Task smiley_draw (Int id, Int x, Int y, Int color)
   Common Int xx[16], yy[16], zz[16]
   verify (id) @
   xx[id]=x; yy[id]=y; zz[id]=color
   drawer (x, y, color) @
End draw

Task smiley_getInfo (Int id, Int x, Int y, Int color)
   Common Int xx[16], yy[16], zz[16]
   verify (id) @
   x=xx[id]; y=yy[id]; color=zz[id]
End getInfo

//-----------------(Private tasks)-----------------//

Task verify (Int id) *
   Common Int active[16]
   If active[id] Return
   err="Error: Instance "+itot(id)+" does not exist"
   Output (err)
   Pause; Quit
End verify

Task drawer (Int x, Int y, Int z) *
   line color (z)
   circle (x, y, 16)
   ellipse (x-4, y-3, 2, 4)
   ellipse (x+4, y-3, 2, 4)
   line (x-5, y+10, x+5, y+10)
   line (x-5, y+10, x-10, y+5)
   line (x+5, y+10, x+10, y+5)
End drawer

The first task, smiley_create, is the object's constructor. When you call it, it returns an instance identifier called id. (An instance identifier allows your program to have more than one copy of the object. Our particular smiley object allows a maximum of 15 copies at any one time.) You will use id as a calling parameter when you invoke the other public tasks in the object.

The second task, smiley_destroy, is the object's destructor. When you call it, it deletes the instance of the object specified by id.

The third task, smiley_draw, draws a colored "happy face" at the specified x,y location on the screen.

The fourth task, smiley_getInfo, allows you to retrieve the location and color of the specified "happy face".

The remaining tasks in the object are private and are not meant to be invoked by the user. [And if you compile the object and put it into a library, it really is impossible for users to access those private tasks ... even if they were to know their names and how to use them! The tasks are truly private.]

To illustrate how to use our smiley object, let's write a program that displays three "smiley faces", each in a different color. We will then destroy one of them, and create a new "smiley face" which we will display somewhere else on the screen:

  Int id1, id2, id3, id4
  Int x, y, color

//Let's create 3 instances of the smiley object...

  smiley_create (id1) @
  smiley_create (id2) @
  smiley_create (id3) @

//and display each one in a different color:

  smiley_draw (id1, 270, 240, 4) @
  smiley_draw (id2, 320, 240, 2) @
  smiley_draw (id3, 370, 240, 1) @

//What is the location of the second smiley?

  smiley_getInfo (id2, x, y, color) @
  Display "The second smiley is at: ",x,,y

//Let's destroy the second smiley...

  smiley_draw (id2, x, y, 0) @
  smiley_destroy (id2) @

//and make a new yellow one somewhere else:

  smiley_create (id4) @
  smiley_draw (id4, 320, 320, 14) @

  [ Webmaster | FAQ's | Home Page | Contact Us ]