Skip to content
Part IA Michaelmas Term

Why Object-Oriented Programming?

The Real Cost of Software

The dominant cost in software is not writing it — it is maintaining it. Studies consistently show that 60–80% of a software system’s total lifetime cost goes into maintenance: fixing bugs, adding features, adapting to new requirements, and understanding what the original authors intended. Code is read far more often than it is written. The primary goal of good software design is therefore to maximise maintainability.

The Maintainability Wishlist

Well-engineered software should be:

  • Readable: another developer (or your future self) can understand the code quickly without reconstructing intent from implementation details
  • Modular: the system is divided into independent, interchangeable components that can be developed, tested, and understood in isolation
  • Reusable: common functionality is written once and used everywhere, avoiding duplication that leads to inconsistent bug fixes
  • Robust: the system handles unexpected inputs and states gracefully, without crashing or corrupting data
  • Testable: individual components can be verified independently, ideally through automated unit tests

What OOP Brings

Object-oriented programming organises programs as collections of interacting objects. Each object bundles together:

  • State (fields, attributes, data members): the values the object knows about itself
  • Behaviour (methods, member functions, operations): the things the object can do

This bundling — called encapsulation — means that an object’s internal data can only be manipulated through its own methods. The object presents a controlled interface to the outside world while keeping its implementation details private.

Objects interact by sending messages to each other — in Java, this means calling methods on object references. A program written in OOP style is a society of cooperating objects, each with a clearly defined role.

Two Important Warnings from the Course

Few Languages Are Pure OOP

Java is not a purely object-oriented language. It has:

  • Primitive types (int, double, boolean, etc.) that are not objects and live outside the object model
  • Static methods and fields that belong to classes rather than instances
  • Lambda expressions and functional interfaces (since Java 8) that introduce functional-programming patterns

This is not a flaw — it is a pragmatic choice. Primitives exist for performance; static methods exist because not every operation naturally belongs to an object instance. Real-world languages hybridise paradigms.

OOP Is Not Always Appropriate

OOP is a tool, not a religion. It is not the best fit for every problem:

  • Small scripts (a few hundred lines of procedural logic) gain little from class hierarchies and design patterns
  • Heavily numerical code (scientific computing, signal processing) often expresses more naturally in procedural or array-oriented styles where the focus is on transforming data rather than modelling entities
  • Pure data pipelines (extract, transform, load) may be better served by functional approaches

The course teaches OOP as one paradigm among several. OCaml is taught in Foundations of Computer Science (FoCS) as the functional counterpart, and the two courses are designed to complement each other.

Benefits and Costs

Benefits

  • Models the problem domain more naturally when the domain contains entities with state and behaviour
  • Encapsulation limits the ripple effect of changes
  • Polymorphism lets new behaviour be added without modifying existing code
  • Inheritance enables code reuse through shared interfaces and implementations

Costs

  • Boilerplate: Java requires explicit class declarations, getters, setters, and constructor code even for simple data carriers
  • Indirection: method calls through interfaces and abstract types add layers that make control flow harder to trace
  • Over-engineering risk: applying design patterns and abstractions before they are needed adds complexity without benefit

Encapsulation, Inheritance, and Polymorphism

These three concepts are often presented as the pillars of OOP, but the course frames them differently: they are tools in service of the maintainability goals, not goals in themselves.

  • Encapsulation serves modularity and robustness by hiding internal state behind a stable public interface
  • Inheritance serves reusability by letting classes share implementation code through an “is-a” hierarchy — but used carelessly it creates brittle, tightly coupled designs
  • Polymorphism serves readability and testability by letting code work with any type that satisfies an interface, rather than being hard-coded to specific concrete types

A well-designed OOP system uses these tools judiciously. A badly designed one uses them because it feels they ought to be used. The measure of success is the maintainability wishlist, not the presence of design pattern vocabulary.