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

Абстракция

Введение

С абстракцией всё непросто: это единственный пункт «большой четвёрки», о котором до сих пор спорят, входит ли он в неё вообще. Когда парадигма только складывалась, концепций было три – инкапсуляция, наследование, полиморфизм. Абстракцию к ним добавили позже, уже в учебниках и на собеседованиях.

Возражение у скептиков простое: абстракция – это не механизм языка, а способ мышления. Инкапсуляцию пишут модификатором доступа, наследование – двоеточием, полиморфизм – интерфейсом или виртуальным методом. А чем пишется абстракция? Ничем конкретным: она проявляется в том, какие классы вы завели и что решили не показывать наружу. Это, кстати, мы уже разбирали в уроке про инженера – там абстракция названа главным навыком проектировщика, и никакого ключевого слова для неё не нашлось.

Тем не менее разобрать её стоит: спрашивают на собеседованиях, встречается в литературе, да и понятие полезное само по себе. Просто держите в голове, что это скорее принцип проектирования, чем механизм языка.

Определение: Абстракция – фокусировка на существенных характеристиках объекта с игнорированием деталей.

  • Видимо: кнопки выбора кофе.

  • Скрыто: механизм помола, нагрев воды.

  • Абстракция: «Что делает?»

  • Инкапсуляция: «Как защищено?»

Пример

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()); // Выведет: True
    }
}
#include <iostream>

class CoffeeMachine {
public:
    // Наружу торчит только этот метод
    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"

// Наружу торчит только MakeCoffee: методы с маленькой буквы
// не видны за пределами пакета.
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()) // Выведет: true
}
class CoffeeMachine:
    def __init__(self):
        self.__coffee_is_prepared = False
        self.__water_is_boiled = False

    # Наружу торчит только этот метод
    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())  # Выведет: 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()); // Выведет: 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()); // Выведет: 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()) // Выведет: true
}

Что здесь происходит. Снаружи у кофемашины ровно одна кнопка – метод MakeCoffee. Внутри он делает две вещи: мелет зерно и кипятит воду, и обе спрятаны в закрытых методах.

Тому, кто варит кофе, важен только результат: нажал – получил напиток. Помол, температура воды, порядок шагов – детали, от которых мы сознательно отвернулись. Их можно менять, добавлять к ним подогрев чашки или очистку – и ни одна строчка вызывающего кода об этом не узнает.

В этом и абстракция: мы оставили снаружи что машина делает, а как она это делает, убрали с глаз. Ошибиться здесь можно в обе стороны – вынести наружу лишнее (тогда чужой код начнёт зависеть от помола) или спрятать нужное (тогда кофемашиной невозможно будет пользоваться).

Четыре принципа рядом

Принципы ООП создают основу для качественного кода:

Принцип Преимущество Пример
Наследование Иерархии классов, повторное использование Автомобиль → Грузовик
Полиморфизм Единый интерфейс для разных объектов Кнопка «Включить»
Инкапсуляция Защита данных Банкомат
Абстракция Упрощение сложных систем Кофемашина