Smart programmers have always found ways to reuse data structures and functions. Reusing them is a good idea-they are already tested, they behave in understood ways, and they often represent data or actions that many programs need.
However, once you move a data structure from one program to another, you discover that you need some functions to manage it. Or, when you move a function, you discover the need for a data structure for it to manipulate. While traditional programming languages don't provide much support for this sort of reuse, good programmers have still done it.
You can change the contents of the object's data structure using the functions that come with the object. These functions don't need a separate data structure. You can think of the object as a component.
Combining the two basic entities-data structures and functions-into objects makes it easier to move them to other programs and reuse them. Data structures and functions that represent the information and tasks of a particular enterprise can be saved and shared, making programming tasks quicker to complete and easier to maintain.
An object controls access to its data by making data inaccessible from outside-you can only get and set its values by using functions for getting and setting that are provided by the object. The correct object-oriented programming term is encapsulation. Encapsulation allows an object to provide quality control for its own data.
In object-oriented programming, such a template is called a class. Every object is built from instructions that are associated with its class. Objects of the same type are instances of the same class. Every object is an instance of any classes that it belongs to or inherits from.
Figure 1-1: Storing data for dog instances
Dog
. Properties that are associated with a particular dog are called instance variables. You might also want to define properties for your program that are associated with all dogs-for example, you might want to keep track of the population of dogs. Properties that are associated with the Dog
class in general are called class variables. Creating Objects and Classes
This code sample creates a class called
Dog
and an instance of Dog
called nikki
. Enter the first eight lines of code below, beginning with the word class
. This creates the Dog
class. The Dog
class can then be used as a template for creating Dog
objects. Each Dog
object will have all the properties, such as owner
and breed
, that are defined in the template. The Dog
class also defines three instance methods-bark
, fetch
, and sniff
-just enough to give you the general idea.--
-- this code example, and others throughout this volume, are on
-- the CD-ROM with the ScriptX Language and Class Library
--
class Dog()
instance variables
name, owner, breed, age, length, weight, sex, temperament
instance methods
method bark self -> print "makes a lot of noise"
method fetch self -> print "fetches a stick"
method sniff self -> print "sticks nose into things"
end
Dog
Dog
) indicates that the ScriptX bytecode compiler has compiled the Dog
class. That means you can now create Dog
objects.Dog
object, which is assigned to the variable nikki
. In this example, the value of four of the instance variables for nikki
are set at initialization. Since the other four are ignored, their values will be undefined
until they are explicitly set.
object nikki (Dog)
settings name:"Nikki", owner:#("Jocelyn","Ken"), sex:@female,
breed:"English Springer Spaniel", temperament:@nervous
end
Dog@0xef8908
name
instance variable contains a StringConstant
object "Nikki"
. The dog's owner
instance variable contains an Array
object. Each item in that array is also a StringConstant
object. The sex
and temperament
instance variables contain NameClass
objects, also known as name literals. Dog@0xef8908
) tells you the Dog
object exists, and it gives the memory address. Of course, this memory address will differ with each computing session, and on each platform. (Unlike the real Nikki, this dog stays put at 0xef8908
until you no longer want her around!)nikki
. Here's what you type into the Listener to get access to the owner
instance variables defined by the object nikki
.nikki.owner
#("Jocelyn", "Ken")
Dog
: bark
, fetch
, and sniff
. In object-oriented programming you can think of calling a method as sending a message to an object, telling that object to perform some operation.
fetch nikki
"fetches a stick"
OK
OK
) is the return value of the fetch
method. The print
function in the definition of the fetch
method prints "fetches a stick"
to the Listener window. This string is not itself a return value-printing a string is just an operation that fetch
carries out. Throughout this manual, an arrow ( ) indicates output from ScriptX to your Listener or debugger window, including both printed messages and return values. Output from ScriptX is also indented, to indicate that you do not type it in.
Dog
class, from which usable Dog
objects can be created. Each Dog
from this template has the same properties and the same general behavior, as described by its methods.Dog
objects share the same behavior. This is because a dog's methods have access to the object's own data. You could write a fetch
method such that a Dog
object with good temperament fetches faster than a Dog
object with bad temperament. Beyond Traditional Programming
Defining by Difference
Dog
. For some programs, that might be adequate. But what if you want to create a program that really models all the different ways that dogs behave? Dog
class has general properties and behavior for all dogs. Using inheritance and specialization, you can specialize that Dog
class to create more specialized classes that have particular characteristics and behavior. For example, you could divide dogs into various categories like hunting dogs and lap dogs as shown in Figure 1-2. There is no right way to categorize dogs; how you set up the dog hierarchy should depend on how you want to use them. Think of all the ways to divide up the world of dogs!
Figure 1-2: Creating inheritance hierarchies of dogs
bark
, fetch
, and sniff
-the advantage is that each dog can implement them in its own way. For example, each specification of the Dog
class can have its own implementation of bark
. The following example shows how to specialize the Dog
class by creating two new subclasses: HuntingHound
and LapDog
. In object-oriented programming terminology, Dog
is a superclass of HuntingHound
and LapDog
. HuntingHound
and LapDog
are subclasses of Dog
. Notice the specialization of the bark
generic function on the HuntingHound
and LapDog
classes. class HuntingHound (Dog)
instance methods
method bark self -> print "wooof, wooof"
end
HuntingHound
class LapDog (Dog)
instance methods
method bark self -> print "yip, yip, yip, yip, yip"
end
LapDog
fetch
and sniff
methods, since they are not specialized. The bark
method, although it was already defined by the Dog
class, has been redefined. In object-oriented programming terminology, to specialize behavior in this manner is to override a method.class Shihtzu (LapDog)
instance methods
method bark self -> (
nextMethod self
print "jumps up and down"
)
end
Shihtzu
object tyler (Shihtzu) settings name:"Tyler" end
Shihtzu@0xefcf88
bark tyler
"yip, yip, yip, yip, yip"
"jumps up and down"
OK
Note - Since you are used to getting a return value in the Listener window,
from now on, return values are shown only where required by the discussion.
nextMethod self
causes any instances of Shihtzu
to call the bark
method defined by the next superclass that implements it. In this case, the next implementing superclass is LapDog
. Each instance of Shihtzu
first calls the more general bark
method for all instances of LapDog
, and then continues with the specialized version for a Shihtzu
object. Protocols
Dog
class defines a protocol in the bark
, fetch
, and sniff
methods. These three methods are implemented for every instance of Dog
, making them the Dog
protocol. A protocol is a set of generic functions that is defined for every class in some branch of a class hierarchy. Defining New Behavior
BassetHound
class that implements the drool
method, a method that other dogs, with less active salivary glands and more active facial muscles, might not share. In the dog world example, drool behavior is peculiar to basset hounds.class BassetHound (HuntingHound)
instance methods
method drool self -> print "slobbers all over everything"
end
-- now create an instance of BassetHound
object vaps (BassetHound)
settings name:"Vaps", owner:"The Crosbys"
end
-- call the drool generic function on vaps
drool vaps
"slobbers all over everything"
Note the following distinction, which is important in object-oriented programming. In the previous example, it isn't the BassetHound
class that drools. The BassetHound
class is a template used to define the properties and behavior of basset hounds. To be more precise, the BassetHound
class is a template that defines the ways in which basset hounds differ from hunting dogs, and from dogs in general. But you have to create an actual instance of BassetHound
, a BassetHound
object, in order to see a dog drool. Multiple Inheritance
Dog
. That might do if you were only interested in one aspect of a dog's behavior. But in the real world, systems are far more complex. Some unlucky dogs are wild or stray, and do not have owners. Instead, they have territories and live in packs. Other dogs are tame, and have both owners and veterinarians. Naturally, wild dogs behave very differently from pets. But so far, the dog world example is set up as if all dogs have a property called owner
, an instance variable that stores the owner's name.Dog
class, renamed Canine
, and create two new classes, Pet
and WildAnimal
, that contain aspects of canine life that are peculiar to domestic and feral dogs (see Figure 1-3). Mixing these classes together will produce dogs with the desired characteristics.
Figure 1-3: Multiple inheritance and dogs
PetDog
class that uses multiple inheritance to define properties and behavior. The new class is a template for creating objects that share characteristics of all of its parents. PetDog
inherits from both the Canine
and Pet
classes, which incorporate the general properties and behavior of the previously defined Dog
class, and add new ones.
Note - If you have been using the ScriptX Language and Class Library in one
continuous session since the beginning of this chapter, you will not be able to
redefine the Dog
class until you eliminate all instances of Dog
from the system.
Instead of reusing Dog
, this example uses the name Canine
as a root class for
dogs.
class Canine ()
instance variables
age, length, weight, sex, temperament
instance methods
method bark self -> print "makes a lot of noise"
method sniff self -> print "sticks nose into things"
method sleep self -> print "lazy dog sleeps all day"
end
class Pet ()
instance variables
name, owner, breed, veterinarian, spayed
instance methods
method fetch self -> print "fetches a stick"
end
class PetDog (Pet, Canine)
end
PetDog
class does not actually define any new behavior or properties for PetDog
objects. An instance of PetDog
will have all the instance variables and instance methods that its superclasses define. Now create a PetDog
object.object tammy (PetDog)
settings name:"Tammy", owner:"the Metzenbergs", sex:@female,
spayed:@true, breed:"Siberian Husky", veterinarian:"Dr. Donovan"
end
fetch tammy
"fetches a stick"
sniff tammy
"sticks nose into things"
PetDog
object tammy
defines properties of both a Canine
object and a Pet
object. She barks, sniffs, and sleeps like a canine, and she fetches like a pet. fetch
method, you could easily create a Cat
class and combine it with Pet
to create the new class PetCat
. In object-oriented terminology, Pet
is being used to mix in characteristics of Pet
with another class to create a new subclass that offers additional features.PetDog
object appear in a ScriptX window, you could create a new class that mixes PetDog
with TwoDShape
, one of the core classes. As an instance of TwoDPresenter
, a TwoDShape
object can present a graphic object, such as a bitmap. Mixing in the TwoDShape
class would give your objects the ability to display a target object (such as a bitmap) in a window with a two-dimensional coordinate system. Ease of Modification
PetDog
object when you create the object.object snoopy (PetDog)
instance methods
method sleep self -> (
print "sleeps on top of his doghouse"
nextMethod self
)
settings name:"Snoopy", owner:"Charlie Brown", breed:"Beagle"
end
sleep snoopy
As this example shows, specialization in ScriptX is not limited to defining new classes. You can add new instance variables and define or override instance methods at any level, even for a single object."sleeps on top of his doghouse"
"lazy dog sleeps all day"
OK
Connections Between Objects
PetDog
class inherits from both Pet
and Canine
. From the Pet
class, a pet dog inherits the instance variable owner
. If an owner has an address
instance variable, you can get access to that information through the dog. The following example defines the Owner
class and creates an instance of Owner
and an instance of PetDog
.class Owner ()
instance variables
name, address
end
-- create an owner
object janne (Owner)
settings name:"Janne", address:"Fairfax, California, USA"
end
-- create a dog for the owner to own
object odan (PetDog)
settings breed:"Mastiff", sex:@male, name:"Odan"
end
owner
instance variable is set, you can figure out where the dog lives by examining the owner's address
instance variable.odan.owner := janne
odan.owner.address
"Fairfax, California, USA"
Canine
or PetDog
classes. (In an actual program, you could make this connection at initialization, and introduce error and type checking.) Building and Maintaining Libraries
Special Kinds of Classes
Wrapping Up Objects
This document is part of the ScriptX Language Guide, one of the volumes of the ScriptX Technical Reference Series. ScriptX is developed by the ScriptX Engineering Team at Apple Computer, successor to the Kaleida Engineering Team at Kaleida Labs, Inc.