This article is part of a series of six articles:
- Multi-paradigm programming
- Declarative Computation
- Functional Programming
- Explicit state
- Object-oriented Programming
- Program design
Object-oriented programming is based on the declarative computation model with explicit state, together known as the stateful model. It introduces one new programming technique called Inheritance.
Object-oriented languages provide linguistic support for inheritance by adding classes as a linguistic abstraction. The combination of encapsulating explicit state and inheritance has led to the field of OOP, which can be seen as a way to structure stateful programs.
With explicit state we have gained a way for our functions to “learn from the past”. If we encapsulate explicit state into procedures we usually call them objects. The only way to modify the internal state of an object should be by calling its methods. This means that the object can guarantee that the state always satisfies some invariant property.
Scala directly provides an `object` abstraction, but it really does not matter, it is just a record type with some syntactic sugar.
You might ask yourself what happens when you need multiple instances of `Counter` to count multiple things. It would be nice to have a factory that can make as many counters as we need. Such a factory is called a `class`.
A class is an entity that specifies an object by defining the classes that the object inherits from and how the class is different from its ancestors.
A class defines an object’s internal state (attributes) and it’s behaviour (methods). There can be any number of objects of a given class, called instances.
Properties, also called Modifiers, modify how objects behave and influence inheritance. Examples properties are `final` (restricts classes from being extended with inheritance), `private` and `public` (modify the visibility of your attributes and methods).
Contructors are procedures that enable the customization of objects by providing custom attributes per class instantiation.
Inheritance is a way to construct new classes from existing classes. It defines what attributes and methods are available in the new class. There are two requirements for inheritance to be legal. First, the relation has to be directed and acyclic. The following is not allowed:
Second, every method and attribute, excluding overridden and overloaded methods, must have a unique name and must be defined only once in the hierarchy.
There are two ways to view inheritance:
- The type view: In this view classes are types and subclasses are subtypes. The type view is consistent with the principle that classes should model real-world entities or some abstract versions of them, i.e. abstract data types. In the type view classes have to satisfy the substitution principle. Every operation that works for a class `C` also works for a subclass of `C`.
- The structure view: In this view inheritance is just another programming tool used to structure programs. This view is strongly discouraged because classes no longer satisfy the substitution principle, which leads to an almost unending source of bugs and bad designs.
Multiple inheritance is useful when an object has to be two different things in the same program. In some languages multiple inheritance is possible through so called mixins or traits. Mixins and traits enable you to reuse common attributes or methods in other data structures. Multiple inheritance has to be used with care: it works well when combining two completely independent abstractions.
If a system is reflective if it can inspect part of its execution state while it is running. Reflection can be purely introspective (only reading the internal state) or intrusive (reading and modifying the internal state). Because of its richness, OOP is a particularly fertile area for reflection. Reflection can be useful for many purposes, e.g. debugging or customisation. Another important use case of reflection is the ability to inspect if an element is of class `x` or class `y`.
Object-oriented programming is a powerful tool that makes stateful programming more expressive. However the expressiveness comes at a price: reasoning about your program will get harder. Getting inheritance right is harder than you might think and there are certain principles that you should follow.
Purists of object-oriented programming will overuse stateful constructs even though they might not be needed and therefore should not be used. Programs written in an purely object-oriented fashion are also very susceptible to bugs introduced by inheritance.