This course has already ended.

The latest instance of the course can be found at: O1: 2024

Luet oppimateriaalin englanninkielistä versiota. Mainitsit kuitenkin taustakyselyssä osaavasi suomea. Siksi suosittelemme, että käytät suomenkielistä versiota, joka on testatumpi ja hieman laajempi ja muutenkin mukava.

Suomenkielinen materiaali kyllä esittelee englanninkielisetkin termit.

Kieli vaihtuu A+:n sivujen yläreunan painikkeesta. Tai tästä: Vaihda suomeksi.


Chapter 2.1: Object-Oriented Programming

About This Page

Questions Answered: How can I represent my program’s “world” to the computer? How can a larger program be built up from components — objects — that work together? How do I command an object in Scala?

Topics: Programming as conceptual modeling. Object-oriented programming: objects and methods, communication between objects. Singleton objects in Scala: calling a method, referring to an object.

What Will I Do? First, there’s some very important reading to do. Then you’ll practice commanding objects in code.

Rough Estimate of Workload:? Two hours or so, if you go through each example carefully, as you should. The chapter may be tricky: the fundamental concepts of object-oriented programming may seem abstract or alien at first.

Points Available: A25.

Related Modules: IntroOOP (new).

../_images/person06.png

Introduction

So far, we’ve covered an assortment of programming constructs: expressions, references, functions, data types, and so on.

Let’s put them all to the side for a moment.

In this chapter, we’ll adopt a new perspective as we discuss our chosen approach to creating larger programs: object-oriented programming.

In a sense, all the preceding chapters have been an overture for the work that begins in this chapter and continues throughout O1. At first, it may not be clear how what you learned earlier connects with this chapter, but by the time you finish the chapter, you should already have a fair inkling. As it happens, the overture had been composed from those elements precisely because they’ll soon come in handy as you learn to program in an object-oriented way.

Conceptual Modeling

A failure to communicate

../_images/communication_prob.png

A problem.

Programs deal with diverse forms of data. Just adding numbers, concatenating strings, or drawing circles won’t get us far. We want our program to “store a file”, “enroll a student in a course”, “react to a button click”, “withdraw money from an account”, or “pick the computer opponent’s next move”.

We face a problem: human thought and language are conceptual, but the computer doesn’t understand real-world concepts. What is a “course”, a “student”, a “hotel”, or a “bank account”?

A programming language cannot unambiguously define all the countless concepts that we might want to use in programs. Nevertheless, the computer needs unambiguous instructions if it is to execute programs that operate on the “world”, or domain, of the program.


../_images/communication_sol.png

Defining concepts in program code.

Bridging the communication gap

Let’s take our human needs as our point of departure. Let’s create programs that a machine can process but that reflect how we humans made sense of the program’s “world”. Let’s specify exactly the concepts and terms that we need.

As programmers, our task will be to model the program’s domain: we’ll create a conceptual model of the relevant information and the behavior that is associated with each concept. Since the programming language itself cannot define the concepts we want, we need the language to help us express definitions of our own.

Object-Orientation, A Programming Paradigm

Over the years, people have come up with a number of different ways to program, so-called programming paradigms (ohjelmointiparadigma). Different programming paradigms have their own recommendations about how to think about programs, how to represent a program’s domain, and how to structure program code.

In O1, we’ll focus on a particular paradigm: object-oriented programming or OOP (olio-ohjelmointi). OOP is a popular and fun way to program. The Scala language is well suited to OOP.

Paradigms in O1

It’s possible to draw from several paradigms at once. The Scala language, for instance, has been deliberately designed to support multiple approaches to programming and to combine approaches. In O1, we’ll combine OOP especially with what is known as imperative programming (imperatiivinen ohjelmointi) and also with functional programming (funktionaalinen ohjelmointi). No need to worry about that now, though; we’ll discuss paradigms in some more detail in Chapter 11.2.

OOP has a number of attractive characteristics. In O1, we’re going to embrace objects rather unquestioningly. Even so, you may wish to make a mental note that OOP is not a silver bullet that always works for any and all programming needs. Chapter 11.2 and O1’s follow-on courses will introduce you to alternative approaches.

Object-oriented programming emphasizes conceptual modeling. Each individual course, student, back account, GUI window, or button can be represented as an “object”.

Objects

object: something mental or physical toward which
thought, feeling, or action is directed

—Merriam-Webster Online

Raindrops on roses and whiskers on kittens,
bright copper kettles and warm woolen mittens,
brown paper packages tied up with strings;
these are a few of my favorite things!

—from The Sound of Music by Rodgers and Hammerstein

../_images/object_car-en.png

An diagram illustrating an object and its methods.

An object (olio) is an abstraction of an entity or “thing” in an object-oriented program. Typically, an object will have a number of behaviors, known as methods (metodi), that define how you can use the object. For instance, you might represent a car as an object that has methods such as “drive”, “add passenger”, “refuel”, “determine remaining fuel”, and so on.

Many objects also have attributes that describe the object’s permanent or variable characteristics. For instance, a car object might have attributes such as manufacturer, current location, the amount of fuel in the car’s tank, or the passengers currently inside the car.

It’s up to the programmer to choose which attributes and methods they want to associate with each object. That decision depends on which aspects of the domain (the “world”) are relevant to the problem at hand. For instance, a car’s manufacturer might be relevant in some programs but not in others. When running an object-oriented program, the computer stores objects in its memory as the programmer has dictated.

Pretty much anything can be represented as an object. Here are some examples in visual form:

../_images/object_many-en.png

As that picture shows, an object can be associated with data (such as the ID number of a student object or the text of a button object) and actions (such as enrolling a student or removing a file).

Notice, too, that objects may resemble each other, like the two animal objects above do. In other words, objects may have the same type.

Modeling a domain as objects

We’ll usually model a program’s domain with a combination of objects. Here’s one simplified example as a diagram:

../_images/object_mycoursesish-en.png

Objects associated with an imaginary course-enrollment system. Notice that the objects refer to each other and combine to form a whole.

Each object has specific responsibilities in making the program work. For instance, a course object might be charged with keeping track of which students have enrolled: it records new enrollments while adhering to restrictions on how many students can sign up. By combining the functionality of different objects, we can define the whole program’s behavior.

We can also use objects to represent a user interface. Consider the following GUI window, for example:

../_images/feedback_form-en.png

In a Scala program, that window can be represented as a combination of objects, as shown in this diagram:

../_images/object_feedback-en.png

Objects as “Actors”

../_images/sir_yes_sir.png

There’s a common metaphor that may help you wrap your brain around OOP.

You can think of objects as human-like actors. Each object is, if not sentient, nevertheless capable in a narrow speciality.

An object is self-aware in the sense that it “knows” its own attributes. For instance, a car object knows the amount of gas left, and a course object knows the students who have enrolled.

An object is capable of receiving messages (“commands”, “requests”) that match its methods. You can command a course object to enroll a student, a car object to refuel, or a file object to delete the corresponding data from the hard drive. An object can’t act on a message unless it has a method available for that sort of message.

An object has a specific way of behaving, that is, a specific way of reacting to the messages it receives. The programmer defines the objects’ behavior using a programming language. For example, we can define objects that compute with numbers, record information in the computer’s memory, send messages to other objects, create new objects, and so forth.

An object is unfailingly obedient and complies with the programmer’s instructions to the letter. Object-oriented programming, too, requires diligence, precision, and unambiguous language.

Communication between Objects

A single object may not be able to do much, but multiple objects in organized co-operation can accomplish a great deal. Let’s look at two examples.

Example: GoodStuff

How does Chapter 1.2’s GoodStuff application work when you use it through its GUI? More specifically, what happens when the user clicks the Add experience button?

At that point, the program is supposed to record a new experience and, if appropriate, move the happy face that marks the user’s favorite experience.

GoodStuff’s objects make use of each other’s methods: they “command each other”. The program’s execution happens as the objects message each other and react to those messages. The presentation below should give you an idea of how this works. Even though this depiction is graphical, it’s consistent with the actual technical implementation within the GoodStuff module.

Above, object communication was shown as speech bubbles, but it’s possible to express the same messages as Scala code; see below. Even though these commands aren’t familiar yet, perhaps you’ll hazard a guess as to which bubble each Scala command matches.

Experience(name, description, price, rating)
this.experiences += newExperience
newExperience.chooseBetter(this.fave)
if this.isBetterThan(another) then this else another
this.favorite = newExperience
category.addExperience(newExperience)

You’ll learn each command’s precise meaning later.

Example: course enrollment

(The second example of communicating objects, below, is essentially similar to the previous one. You would do well to study this example, too. You may skip it if you’re in a dreadful hurry or if everything seems crystal clear. We hereby forbid you from skipping the example just because you’re lazy.)

Let’s consider an imaginary application program where a student can enroll in courses by clicking a GUI button associated with the desired course. At this point, the program should confirm whether the student can be successfully enrolled. Enrollment is successful if there’s enough space in the lecture hall and if the same student hasn’t already enrolled. Upon enrollment, the program should record the student in the course’s list of enrollees and update the student’s list of courses they’re personally enrolled in.

Here’s a sketch of an object-oriented solution:

Program state

The states of individual objects form the state of the object-oriented program. In GoodStuff, the program’s state encompasses the state of a category object (its name, favorite, and list of recorded experiences) as well as the data associated with each experience object (descriptions, ratings, and prices).

As an object acts on the messages that it receives, its state sometimes changes, as does the state of the whole program. Examples: a new experience is added in a category, a new student is enrolled in a course, a user’s personal information is edited, etc.

Program execution

The objects of an object-oriented program form a conceptual model that structures the program so that it makes sense to a human. Each object has a particular role in the interplay that unfolds within this structure as the program is run. Ultimately, though, a program run is just a sequence of commands being executed one after the other by the computer. The programmer defines these commands and attaches them to objects as methods. Some of the methods cause objects to issue further commands to each other: it’s as if one object passes the turn to act to another object; it then waits for the other object to finish, so only a single object is active at a time.

The methods on objects implement small algorithms, which combine to produce the overall algorithm that accomplishes what the program is meant to do.

Well, to be more exact...

... what was just said is a simplification. It’s possible to create programs where multiple objects work simultaneously. However, we won’t be covering concurrency or parallel computing in O1.

A Closer Look at Messages

If this chapter still seems to be disconnected from the earlier chapters, that’s about to change.

Method calls

Sending a message to an object activates one of the object’s methods. Sending such a message is known as calling a method (or invoking a method; metodikutsu). Here, we call a method named “drive” on a car object:

../_images/object_method_car_drive-en.png

Some method calls simply request the object to report something about its state, such as the amount of fuel in the tank. Other calls are more elaborate:

Method parameters

Methods can receive parameters that convey additional information about what the object is supposed to do. In this example, the amount of fuel is provided as a method parameter (which is highlighted in yellow):

../_images/object_method_car_refuel_10-en.png

Different kinds of values can be passed as parameters: numbers, references to other objects, and so forth.

A method may receive one or more parameters. Or zero, if the message itself says it all, like here:

../_images/object_method_car_getfuel-en.png

Responses from object a.k.a. return values

Often, an object will answer a method call in some way, sending a message to the caller as a response. We say that the method returns a value. One use for a return value is to acknowledge an operation’s success or failure:

../_images/object_method_car_refuel_10_ok-en.png

A method’s return value may also report something about the object’s state:

../_images/object_method_car_getfuel_20-en.png

Questions about objects and methods

Assess the following claims based on what this chapter has told you about object-oriented programming. The answer to every item isn’t directly available in the text above, but it’s possible to work out each answer from what has been said.

Think carefully, then take your best guess and read the feedback you receive. It’s not necessary to get everything right on the first try. This assignment, too, is intended to be a part of the learning process, not just a test to see what you’ve previously learned.

Tick each correct claim.

Tick each correct claim.

Methods are functions attached to objects

What was said above about calling methods, passing parameters to methods, and receiving return values is very similar to what you learned about functions in Week 1. That is of course not a coincidence: methods are functions that are associated with objects. They have access to the object’s data and they take care of things that fall within the object’s purview. They define what the object can do.

What we’ve called communication between objects is essentially one object’s functions calling other objects’ functions.

We are now ready to place the fundamental concepts of object-oriented programming in our concept map:

Objects and Scala

The Scala language gives us the means to define singleton objects (yksittäisolio): we can write a piece of code that specifies the characteristics of one individual object. Once we’ve so defined an object, we can issue Scala commands to it.

In the rest of this chapter, you’ll continue to learn object-oriented programming by commanding some singleton objects that have been already defined for you. In the next chapter, you’ll get to define objects and methods of your own. (This is much as in Chapters 1.6 to 1.8 where you first used given functions before implementing your own.)

In order to command an object, we need to tell the computer which object we wish to address and which of its methods we wish to call. That’s precisely what we’ll do in the next example as we experiment with a particular object:

The parrot: an introduction

Let’s use a virtual “parrot”. This parrot object has a particular repertoire of phrases (strings) that it has learned to “say” and that it recites whenever it “hears” a familiar-sounding word. We can command the parrot to speak by calling a method named respond and passing in a phrase that the parrot hears.

../_images/object_method_parrot_respond-en.png

Calling the parrot’s respond method. The method’s name is in red, the parameter in yellow, and the return value in green.

Our virtual parrot is defined in the IntroOOP module. Before we begin calling its methods, fetch IntroOOP into your O1 project in IntelliJ. Load up the module in the REPL.

Method calls in Scala

Let’s call respond and pass in a string parameter:

parrot.respond("Would you like a cracker?")res0: String = Polly wants a cracker!

The method call’s recipient, the target object, comes first. We indicate who the recipient is by writing an expression that refers to an object. The name parrot is defined in package o1 and refers to a particular object.

After the recipient, a period-dot.

The rest of the method call looks familiar: first, the method’s name, then a parameter expression (or expressions) in round brackets just as we did with function calls in previous chapters. (Remember: methods are functions attached to objects.) Here we pass a fairly arbitrary string as a parameter to respond; the parrot “hears” it and responds to it.

The method returns a string value that has been chosen by the parrot object according to its own internal logic.

Here are four more examples of calling respond on different parameter values:

parrot.respond("Would you like some crackers?")res1: String = Polly wants a cracker!
parrot.respond("So you're called Polly?")res2: String = Polly wants a cracker!
parrot.respond("How are you?")res3: String = How!
parrot.respond("Bye then, I guess.")res4: String = Bye!

What’s important here is for you to see how to command an object, which just happens to be a particular sort of parrot object. How the parrot has been programmed to pick its responses is not significant in itself, but it may be easier for you to follow the rest of the example if you know this much:

When the parrot hears a word that resembles any word in a phrase that it knows, it responds by repeating that entire phrase. This parrot’s repertoire includes the phrase “Polly wants a cracker”, which features both “cracker” and “Polly”. The parrot recognizes a word as familiar if the word’s Levenshtein distance (Chapter 1.6) from any word in the repertoire is one or zero. For instance, the distance between “crackers” and “cracker” is one.

The parrot has been programmed so that whenever none of the words match anything in its repertoire, it simply repeats the first word that it heard.

Go ahead and experiment with the parrot object. Choose IntroOOP as you launch the REPL.

A study in parroting

Our parrot’s repertoire contains two phrases. One, as you’ve seen, is “Polly wants a cracker”. Call the parrot’s respond method to find out what the other phrase is. Hint: mention “rum”.

The other phrase in the parrot’s repertoire is this:

Affecting an Object’s State

So far, nothing about our example suggests that an object is anything more than a bunch of functions that can be accessed via a particular name such as parrot. But there’s more to an object than that.

In the earlier examples, you saw that an object has data that defines the object’s state. That state may influence how the object reacts to method calls.

Some of an object’s data can be immutable (e.g., the manufacturer recorded for a car object), and indeed there are objects whose state never changes. However, other objects have methods that have effects on state. For instance, a method might change a car object’s location or a bank-account object’s balance.)

Our parrot, too, has a method that affects the parrot’s state by expanding its repertoire:

parrot.respond("Time flies like an arrow")res5: String = Time!
parrot.learnPhrase("Fruit flies like a banana")parrot.respond("Time flies like an arrow")res6: String = Fruit flies like a banana!

Initially, the parrot doesn’t recognize any of the words in this sentence. It just parrots the first word.

Our parrot is a swift learner: we need but call its method learnPhrase and pass a new phrase as a parameter. This method returns only Unit, which means that no return value shows up in the REPL.

Invoking respond again attests that learning took place: now the parrot recognizes something familiar and recites a phrase from its updated repertoire. Our object has a memory!

Another Example: A Virtual Radio

The object of the next exercise is a radio. Once again, we recommend that you work along in the REPL, trying the commands shown below and your own variations of them.

Our radio object represents the tuning interface of an FM radio (for an imaginary, simplified device). It has four “presets” for quickly accessing particular (virtual) radio channels. We can switch the radio to a preset station by calling the select method and passing in a parameter between 1 and 4. Let’s pick preset number two, for instance:

radio.select(2)res7: String = 94,0 MHz: Radio Suomi

As it happens, the radio object has been pre-programmed so that preset number 2 is 94.0 megahertz. The select method returns a string that informs us of the frequency and the corresponding channel. Other presets produce other frequencies:

radio.select(4)res8: String = 98,5 MHz: Radio Helsinki

The method tune “turns the hertz knob”. That is, it adjusts the frequency from the current value by a given amount:

radio.tune(2)res9: String = 98,7 MHz: just static

An adjustment’s size is measured in “notches”. For this radio, one notch equals 100 kHz, so the command above increases the frequency by 2 * 100 kHz from what it was. (There’s no channel at that frequency.)

Notice that the radio object clearly is capable of keeping track of the frequency that it’s tuned to: it computes a new state for itself from its old state and the parameter value it receives. This object, too, has a memory.

Requesting the values of attributes

Our radio object stores certain information that we can request from it simply by writing the desired attribute’s name after the dot. Below, we ask for the currently selected frequency and the radio’s notch size, both of which are integers:

radio.frequencyKHzres10: Int = 98700
radio.notchKHzres11: Int = 100

You don’t need parameters or brackets when accessing these attributes.

Assigning to an attribute

The way our radio object has been programmed, we can assign values to some of its attributes. Instead of calling the methods tune or select, we can set the frequency directly:

radio.frequencyKHz = 92200radio.frequencyKHz: Int = 92200

This is, in effect, a different-looking way to send a message to the object: “Set your frequencyKHz attribute to the value 92200.”

Using objects, an animation

Use the REPL first to select preset station number three, then to tune the radio five notches (500 kHz) higher. What is the radio’s frequency, in kilohertz, after you’ve entered these commands?

In the field below, enter a Scala command that sets the radio’s frequency to exactly 106200 kHz, no matter what its earlier frequency was.

On Objects and Abstractions

At the end of Chapter 1.6, we identified functions and variables as abstractions that make your work as a programmer easier.

Abstraction is also central to objects, in two different ways.

First, each object is an abstraction of some entity that it represents. The programmer has chosen to represent, in the object, certain aspects of the problem domain, while leaving out aspects that are less salient.

Second, each object has both an internal implementation and an external “façade” or interface through which programmers communicate with the object. The interface is an abstraction of the actual object: it includes the names of methods, the types of parameters and return values, and any other information that you need to use the object. The algorithms that implement each method aren’t part of the interface, nor are the details of how an object keeps track of its state. We’ll return to this important notion in Chapter 3.2.

In Conclusion: Objects as Static and Dynamic

This chapter is crucial to our programming efforts in future chapters. With that in mind, please take the time to view the short presentation below. It frames this chapter’s topic in a broader context and sets the scene for the chapters to come.

In this chapter, you’ve seen examples of objects defined in Scala, and should now have a fair idea of how you can command an object whose interface you know. So what next? Here’s the plan:

  • In Chapter 2.2, you’ll learn to implement singleton objects. You’ll see how to define an object and the way the object responds to messages.

  • Chapter 2.3 introduces classes. You can use a class to define the characteristics of many similar objects in one go. First, you’ll learn to use a given Scala class, and then...

  • ... in Chapter 2.4 you’ll get to practice implementing classes of your own.

  • During Weeks 2, 3, and 4, we’ll continue exploring the GoodStuff application. You’ll learn to understand the classes that implement that program. We’ll discuss many other example programs as well, to be sure.

  • During the rest of O1, you’ll continue to learn concepts and techniques that you can apply as you build programs from classes and objects.

Summary of Key Points

  • Object-oriented programming (OOP) is one way of defining the entities and concepts that make up a program’s domain, such that they can be manipulated by a computer.

  • An object is a representation of a single entity or “thing”. Myriads of different things can be represented as objects.

    • An object commonly has attributes, which are part of its state, as well as actions, called methods, which determine what you can do with the object.

    • As an object-oriented program runs, the objects are held in the computer’s memory. Together, they form the program’s state.

  • You command an object by calling its methods. Methods are functions attached to objects; their purpose is to process information associated with the object and to take care of tasks that have been consigned to the object.

  • You can view the execution of an object-oriented program as communication between objects:

    • An object can be defined so that it commands other objects. Objects communicate by calling each other’s methods. An object can delegate part of its task to another object.

    • An object-oriented program, too, is ultimately executed by a computer as a sequence of instructions. Objects structure the problem domain; communication between objects determines the order in which instructions get executed.

  • In Scala, you call a method like you call any function:

    • You just have to indicate the target object, as in someObject.methodName(parameters)

    • Some objects have attributes that you can modify by assigning new values to them, as in someObject.attributeName = newValue.

  • Links to the glossary: object-oriented programming, object, method; abstraction; domain; state; static, dynamic; singleton object.

Feedback

Please note that this section must be completed individually. Even if you worked on this chapter with a pair, each of you should submit the form separately.

Credits

Thousands of students have given feedback and so contributed to this ebook’s design. Thank you!

The ebook’s chapters, programming assignments, and weekly bulletins have been written in Finnish and translated into English by Juha Sorva.

The appendices (glossary, Scala reference, FAQ, etc.) are by Juha Sorva unless otherwise specified on the page.

The automatic assessment of the assignments has been developed by: (in alphabetical order) Riku Autio, Nikolas Drosdek, Kaisa Ek, Joonatan Honkamaa, Antti Immonen, Jaakko Kantojärvi, Niklas Kröger, Kalle Laitinen, Teemu Lehtinen, Mikael Lenander, Ilona Ma, Jaakko Nakaza, Strasdosky Otewa, Timi Seppälä, Teemu Sirkiä, Anna Valldeoriola Cardó, and Aleksi Vartiainen.

The illustrations at the top of each chapter, and the similar drawings elsewhere in the ebook, are the work of Christina Lassheikki.

The animations that detail the execution Scala programs have been designed by Juha Sorva and Teemu Sirkiä. Teemu Sirkiä and Riku Autio did the technical implementation, relying on Teemu’s Jsvee and Kelmu toolkits.

The other diagrams and interactive presentations in the ebook are by Juha Sorva.

The O1Library software has been developed by Aleksi Lukkarinen and Juha Sorva. Several of its key components are built upon Aleksi’s SMCL library.

The pedagogy of using O1Library for simple graphical programming (such as Pic) is inspired by the textbooks How to Design Programs by Flatt, Felleisen, Findler, and Krishnamurthi and Picturing Programs by Stephen Bloch.

The course platform A+ was originally created at Aalto’s LeTech research group as a student project. The open-source project is now shepherded by the Computer Science department’s edu-tech team and hosted by the department’s IT services. Markku Riekkinen is the current lead developer; dozens of Aalto students and others have also contributed.

The A+ Courses plugin, which supports A+ and O1 in IntelliJ IDEA, is another open-source project. It has been designed and implemented by various students in collaboration with O1’s teachers.

For O1’s current teaching staff, please see Chapter 1.1.

Additional credits appear at the ends of some chapters.

a drop of ink
Posting submission...