What is an object, anyway?

The best way to think about objects is as people at work. Each one has a specific job to do. They know how to do their job. They have all the knowledge, tools and supplies they need to do it.

Think about how a handwritten letter gets sent through the post.

We have a writer. They choose what words go in that letter, then write them down. This letter is then handed to a postman. The postman drives the letter to a sorting office.

The sorting office has inside it a big map of postcodes. On that map, each postcode has a little pin with the another postman’s name, who is responsible for delivering the letters.

The sorting office hands over the letter to the correct postman, who delivers it to a reader.

So far, so dull. Just an everyday story about an outdated way of sending mail. But it contains the two key insights into Object Oriented Programming.

Notice how the writer asks the postman to deliver the letter. The writer does not tell the postman how to deliver the letter. That’s not the writer’s job. The writer writes. The postman delivers. They each do their own job without interference from the other.

The writer also doesn’t ask the postman anything at all about how the delivery will be done. The postman is free to do that however they see fit. They can even change how they do the delivery later. The writer will not be affected at all.

This is the essence of OOP. Instead of people, we have objects. Each object knows how to do its own job.

You’ll notice that this description is quite independent of programming languages. OOP itself is a design approach that can be done in any language - even assembler or C.

Languages like Java are called OOP languages because they provide direct support for writing code that reads like this.

What’s all this got to do with Java?

As an object oriented language, Java was designed to reflect the real world, like in our example above. It allows us to write code that speaks about Postman and Letter and deliver() and so on. It allows us to write computer code that reads like an English description of our problem. Not like code.

We can write short pieces of code that can hold secrets, and present behaviours. Those short pieces of code can be asked to do things. The code representing what a postman knows and does can be asked to deliver a letter.

The way Java does this is in two parts.

First, it provides a ‘traditional’ computer language. It has variables, conditionals, loops and all the other good stuff we think of as code.

Then it gives us a way to package up that code to represent real world concepts.

These packages of code and data are called ‘objects’. Each object has a certain type - like “this object represents a postman”, or “this one is a user”. We call these Classes in Java. All objects belong to a class, which tells you what they represent. You can have any number of objects of the same class. You can have any number of classes.

A Class represents an idea. It can create multiple objects. Objects can store their own data. They provide methods, which are small chunks of code that can be called by a program. These methods represent the behaviours we talked about. Things like ‘deliver a letter’, or ‘check spelling’ on a word.

Methods can describe absolutely any behaviour we can think of. This is the superpower of object oriented programming.

Confusing? Yes. It is at first. There’s a lot to learn.

The easiest way to understand it all, I think, is by the ‘User’ example in the next section. But let’s introduce some terminology and Java syntax before we look at that.

Classes, methods, constructors, objects, references

Object An object is a small bundles of methods and data, used to represent a single, specific thing: a person, a train, a word, a product.

Class In Java, all objects belong to a class. A class allows one or more objects to be created. It is a blueprint or a template for the common features of each object.

Method A method is used to describe the behaviours our object gives us. It is rather like a function that is specific to a class.

New Keyword new takes a class name, then creates a specific object. It returns a reference to that object that we can store. This lets us use that object later in our code.

Constructor A special method that new will call. It is responsible for setting up the object as it is being created. We can supply initial values of things here.

Fields These are variables that are unique to each object. To help our behaviour methods do their work, we can store data in our object in fields.

Object reference keyword new will create an object, set it up with a constructor, then return a ‘reference’. We can store this reference in a local variable, or in an object field. We can then call methods on that object, using the reference and the dot operator.

this Inside a method, keyword ‘this’ is an object reference that refers to the ‘current object’. It is the same idea as when I talk to you and say ‘me’. I am referring to myself.

private A keyword to mark a field or method as being usable only inside the Class. Outside, it is invisible. It is used to mark ‘secrets’.

public A keyword to mark a method as being usable from outside the Class in calling code. Can be used on fields, but that is rare.

A simple example: Greeting users

To show what we mean, let’s make a simple object: a User.

Most systems have users. Hopefully, we’ll have loads of users - if our marketing works right. Our job is to show a personalised greeting to every one of them.

The first design decision is to represent each user as an object. And to do that, we’ll need to code up a ‘blueprint’ for what all user objects have in common. A Java ‘User’ class:

 1 public class User {
 2   private String name ;  // 3. private data
 3 
 4   public User( String name ){. // 2. constructor
 5     this.name = name ;
 6   }
 7 
 8   public void greet() { // 1. greet method
 9     System.out.println( "Hello, " + name );
10   } 
11 }

Before we can do anything with them, we need to create a simple test application. We’ll use the standard bit of Java boilerplate code to create an Application class with a ‘static main()’ - That’s a ‘magic method’ that tells the operating system where to start our Java program.

1 public class GreetingsApplication {
2   public static void main( String[] commandLineArgs ) {
3     User u1 = new User( "Jake" );
4     User u2 = new User( "Katy" );
5 
6     u1.greet();
7     u2.greet();
8   }
9 }

Running this program shows us ‘Hello, Jake’ and ‘Hello, Katy’. It does this by creating two, separate user objects and asking them to greet the user they represent.

This code might look underwhelming, but it contains the most important basics of OOP. Let’s go through the key pieces.

Methods - Making objects do stuff

The most important part about each object is what you can ask it to do: the public methods.

Class User has a single method on it called ‘greet()’ (see 1).

The method name tells us what the method does, not how. When we write the calling code, this name describes what this method will do for us. It also insulates us from having to care about how this gets done.

This is important because it lets us change the internals of method greet(). When we do, there will be no change at all to the calling code.

Our test app creates two user objects and calls the greet() method on each of them. Notice how close to plain English the test app reads. OOP is all about designing customised objects that do exactly the right thing for our app. We can name methods the same way we talk in English about the problem we’re solving.

When we design objects, we start with behaviour methods. We add data and logic later, if it is needed to make those methods do their job.

Secrets - Specialist knowledge for our objects

Just like the people in our example know things and have tools to help them work, objects have their secrets, too.

Objects typically have two secrets:

  • Data - which is unique to each object
  • Algorithms - the logic of how work is done

Our greet method has both kinds of secrets.

The logic secret of greet() is simple. We use Java’s System.out.println() library method to write text to the console.

This is the how - how our method greets a user. Because it is hidden behind the greet method’s signature, we are free to change how we do this without affecting the calling code. This is important. This stops changes from ‘rippling out’ through the system. That makes the code easier to understand and safer to change.

This is also why the calling code looks so simple. It is not concerned with deep technical details. It just asks for what it needs doing - user.greet(). It leaves it up to the object to get the work done.

This is called the Tell Don’t Ask principle

We tell the object what we would like it to do for us. We don’t ask it for any of its secrets and try to do its job ourselves in the calling code. It is a huge advantage in simplifying our code.

Our object also has the other secret - data.

For our greet() method to work, it will need to know the user’s name.

We decided upfront that each User object will represent one individual user in the real world. That user has a name. So our user objects make the perfect place to store their name. We do this in the private field called ‘name’ (see 3)

Having just one String field can be confusing to OOP beginners. How do you store the names of all the users without an array or something?

Non-OOP code might well have an array or dictionary to store all the names of the users. OOP code splits this problem up a different way. As we have one object per user and only store one name for each user, our object only needs to store one name.

Instead of one array with many names, we have many objects each with only one name. It’s less to think about and a lot more obvious. Where can we find a user’s name? In their object.

This idea of self-contained objects makes Object Oriented programs simpler to understand.

Constructors - Getting objects ready to use

As we create one object per user, we want to make sure that the object is ready to use after creation. This avoids many problems of ‘uninitialised data’ that plague other approaches.

We have a special method to do this: the Constructor.

The big idea of a constructor is that it creates an object, loads it up and makes it ready to use.

For our User objects, the private ‘name’ field (see 3) is set inside the constructor. We pass each individual name into the constructor as a parameter.

Constructors help keep our private data secret. They provide a way for the calling code to set up an object without knowing anything about its secrets.

The big idea: calling code is simple

Now we understand how the User class has been coded inside, let’s look at the big win of OOP: the public interface. The part that the calling code sees.

It’s really simple.

1 User u = new User("Alan");
2 u.greet();

To write a program that knows how to greet a user, all we need to know - and call - are the two public pieces. We call the constructor to create the object and get it ready for use. Then we call greet(). It really couldn’t be any easier.

Now, imagine how this helps us as we grow bigger programs. We might have hundreds or thousands of classes. Maybe tens of millions of lines of code. But with OOP, we only need to understand what our object’s public interface can do. It insulates us from the details.

This is the key of OOP done right: the calling code is simple

And if it isn’t … we’re doing it wrong.

Object Oriented Design is Behaviour Driven Design

The key to doing OOP right is to let behaviours drive the design.

The first question is always ‘what does this object need to do?’. We represent that as a method, using the name to describe the behaviour.

Then, we can add supporting code inside that method to make it work. We might add calls to private methods to break the work into small chunks. We might need stored data as fields in the object. We would need a constructor in that case.