Course program← Объектно ориентированное программирование
1История ООП
2Объект и Класс
3Базовые концепции ООП
4Принцип проектирования GRASP
5Принцип проектирования SOLID
6Паттерны GOF
7Методические материалы

Abstraction

This lesson was translated automatically – it may contain errors.

Introduction

Abstraction is not simple: it is the only item in the «big four» that people still argue belongs there at all. When the paradigm was first taking shape there were three concepts – encapsulation, inheritance, polymorphism. Abstraction was added to them later, already in textbooks and at interviews.

The sceptics’ objection is simple: abstraction is not a language mechanism but a way of thinking. Encapsulation is written with an access modifier, inheritance with a colon, polymorphism with an interface or a virtual method. And what is abstraction written with? Nothing in particular: it shows up in which classes you introduced and what you decided not to show outside. We already touched on this in the lesson about the engineer – there abstraction was named the key skill of a designer, and no keyword for it turned up.

It is still worth taking apart: it comes up at interviews, it appears in the literature, and the notion is useful on its own. Just keep in mind that it is more a design principle than a language mechanism.

Definition: abstraction is focusing on the essential characteristics of an object while ignoring the details.

  • Visible: the buttons for choosing a coffee.

  • Hidden: the grinding mechanism, the water heating.

  • Abstraction: «What does it do?»

  • Encapsulation: «How is it protected?»

An example

using System;
using System.Reflection;

public class CoffeeMachine
{
    private bool _coffeeIsPrepared = false;
    private bool _waterIsBoiled = false;

    public bool MakeCoffee()
    {
        GrindCoffee();
        BoilWater();
        return _coffeeIsPrepared && _waterIsBoiled;
    }

    private void GrindCoffee()
    {
        _coffeeIsPrepared = true;
    }

    private void BoilWater()
    {
        _waterIsBoiled = true;
    }
}

class Program
{
    static void Main()
    {
        CoffeeMachine coffeeMaker = new CoffeeMachine();
        
        Console.WriteLine(coffeeMaker.MakeCoffee()); // Prints: True
    }
}
#include <iostream>

class CoffeeMachine {
public:
    // Only this method sticks out
    bool MakeCoffee() {
        GrindCoffee();
        BoilWater();

        return coffee_is_prepared_ && water_is_boiled_;
    }

private:
    bool coffee_is_prepared_ = false;
    bool water_is_boiled_ = false;

    void GrindCoffee() { coffee_is_prepared_ = true; }
    void BoilWater() { water_is_boiled_ = true; }
};

int main() {
    CoffeeMachine coffee_maker;

    std::cout << std::boolalpha << coffee_maker.MakeCoffee() << std::endl; // true

    return 0;
}
package main

import "fmt"

// Only MakeCoffee sticks out: methods with a lowercase letter
// are invisible outside the package.
type CoffeeMachine struct {
	coffeeIsPrepared bool
	waterIsBoiled    bool
}

func (m *CoffeeMachine) MakeCoffee() bool {
	m.grindCoffee()
	m.boilWater()

	return m.coffeeIsPrepared && m.waterIsBoiled
}

func (m *CoffeeMachine) grindCoffee() { m.coffeeIsPrepared = true }

func (m *CoffeeMachine) boilWater() { m.waterIsBoiled = true }

func main() {
	coffeeMaker := &CoffeeMachine{}

	fmt.Println(coffeeMaker.MakeCoffee()) // Prints: true
}
class CoffeeMachine:
    def __init__(self):
        self.__coffee_is_prepared = False
        self.__water_is_boiled = False

    # Only this method sticks out
    def make_coffee(self):
        self.__grind_coffee()
        self.__boil_water()
        return self.__coffee_is_prepared and self.__water_is_boiled

    def __grind_coffee(self):
        self.__coffee_is_prepared = True

    def __boil_water(self):
        self.__water_is_boiled = True

if __name__ == "__main__":
    coffee_maker = CoffeeMachine()

    print(coffee_maker.make_coffee())  # Prints: True
class CoffeeMachine {
    #coffeeIsPrepared = false;
    #waterIsBoiled = false;

    makeCoffee(): boolean {
        this.#grindCoffee();
        this.#boilWater();

        return this.#coffeeIsPrepared && this.#waterIsBoiled;
    }

    #grindCoffee(): void {
        this.#coffeeIsPrepared = true;
    }

    #boilWater(): void {
        this.#waterIsBoiled = true;
    }
}

const coffeeMaker = new CoffeeMachine();

console.log(coffeeMaker.makeCoffee()); // Prints: true
public class CoffeeMachine {
    private boolean coffeeIsPrepared = false;
    private boolean waterIsBoiled = false;

    public boolean makeCoffee() {
        grindCoffee();
        boilWater();

        return coffeeIsPrepared && waterIsBoiled;
    }

    private void grindCoffee() {
        coffeeIsPrepared = true;
    }

    private void boilWater() {
        waterIsBoiled = true;
    }
}

public class Program {
    public static void main(String[] args) {
        CoffeeMachine coffeeMaker = new CoffeeMachine();

        System.out.println(coffeeMaker.makeCoffee()); // Prints: true
    }
}
class CoffeeMachine {
    private var coffeeIsPrepared = false
    private var waterIsBoiled = false

    fun makeCoffee(): Boolean {
        grindCoffee()
        boilWater()

        return coffeeIsPrepared && waterIsBoiled
    }

    private fun grindCoffee() {
        coffeeIsPrepared = true
    }

    private fun boilWater() {
        waterIsBoiled = true
    }
}

fun main() {
    val coffeeMaker = CoffeeMachine()

    println(coffeeMaker.makeCoffee()) // Prints: true
}

What is happening here. From outside the coffee machine has exactly one button – the method MakeCoffee. Inside it does two things: grinds the beans and boils the water, and both are hidden in private methods.

Whoever is making coffee cares only about the result: press the button, get a drink. The grind, the water temperature, the order of the steps – details we deliberately turned away from. They can be changed, cup warming or cleaning can be added to them – and not a single line of the calling code will ever know.

That is abstraction: we left what the machine does on the outside and removed how it does it from view. You can get this wrong in both directions – expose too much (then somebody else’s code starts depending on the grind) or hide too much (then the coffee machine becomes unusable).

The four principles side by side

The principles of OOP form the basis of quality code:

Principle Benefit Example
Inheritance Class hierarchies, reuse Vehicle → Truck
Polymorphism A single interface for different objects The «On» button
Encapsulation Protecting data An ATM
Abstraction Simplifying complex systems A coffee machine