SOLID - это акроним пяти ключевых принципов объектно-ориентированного программирования и проектирования, которые помогают создавать поддерживаемый и масштабируемый код. Разберем каждый принцип подробно с примерами на C++.
Определение: Класс должен иметь только одну причину для изменения, то есть только одну ответственность.
Пример нарушения SRP:
class Report {
public:
void generateReport() {
// генерация отчета
}
void saveToFile(const std::string& filename) {
// сохранение в файл
}
};
Проблема: Класс Report отвечает и за генерацию отчета, и за его сохранение.
Исправленный вариант:
class ReportGenerator {
public:
std::string generate() {
// генерация отчета
return reportData;
}
};
class ReportSaver {
public:
void save(const std::string& report, const std::string& filename) {
// сохранение в файл
}
};
Определение: Программные сущности должны быть открыты для расширения, но закрыты для модификации.
Пример нарушения OCP:
class Rectangle {
public:
double width;
double height;
};
class AreaCalculator {
public:
double calculate(Rectangle& rect) {
return rect.width * rect.height;
}
// При добавлении нового класса Circle придется изменять AreaCalculator
};
Исправленный вариант:
class Shape {
public:
virtual double area() const = 0;
};
class Rectangle : public Shape {
public:
double area() const override { return width * height; }
// ...
};
class Circle : public Shape {
public:
double area() const override { return 3.14 * radius * radius; }
// ...
};
Определение: Объекты в программе должны быть заменяемыми на экземпляры их подтипов без изменения правильности программы.
Пример нарушения LSP:
class Rectangle {
public:
virtual void setWidth(int w) { width = w; }
virtual void setHeight(int h) { height = h; }
// ...
};
class Square : public Rectangle {
public:
void setWidth(int w) override {
width = height = w; // Нарушение LSP - изменяет и высоту
}
// ...
};
Проблема: Квадрат не может быть подтипом прямоугольника, так как его поведение отличается.
Определение: Клиенты не должны зависеть от интерфейсов, которые они не используют.
Пример нарушения ISP:
class IMachine {
public:
virtual void print() = 0;
virtual void scan() = 0;
virtual void fax() = 0;
};
class Printer : public IMachine {
// Должен реализовывать scan() и fax(), хотя они ему не нужны
};
Исправленный вариант:
class IPrinter {
public:
virtual void print() = 0;
};
class IScanner {
public:
virtual void scan() = 0;
};
class Printer : public IPrinter {
void print() override { /* ... */ }
};
Определение: Модули верхнего уровня не должны зависеть от модулей нижнего уровня. Оба должны зависеть от абстракций.
Пример нарушения DIP:
class LightBulb {
public:
void turnOn() { /* ... */ }
};
class Switch {
private:
LightBulb bulb;
public:
void operate() {
bulb.turnOn();
}
};
Исправленный вариант:
class Switchable {
public:
virtual void turnOn() = 0;
virtual void turnOff() = 0;
};
class LightBulb : public Switchable {
void turnOn() override { /* ... */ }
void turnOff() override { /* ... */ }
};
class Switch {
private:
Switchable& device;
public:
Switch(Switchable& device) : device(device) {}
void operate() {
device.turnOn();
}
};
SOLID принципы помогают создавать:
Применение этих принципов особенно важно в крупных проектах на C++, где сложность кода может быстро расти. Хотя иногда кажется, что следование SOLID увеличивает объем кода, в долгосрочной перспективе это окупается за счет снижения стоимости поддержки и модификации.