Object-oriented Programming (OOP)

Introduction

Object-oriented programming (or design) makes use of:
  • Modularity: e.g. reusable components, class libraries, frameworks
  • Inheritance / Layering / Abstractions: e.g. High-level programming language
  • Patterns
In order to be object-oriented there are a couple of must haves:
  • Objects
  • Inheritance
  • Dynamic Binding
  • Classes (nice-to-have)
  • Garbage collection (nice-to-have)

Objects

An object is an instance of an abstract data type. It has structural (e.g. state, set of possible values) and functional properties (e.g. reacts to external requests). They must be uniquely identifiable. The abstract data type is implicit (no abstraction). Objects represent values. Creating objects without explicit types (explicit abstraction) is unsustainable because the object definition cannot be reused and factorization is limited. Manually adapting individual objects leads to code that is hardly maintainable. This violates two principles that essentially mean the same thing:
Factorization: A fact (e.g., method / procedure / constant) is defined only once in a software.
DRY: Don't Repeat Yourself
Whereas the issue of values vs. types is a philosophical one, typed universes are generally easier to handle because the abstraction reduces the complexity. Additionally, (explicit) types enforce reasoning about similarities and differences of objects. Classification and specification of objects is only possible with (explicit) types.

Classes

A class is an abstract description of an object. It is like a template / blueprint for creating objects. It consists of:
  • Structural properties (state):
  • Individual State (Instance Variables)
  • Shared state (class variables)
  • Functional properties (behavior)
  • Instance methods
  • Class methods
Classes (types) define common properties of their instances. Instead of "classes" as a language construct, prototypes and delegation can also be used. To capture commonalities and differences a type hierarchy is necessary that can be used to model relations among types:
  • Specialization: downwards along the type hierarchy, e.g. person -> teacher, student
  • Generalization: upwards along the type hierarchy, e.g., student, teacher -> person
One can distinguish static and dynamic typing:
  • Static type: defined by declaration, cannot be changed
  • Dynamic type: defined by the type of a referenced object at run-time

Inheritance

Inheritance (i.e., subtyping) allows the definition of new types based on existing ones without copying. State and behaviour can be added and existing behaviour can be modified. Only this allows the reuse and flexible adaptation to new contexts (components without adaptability are hardly reusable)

Dynamic Binding

Dynamic binding is a kind of polymorphism that allows the look-up of the appropriate method according to the dynamic type (at runtime). Usually several implementations of a method exists (e.g., along the type hierarchy) and dynamic binding will select the appropriate one. The name of the method can be defined in the (abstract) base class or interface. Derived classes can then change the behaviour of said method without requiring changes in the implementation. It also enables substitutability: Objects of a base class can be replaced by objects of a derived class.
Liskov Substitution Principle
An object of class T can always be substituted by an object of class T' where T's is derived from T.

Garbage Collection

At run-time a large number of objects are generated. However, most of them are released (not used anymore) shortly (e.g., local variables). One approach to free up memory would be to explicitly release them. However, this is error-prone because a global view is required (Am I sure that this object is not used anymore elsewhere in the code?) The garbage collector addresses this issue. However, reliability and efficiency are negatively correlated i.e. a 100% reliable garbage collector will be slow whereas a 100% efficient garbage collector will not be as reliable.
  • Advantages: enhanced reliability, less effort for implementation, testing and maintenance
  • Disadvantages: run-time and memory impact, limited suitability for realtime systems, integration into systems without garbage collection is difficult