We've used sequences as ways of joining together values of the same type, typically where each value plays a similar role.
What about joining together values that play different roles in a computation?
In real-life settings, we might collect related but differing tools in a box, with a compartment for each item.
And of course there are always food-related examples to be found.
We observe that there are different kinds of boxes such as tool boxes and chocolate boxes,
and that there are specialized slots for different items.
We can think of a box-like way of joining related data.
A date might consist of the month name as a string and two integers for the day and year.
A point might be defined by its x and y coordinates.
For latitude and longitude, perhaps four values would be stored,
and for an address, perhaps three.
Here's some useful terminology to talk about bundles.
Each type of bundle is called a class.
Here we've talked about four different classes.
An example of a bundle, with particular values, is called an object.
And we use the term attribute to refer to one of the slots for an item in a bundle.
Let's illustrate the terms using TV dinners as an example of a class.
Objects are specific choices of foods to put in the slots, here a beef dinner and a turkey dinner.
Each has four slots for the following attributes: vegetables, a side, a starch, and a main dish.
We can view an object being stored in memory as a bundle of addresses,
where the name of the object is stored with the address of the bundle.
Just like indices are used to label addresses in sequences, here names of attributes are used to label the addresses in the bundle.
The values associated with the attribute addresses are the values of the attributes.
Another way to view a class is a factory that makes items of a particular variety.
There can be individual differences, like in the case of the TV dinners, but only within the limitations of the attributes
The design principles for a class echo many of the design principles we've been using all along.
We choose names that convey meaning,
and types that fit our needs.
Moreover, we ensure that this information is documented, for example, in comments.
Using as an example the "Time" class seen earlier, we use an assignment statement in which we provide the values for the attributes in order.
We use dot notation to refer to a particular attribute of an object, where we use the name of the attribute.
For some languages, there may be automatically created functions associated with a class.
Common functions include creating objects, being able to determine if an object is in a particular class, or checking if objects are equal.
There is no restriction on what type of value can be stored in an object.
Here we have an event object that consists of two time objects and a string.
The dot notation swim.start refers to an time object for which we can use swim.start.min to extract the attribute for minutes, with value 30.
Since objects are mutable, we can list an attribute on the left side of an assignment statement, resulting in a change to the object.
Consequently, we need to be concerned with aliasing,
where an object can be accessed by two different names,
and a change under one name changes the value stored under both.
A class contains not only bundles of values, but also functions that can act on those bundles.
These use dot notation with the first parameter of the function being followed by a dot and the function name, just as we used for strings.
This is no coincidence as in many languages strings are objects.
Why use methods instead of functions?
One advantage is that methods can only be used on data of the correct type, keeping us from making errors.
Associating functions with classes also ensures that related functions are grouped together in a logical way.