Learn Kotlin, Unit 3: Object- and function-oriented programming concepts and principles
Kotlin is both an object-oriented language and a functional programming language
In this unit, you will:
- Learn what an object is
- Learn what a class is
- Learn two types of object hierarchies
- Learn the object-oriented programming concepts of:
- And learn how to provide examples of each
- Learn the fundamental concepts of function-oriented programming
What is an object?
All data processing applications are solutions to business problems that take information (the data) and do something useful with it (the processing).
Kotlin applications are focused on objects rather than data structures because Kotlin is an object-oriented language.
Structured and other procedural programming languages stress data structures and the subroutines that act on them. There is no formal concept of visibility built into the language; any subroutine may act on any data structure or call any subroutine.
An object consists of state + behavior
State is comprised of the attributes of the object (the data) and is not exposed outside of the object. The object’s data is visible only to the program logic within the object.
Behavior is the program logic (the code) that acts on that state through an externally visible interface. The object’s interface consists of a select set of exposed program logic called methods that provides the only means of interacting with the object.
An object consists of state plus behavior.
Visibility is key
The concept of visibility is a key differentiator between structured programming and object-oriented programming languages. The ways in which objects may interact are limited (that is, through an object’s interface only), so object-oriented programming makes writing maintainable code easier.
Since the interface is the only way to interact with the object (we say “the interface must be obeyed”), working with objects is more straightforward than working with data structures that have no interface. The object’s state is not exposed outside of the object and this reduces the number of locations in the source code where the data is touched, resulting in fewer side effects and more maintainable code.
What is a class?
It’s difficult to talk about objects without also talking about classes.
Consider construction blueprints: detailed designs for houses, buildings, and other complicated physical structures. A class is like the blueprint for an object. Just like a house is built from a set of blueprints, an object is built, or instantiated, from its class. An object is an instance of a class.
Don’t worry. You’ll learn more about classes and objects throughout the course.
For the purposes of this discussion, consider two types of hierarchies:
- Taxonomy is based on one or more shared intrinsic properties.
- Collection is containment based on one or more extrinsic properties.
Think about biological classification for a moment (which, by the way, is where taxonomies were first used). For example, human beings (Homo sapiens) are mammals, which are primates, which are vertebrates, and so on.
Taxonomies are ways of organizing living things that share similar characteristics in a hierarchy where the similarities become more and more general as you move “up” the hierarchy and more specific as you move “down.” For example, cats and dogs are different (specific), but both are classified as mammals (general).
Our brains are really good at classifying things in taxonomies and some really smart people decided that data processing would be better if we had programming languages to support decomposing business problems using taxonomy.
Think of the relationship between constituents of a taxonomy as an “is a” relationship. For example, a dog “is a” mammal, a book “is a” publication, and a Tyrannosaurus “is a” reptile (although I suppose “was a” would better apply in the last example).
Collections are hierarchies of containment, organized by an arbitrary extrinsic rather than intrinsic set of properties in which one thing in the hierarchy contains one or more other things.
Consider a set of data on a disk, organized into directories and files. Directories can contain other directories and files. There is nothing intrinsic about the relationship; some agent (a person or a program) decided to organize them in a particular way. For example, you’re free to arrange (or rearrange) the files on your computer any way you see fit.
Another example is a company, comprised of divisions, which are made up of departments, which have teams, which have employees. Again, there is nothing intrinsic about these associations other than some agreed upon convention.
Think of the relationship between constituents of a collection as “has a” relationships. For example, a division “has a” department (or departments), a directory “has a” file (or files), and a database table “has a” record (or records).
At first, object-oriented analysis, design, and programming (collectively called OO) seem difficult to understand, rife with terms like “abstraction”, “encapsulation”, “polymorphism”, and “bokononism” (okay, I made that last one up).
It might seem like objects just makes things harder, but in reality, thinking of the world (and software in particular) in terms of objects actually makes it easier. Let me explain.
The best way to talk about these principles is to define them in ordinary, everyday language with examples. After all, an object is really just a “thing” (and we’re all familiar with things).
In a nutshell, OO involves these three principles:
- Encapsulation in which an object hides (encapsulates) its internal details
- Inheritance in which an object extends (via inheritance) the characteristics of ancestor objects to specialize its behavior
- Polymorphism in which objects that implement the same interface behave differently (take different forms) depending on the underlying implementation
I won’t talk about abstraction here. A good understanding of abstraction is necessary to do OO well, but not to understand the basics. Be sure you check out this link on abstraction if you want to learn more.
Encapsulation means the object hides its internal details behind an interface. This concept contains two important aspects:
- Information hiding
- Restricted access
Recall that an object has state and behavior: its state is not visible outside of the object and its behavior constitutes its interface.
From an object-oriented perspective, the object’s state is its information, hidden from the outside.
Since the object’s state is not exposed to the outside, the number of state transitions the object can undergo is limited, leading to fewer side-effects and potential bugs. The code that can touch the object is limited to the code within the object, so bugs are less likely to be introduced and are much easier to track down when they are because there is a fewer number of places in the code where the bug can be.
Interaction with the object from outside is restricted to its interface. The object’s behavior forms a sort of capsule, behind which the object’s internal workings are sealed off from outside. We say it is encapsulated.
The object’s behavior is determined by its interface, limiting the ways the object may be used. Since the object’s interface alone determines the behavior of the object, developers who write code to use the object know what to expect, making the object easier to use, test, and debug.
Think of an Automatic Teller Machine. As you can imagine, the machine itself includes complex circuitry, network communications technology, a currency reservoir, power connection, and so forth.
As a user, you have restricted access: a place to insert your ATM card, a keypad to enter your PIN, a touch screen with which to interact with the machine, a slot to retrieve currency, and so on, but the machine’s internals are hidden from you.
Your interface to the ATM is the only way with which you are allowed to interact with the machine; its internals are encapsulated. As you can imagine, if this were not the case and a user required a deep understanding of how an ATM’s internals worked to use one, it would be a useless consumer technology.
When an object extends the characteristics of another object to specialize its behavior, we say that the former inherits from the latter. Inheritance reduces the amount of code that has to be maintained.
Suppose an object’s interface does not provide all of the behavior you need from the object? You extend the original (parent) class, creating a new, specialized (child) class. The specialized child class reuses (via inheritance) the parts of the parent class that already do what you need and you only need to modify or add those parts that require specialization. The result is less code. The concept is called code reuse.
Inheritance creates an “is a” relationship
The specialized object inherits the characteristics of its predecessor, much like a child inherits traits from its parents. Parent and child objects form a taxonomy-style hierarchy since the child object is a type of its parent, like a dog is a mammal and our old friend Tyrannosaurus is a reptile.
Imagine there is an object representing a person. We can create a specialization of person called Employee, which inherits the traits of a person (such as Name) and adds traits like Employee Id and Hire Date.
We can create a Manager object that inherits from Employee and a Senior VP object that inherits from Manager. And so on.
Objects that implement the same interface behave differently (take different forms) depending on the underlying implementation. Let’s look at a simple example.
Imagine there is an interface called Shape with the single method draw. Now suppose there are several objects that implement this interface, hiding (encapsulating) the details behind their respective interfaces. Suppose one of those objects is Circle, another is Square, and a third is Triangle.
Now imagine that the software that uses these objects renders (draws) them on a display, and in the process is simply handed a Shape, unaware of the underlying Shape implementation.
Since all of the objects it deals with implement the Shape interface, the draw method can be invoked on each object, rendering the object according to its underlying implementation.
When a Circle is drawn, it looks very different from a Square, which looks different still from a Triangle.
This is polymorphism in action. Through a single interface — Shape — the behavior takes many (poly) forms (morphe), depending on the details of the underlying object.
Let’s face it, not every software system needs to be decomposed into a sea of objects. Imposing an object-oriented model onto a system that is better modeled using a function-oriented approach is a poor design choice and leads to design friction, bad implementations, bugs, and (worst of all) late projects.
Kotlin is an object-oriented language, but it also includes support for function-oriented programming, which has become popular in recent years.
Function-oriented programming is comprised of three fundamental principles:
- Immutability: Functions don’t change the state of the system
- First-class functions: Functions are defined and used at the same level as classes
- Functions are pure: Functions only take parameters, perform a computation, and return results
Let’s look at these in more detail.
An immutable object is one whose state cannot be changed. If the object needs to be modified, instead of modifying the object, a new object is created with a different state.
This has several benefits:
- Immutable objects are thread-safe: An object cannot be modified by one thread and read in another (which can lead to race conditions, heap pollution, and other nasty problems that are hard to track down)
- There are no side-effects: If an object is expected to behave a certain way based on its state and that state is changed, the change has introduced a side-effect because the object will now behave differently
A first-class function is one that can be passed as an argument to another function, returned from a function, and stored as a value.
In a functional programming language, functions are not just a construct of the language, but they can be stored and passed around just like any other object. This is sometimes called function as data or function as object.
Functions are pure
When we say “function” in the context of functional programming, we mean function in the mathematical sense (not “block of code we can call”).
Suppose we have some function
f(x) defined as:
f(x) = x + 3
This function can be used to calculate a result for any value of
x, independent of the state of the system. That is to say, for any given value of
x this function produces the same result
For example, suppose we call this function for
x = 2; the result would then be:
f(2) = 2 + 3 = 5
No matter how many times we call
f(x), it will return
5 so we say this function is a pure function.
In addition, a pure function does not modify the parameter values in any way, thus it has no side-effects.
In this unit:
- You learned how an object is made up of state plus behavior and that a class is a blueprint for an object
- You discovered the two types of object hierarchies — taxonomy and collection
- You encountered the three fundamental concepts of OO — inheritance, encapsulation, and polymorphism
- You got a real taste of the three fundamental concepts of functional programming — immutability, first-class functions, and pure functions
Test your understanding
True or False
Kotlin is an object-oriented language with support for functional programming.
Functional programming’s three principles are inheritance, pure functions, and lambda calculus.
An object consists of state, or attributes, and is another way of thinking of a data structure.
A pure function has no side-effects.
Polymorphism is from the Greek word meaning “many forms.”
Check your answers
2. False. Functional programming’s three principles are immutability, first-class functions, and pure functions.
3. False. An object consists of both state (which is like a data structure) and behavior.
What is encapsulation? Choose all that apply.
A. Hiding a function inside of the Linux kernel so it cannot be called
B. Hiding an object’s implementation behind an interface
C. Hiding an object’s state from any program logic outside of the object
D. Hiding an object so that it can only be instantiated under certain circumstances
What is a class (in object-oriented programming)? Choose all that apply.
A. A template for creating an object
B. Another term for first-class functions, which are used as object factories sometimes
C. Another term for category used in Lambda calculus
D. A blueprint for an object
E. A type of programming language used in graduate school mathematics
Which of the following are types of object hierarchies? Choose all that apply.
B. Cats and dogs
D. Functional programming
Which of the following do not accurately describe the fundamental difference between structured programming and object-oriented programming languages?
A. Structured programming languages use objects under the hood that it refers to as data structures, whereas object-oriented languages have no structure at all.
B. Object-oriented languages use first-class functions to manipulate data structures and return immutable objects, whereas structured programming languages deal with mutable objects.
C. Object-oriented languages do not have methods. Instead, you create objects, and then cause the system to respond by sending messages to their attributes. Structured programming languages, on the other hand, only have methods and data structures, but no attributes.
D. Structured programming languages use data structures and functions that are visible globally, whereas object-oriented languages use objects that encapsulate their state behind well-defined interfaces that define the objects’ behaviors.
Check your answers
1. B, C. Encapsulation is the principle of hiding information (state) and ways to interact with an object (behavior) behind an interface.
2. A, D. A class is a template or blueprint for creating an object.
3. C, E. Of the choices listed, only collection and taxonomy are types of object hierarchies.
4. D. Answer D summarizes the difference between structured programming and object-oriented programming languages.
Fill in the blank
Inheritance is when one class, called the _____________ class, extends the behavior of another, called the _____________ class.
The three principles of functional programming are _____________, _____________, and _____________ functions.
Check your answers
1. child, parent
2. immutability, first-class, pure