← Назад к вопросам

Что такое интерфейсы?

2.0 Middle🔥 131 комментариев
#ООП и проектирование#Язык C++

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI30 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое интерфейсы

Интерфейс — это контракт (договор), который определяет, какие методы должны быть реализованы в классе, но не определяет, как их реализовать. В C++ интерфейсы реализуются через абстрактные классы с виртуальными функциями.

Основная концепция

Интерфейс задаёт "что делать", а не "как делать". Это позволяет:

  • Писать полиморфный код
  • Разделять абстракцию от реализации
  • Менять реализацию без изменения клиентского кода

Синтаксис интерфейсов в C++

Определение интерфейса (чистый виртуальный класс):

// Logger.h - интерфейс
class ILogger {
public:
    virtual ~ILogger() = default;
    
    // Чистые виртуальные функции (=0)
    virtual void debug(const std::string& message) = 0;
    virtual void info(const std::string& message) = 0;
    virtual void warning(const std::string& message) = 0;
    virtual void error(const std::string& message) = 0;
    
private:
    // Обычно интерфейсы содержат только методы, без состояния
};

Основные правила:

  • Имя начинается с I по convention
  • Все методы virtual и = 0 (чистые виртуальные)
  • Виртуальный деструктор (обязателен!)
  • Без данных-членов (обычно)

Реализация интерфейса:

class ConsoleLogger : public ILogger {
public:
    void debug(const std::string& message) override {
        std::cout << "[DEBUG] " << message << std::endl;
    }
    
    void info(const std::string& message) override {
        std::cout << "[INFO] " << message << std::endl;
    }
    
    void warning(const std::string& message) override {
        std::cout << "[WARN] " << message << std::endl;
    }
    
    void error(const std::string& message) override {
        std::cout << "[ERROR] " << message << std::endl;
    }
};

class FileLogger : public ILogger {
private:
    std::ofstream file;
    
public:
    FileLogger(const std::string& filename) {
        file.open(filename, std::ios::app);
    }
    
    void debug(const std::string& message) override {
        file << "[DEBUG] " << message << std::endl;
    }
    
    void info(const std::string& message) override {
        file << "[INFO] " << message << std::endl;
    }
    
    void warning(const std::string& message) override {
        file << "[WARN] " << message << std::endl;
    }
    
    void error(const std::string& message) override {
        file << "[ERROR] " << message << std::endl;
    }
};

Использование интерфейсов

Полиморфизм через интерфейсы:

void process_data(ILogger& logger) {
    logger.info("Начинаем обработку");
    
    try {
        // Бизнес-логика
        logger.debug("Шаг 1");
        logger.debug("Шаг 2");
        
        logger.info("Обработка завершена");
    } catch (const std::exception& e) {
        logger.error(std::string("Ошибка: ") + e.what());
    }
}

int main() {
    // Используем разные реализации через один интерфейс
    ConsoleLogger console_logger;
    process_data(console_logger);
    
    FileLogger file_logger("app.log");
    process_data(file_logger);
    
    // Функция работает с обеими без изменений!
    return 0;
}

Сложные интерфейсы

Интерфейс для работы с коллекциями:

template<typename T>
class ICollection {
public:
    virtual ~ICollection() = default;
    
    virtual void add(const T& item) = 0;
    virtual void remove(const T& item) = 0;
    virtual bool contains(const T& item) const = 0;
    virtual size_t size() const = 0;
    virtual bool is_empty() const = 0;
    virtual void clear() = 0;
};

template<typename T>
class Vector : public ICollection<T> {
private:
    std::vector<T> data;
    
public:
    void add(const T& item) override {
        data.push_back(item);
    }
    
    void remove(const T& item) override {
        auto it = std::find(data.begin(), data.end(), item);
        if (it != data.end()) {
            data.erase(it);
        }
    }
    
    bool contains(const T& item) const override {
        return std::find(data.begin(), data.end(), item) != data.end();
    }
    
    size_t size() const override {
        return data.size();
    }
    
    bool is_empty() const override {
        return data.empty();
    }
    
    void clear() override {
        data.clear();
    }
};

Множественное наследование интерфейсов

Интерфейсы позволяют класс реализовывать несколько контрактов:

class IDrawable {
public:
    virtual ~IDrawable() = default;
    virtual void draw() = 0;
};

class IClickable {
public:
    virtual ~IClickable() = default;
    virtual void on_click(int x, int y) = 0;
};

class Button : public IDrawable, public IClickable {
public:
    void draw() override {
        std::cout << "Рисуем кнопку" << std::endl;
    }
    
    void on_click(int x, int y) override {
        std::cout << "Нажали кнопку в позиции (" << x << ", " << y << ")" << std::endl;
    }
};

Лучшие практики

  1. Используйте интерфейсы для абстракций — если несколько классов делают одно и то же по-разному

  2. Обязательно виртуальные деструкторы:

class IBase {
public:
    virtual ~IBase() = default;  // Очень важно!
};
  1. Используйте override — явно указывает, что метод переопределяет виртуальный
class Derived : public IBase {
public:
    void method() override {  // ✓ Компилятор проверит сигнатуру
        // ...
    }
};
  1. Dependency Injection — передавайте интерфейсы через параметры:
class Service {
private:
    std::unique_ptr<ILogger> logger;
    
public:
    Service(std::unique_ptr<ILogger> log) : logger(std::move(log)) {}
    
    void work() {
        logger->info("Работаю...");
    }
};

Различие: interface vs abstract class

АспектInterface (чистый)Abstract Class
МетодыТолько virtualvirtual + обычные
ДанныеНЕТМожно
НаследованиеМножественное OKОсторожнее с множественным
НазначениеКонтрактЧастичная реализация

Заключение

Интерфейсы — это фундамент объектно-ориентированного дизайна. Они позволяют писать гибкий, тестируемый код, где реализация отделена от абстракции. В высоконагруженных системах это критично для поддерживаемости и масштабируемости.