knowledge-kitchen

Object Orientation - Core Concepts (in Java)

Object-oriented programming is an exceptionally bad idea which could only have originated in California.

Edsger Dijkstra

  1. Overview
  2. Black Box
  3. Files
  4. Dogs
  5. Creating Difference
  6. Controlling Belongingness
  7. Comparing Sameness
  8. Stringification
  9. The 4 Pillars of Object-Orientation
  10. Alternative Paradigms
  11. Conclusions

Overview

Concept

Object-Oriented Programming (OOP), first experimented with in the 1950’s, is today the standard programming paradigm. Nearly all major contemporary programming languages offer the ability to program with objects.

Concept (continued)

With object-oriented programming, a developer writes a description of things of a certain type.

Digression

In the 5th century, B.C., the astonishingly brilliant philsopher, Plato, described what we now call the Theory of Forms.

It’s imperative to understand this!

Object-oriented programming is a variety of imperative programming.

Black Box Metaphor

Smoke and mirrors

Despite being a form of imperative-style programming, specifying in detail how problems are to be solved, object-orientation nevertheless attempts to put to practice the black box metaphor of engineering, where, provided particular inputs, a machine produces a predictable output and the user of it doesn’t have to know the implementation details, even though they are written in the code.

Black box metapahor

Digression

The black box approach transcends any one discipline. For example, it was popular in the Behaviorist school of psychology, which held that human psychology could be defined by the empirically observed responses to given stimuli, regardless of the internal psychological thought patterns internal to the person.

Digression (continued)

The “Skinner Box”:

Skinner box

Example 1 - Files

Concept

Imagine you wanted to write code that represented files on a computer’s hard drive.

Properties

Every file on the hard drive might have some properties, e.g.

The values these properties hold collectively represent the internal state of any given file at any given moment in time.

Actions…

If we say a file is in control of its own destiny (a big if), we could say a file has a few actions it could take, e.g.:

… and their consequences

These actions represent a public interface through which other code can interact with any given file and instruct it what actions to take.

// telling the thing what to do...
myFile.setFilename("foobar.txt");
myFile.storeData(someData);

Calling these methods automatically updates the internal state of the thing.

// ...causes an internal update of its state - i.e. the values of the properties
filename = "foobar.txt";
data = someData;

UML class diagram

There is a standardized way of representing such things, called the Unified Modeling Language (UML). Here is an example of a UML ‘class’ diagram:

UML file example

Class definition

This diagram can be translated into code as such:

public class File {
    // properties
    private byte[] data;
    private String filename;
    private int size;
    private int modifiedDate;

    // actions
    public byte[] getData() { ... };
    public void setData(byte[] data) { ... };
    public String getFilename() { ... };
    public void setFilename(String filename) { ... };
    public void saveTo(String filePath) { ... };
}

Intention

A class definition is a template from which as many objects can be made as we like.

Example 2 - Dogs

Concept

Imagine dog, the concept.

Properties

Every dog might have some properties, e.g.

Different dogs will probably have different values for each of these properties.

Actions…

If we say a dog is in control of its own destiny (a big if), we could say a dog has a few actions it could take, e.g.:

… and their complications

Two dogs with different internal states might implement these same actions differently.

UML class diagram

A UML class diagram for this dog thing:

UML dog example

Class definition

This diagram can be translated into code as such:

public class Dog {
    // properties
    private String name;
    private int age;
    private String breed;
    private int weight;

    // actions
    public void bark() { ... };
    public void fetch() { ... };
    public void sleep() { ... };
}

Intention

This Dog class definition is a template from which as many Dog objects can be made as we like.

Difference

Constructing different objects from the same class

Our intention in creating a class is to be able to instantiate multiple distinct objects from that class template.

No difference

The following code, placed in the code of some other class definition, would instantiate two Dog objects from our Dog class.

Dog dog1 = new Dog();
Dog dog2 = new Dog();

These are different things in memory. See for yourself.

(dog1 == dog2) // false... they are different references

But they do not have any difference in terms of their internal states.

Difference!

If the internal properties of each Dog object were public or protected (which they are not), it might be possible to give each object distinct values:

Dog dog1 = new Dog();
// the following won't work, since the Dog's properties are all private
dog1.name = "Fido";
dog1.breed = "Bugle";
dog1.age = 10;

Dog dog2 = new Dog();
// the following won't work, since the Dog's properties are all private
dog2.name = "Tobik";
dog2.breed = "German Shepherd";
dog2.age = 3;

But this requires us to make visible the internal properties of the object, which goes against the black box metaphor and imperative-style programming.

Difference! (continued)

If we were able to set each Dog’s name, breed, and age to different internal states, while retaining hidden internal properties for each object, then we would achieve our goal.

We would like to be able to do something like this:

Dog dog1 = new Dog("Fido", "Bugle", 10);
Dog dog2 = new Dog("Tobik", "German Shepherd", 3);

At the moment, our class definition provides no mechanism for setting a given object’s internal properties.

Constructors

In order to be able to set each object’s properties discretely, we need to modify the Dog class to have a special constructor function.

public class Dog {

    // a constructor function!
    public Dog(String name, String breed, int age) {
        // set this object's internal properties
        this.name = name;
        this.breed = breed;
        this.age = age;
    }

    // properties
    private String name;
    private int age;
    private String breed;
    // and so on...
//...

Constructors (continued)

Note that constructor functions do not specify a return type.

public class Dog {
    //...
    public Dog(String name, String breed, int age) {
        //...
    }
    //...
}

This, not that

The this keyword is used to indicate which object we are talking about it.

Imagine we defined the Dog class’s bark method:

public class Dog {
    //...
    public void bark() {
        System.out.printf( "%s says, 'Woof!' ", this.name );
    }
    //...

This, not that (continued)

The answer is, “whichever dog the method is called upon”.

If somewhere we call that method on a dog named Fido, then Fido’s name will be output.

Dog dog1 = new Dog("Fido", "Bugle", 10);

//...

dog1.bark(); // outputs "Fido says, 'Woof!' "

Whereas, if we were to call that method on a dog named Tobik, then Tobik’s name would be output.

Dog dog2 = new Dog("Tobik", "German Shepherd", 3);

//...

dog2.bark(); // outputs "Tobik says, 'Woof!' "

Instantiating objects

With constructor functions, we can instantiate as many objects as we like with whatever property values we want them to have.

Dog dog1 = new Dog("Fido", "Bugle", 10);
Dog dog2 = new Dog("Tobik", "German Shepherd", 3);

Try it

Instantiating objects (continued)

We could make 101 Dalmatians with random names and ages, if we wanted…

// let's make a random mix of names and ages objects
String[] names = "Patch,Lucky,Cadpig,Roly Poly,Penny,Freckles,Pepper".split(",");
int[] ages = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 };

Dog[] dogs = new Dog[101]; // will hold 101 Dalmatians

for (int i=0; i<dogs.length; i++) {
    // assign each dog random property values from these arrays
    String randomName = names[ (int) (Math.random() * names.length) ];
    int randomAge = ages[ (int) (Math.random() * ages.length) ];

    dogs[i] = new Dog( randomName, "Dalmatian", randomAge ); // instantiate a Dog object

    dogs[i].bark(); // make each dog bark

}

Try it!

Changing internal state

Constructors allow us to set up an object’s internal state at the moment of its creation. But what if we wanted to update those values at some later date?

// instantiate an object with an initial internal statee
Dog dog1 = new Dog("Fido", "Bugle", 10);

// do some very important significant stuff...

// update the object's internal state at some later point
dog1.age = dog1.age + 1; // happy birthday!

By now, you know that this is not possible. The internal properties are private and cannot be accessed from ‘user’ code in a different class.

Setters

To change an object’s internal state, we need a ‘setter’ function to allow us to set the value of an existing object.

public class Dog {
    // ...etc etc

    // a setter function
    public void setAge(int age) {
        // first validate the value, and then use it if good
        if (age > 0 && age < 15) this.age = age;
    }

    // ...and so on and so forth
// ...

Setters (continued)

With setters, it is possible to set the values of properties on existing objects.

Assuming we have created a Dog object…

Dog dog1 = new Dog("Roly Poly", "Dalmatian", 10); // instantiate a dog

…we can now update its age using the setter

dog1.setAge(11);

Setters (continued again)

If setters are present, they should always be used whenever an object’s property values need to be changed.

Setters (continued once again)

For example, our original constructor can be updated to use setters that validate the values being set:

    public Dog(String name, String breed, int age) {
        this.setName(name);
        this.setBreed(breed);
        this.setAge(age);
    }

Accessing internal state

What if you wanted to know a particular Dog’s age… how could you find that out programmatically?

dog1.setAge ( dog1.age + 1 )

Getters

Of course, we can allow access to an object’s internal state. But we need to create ‘getter’ functions to do so.

public class Dog {
    // ...etc etc

    // a getter function
    public int getAge() {
        // simply return the value of the age property
        return this.age;
    }

    // ...and so on and so forth
// ...

Getters (continued)

With a getter function, we can read the current value of a property of an object…

System.out.println ( dog1.getAge() ); // will output dog1's age

… and use that information to help us understand that object’s internal state

dog1.setAge( dog1.getAge() + 1 );

Knowledge of an object’s internal state can help us make decisions

if ( dog1.getBreed().equals("Dalmation") && dog1.getName().equals("Pepper") ) {
    // use your imagination
}

Controlling Belongingness

Non-static properties and methods

Neither setters nor getters, nor even the methods that represent actions of the object (e.g. bark, fetch, sleep) were declared with the keyword static in their method signatures. Neither were any of the properties (e.g. name, breed, age).

Shared concerns

There are, of course, cases where it makes sense for a property or method to belong to the class as a whole… i.e. for it to be declared as static.

Shared concerns (continued)

public class Dog {
    //...
    public Dog(String name, String breed, int age) {
        //...
        Dog.numDogs++; // increment the static property
        //...
    }
    //...
    private static int numDogs = 0;

//...

Shared concerns (continued again)

Every time we instantiate a new Dog object, the counter will be incremented.

Dog dog1 = new Dog("Fido", "Bugle", 10);
Dog dog2 = new Dog("Tobik", "German Shepherd", 3);
System.out.printf( "There exist %d dogs in our world.\n", Dog.getNumDogs() );

Try it!

Comparing Sameness

Different kinds of difference

There are a few ways we can compare two objects.

Identity comparison

To check whether two supposedly-separate objects are, in fact, the same object, use the == operator.

if (dog1 == dog2) {
    System.out.printf("%s is actually the same dog as %s!\n", dog1.getName(), dog2.getName);
}

Objects as reference types

Because objects are reference types, when they are passed as arguments to a method, the memory address is what is passed.

public void rename(Dog d) {
        d.setName("Dog");
}
Dog dog1 = new Dog("Fido", "Bugle", 10);
rename(dog1);
System.out.println ( dog1.getName() )

Internal state comparison

To check whether two objects share the same internal state, even if they are different objects in memory, we have to roll our own solution.

public class Dog {
        //...

        public boolean equals(Dog that) {
            boolean sameName = this.name.equals( that.getName() ); // are the names the same?
            boolean sameAge = ( this.age == that.getAge() ); // same age?
            boolean sameBreed = ( this.breed.equals( that.getBreed() ) );
            return sameName && sameAge && sameBreed; // true if all the same, false otherwise
        }
        //...

Internal state comparison (continued)

To compare two dogs, you would use one of those dogs’ equals() method.

if ( dog1.equals(dog2) ) {
    System.out.printf("%s and %s have the same internal state!\n", dog1.getName(), dog2.getName);
}

EqualsBuilder

Since object’s properties are often of many different types, writing a custom [].equals()](#tedium) method must be done carefully and tediously. Fortunately, Apache Commons Lang’s EqualsBuilder class can make object comparisons easier and less prone to silly errors.

public class Dog {
        //...

        public boolean equals(Dog that) {
            return new EqualsBuilder()
                            .append(this.name, that.name)
                            .append(this.age, that.age)
                            .append(this.breed, that.breed)
                            .append(this.weight, that.weight)
                            .isEquals();
        }
        //...

Stringification

Concept

Converting an object to a String usually leads to unwanted text.

// instantiate a Dog object
Dog dog2 = new Dog("Tobik", "German Shepherd", 3);

// do something that requires the object to be converted to a String
String text = String.format("The dog as a string looks like: %s", dog2);

// see what you get...
System.out.println(text);

By default, the output would show the class name of the object and a hashcode - random-looking text that is not probably what you hoped for. Try it!

The dog as a string looks like: Dog@63961c42

Wouldn’t it be nice if we could instead output something descriptive, like,

The dog as a string looks like: Tobik, a 3-year-old German Shepherd

Fulfilling our desires

Java allows an object to describe how it should be converted to a String with a special method named toString() that returns the String equivalent of the object.

public class Dog {
    //...
    public String toString() {
        String myself = String.format( "%s, a %d-year-old %s", this.getName(), this.getAge(), this.getBreed() );
        return myself;
    }
    //...
}

Now, converting the object to a String will result in something more descriptive, such as:

Tobik, a 3-year-old German Shepherd

Try it!

The 4 Pillars of Object-Orientation

Concept

No university course would pass the censors unless it mentioned at least one set of immutable laws students must memorize despite their better judgment.

The four pillars of object-oriented programming:

  1. Abstraction

  2. Encapsulation

  3. Inheritance

  4. Polymorphism

Abstraction

abstract (adjective): disassociated from any specific instance

-Mirriam-Webster

Abstraction is thus the process of making something abstract or the state of being abstracted.

Encapsulation

encapsulate (verb): to enclose in or as if in a capsule

-Mirriam-Webster

Encapsulation is thus the process of enclosing some things within other things.

Inheritance

inherit (verb): to receive from an ancestor

-Mirriam-Webster

Inheritance is thus that which is inherited from an ancestor.

Polymorphism

polymorphism (noun): the quality or state of existing in or assuming different forms

-Mirriam-Webster

Alternative Paradigms

Free choice

There is currently no major threat to object-oriented programming, since it is so widespread. However, other programming paradigms do exist:

The future

Conclusions

You now have a basic understanding of object-oriented programming in Java. Well done.