Примеры реализации SOLID принципов

Примеры реализации SOLID принципов


Принцип единственной ответственности (Single Responsibility Principle):

// Плохо: Класс отвечает за хранение данных и вывод их на экран
public class Employee {
    private String name;
    private double salary;

    public Employee(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }

    public String getName() {
        return name;
    }

    public double getSalary() {
        return salary;
    }

    // Метод отвечает за вывод информации о сотруднике на экран
    public void printEmployeeDetails() {
        System.out.println("Name: " + name + ", Salary: " + salary);
    }
}

// Лучше: Класс только хранит данные о сотруднике
public class Employee {
    private String name;
    private double salary;

    public Employee(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }

    public String getName() {
        return name;
    }

    public double getSalary() {
        return salary;
    }
}

// Отдельный класс отвечает только за вывод информации о сотруднике на экран
public class EmployeePrinter {
    public void printEmployeeDetails(Employee employee) {
        System.out.println("Name: " + employee.getName() + ", Salary: " + employee.getSalary());
    }
}

Принцип открытости/закрытости (Open/Closed Principle):

// Плохо: Класс Employee имеет жесткую реализацию бонусной системы
public class Employee {
    private String name;
    private double salary;

    public Employee(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }

    public double calculateBonus() {
        return salary * 0.1; // Фиксированный процент бонуса
    }
}

// Лучше: Метод calculateBonus() вынесен в интерфейс, чтобы классы могли реализовать его по-разному
public interface BonusCalculator {
    double calculateBonus(double salary);
}

public class StandardBonusCalculator implements BonusCalculator {
    @Override
    public double calculateBonus(double salary) {
        return salary * 0.1; // Фиксированный процент бонуса
    }
}

public class Employee {
    private String name;
    private double salary;
    private BonusCalculator bonusCalculator;

    public Employee(String name, double salary, BonusCalculator bonusCalculator) {
        this.name = name;
        this.salary = salary;
        this.bonusCalculator = bonusCalculator;
    }

    public double calculateBonus() {
        return bonusCalculator.calculateBonus(salary);
    }
}

Принцип подстановки Барбары Лисков (Liskov Substitution Principle):

// Плохо: Подкласс не может полностью заменить суперкласс
public class Rectangle {
    protected int width;
    protected int height;

    public Rectangle(int width, int height) {
        this.width = width;
        this.height = height;
    }

    public void setWidth(int width) {
        this.width = width;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public int getArea() {
        return width * height;
    }
}

public class Square extends Rectangle {
    public Square(int size) {
        super(size, size);
    }

    @Override
    public void setWidth(int width) {
        super.setWidth(width);
        super.setHeight(width);
    }

    @Override
    public void setHeight(int height) {
        super.setHeight(height);
        super.setWidth(height);
    }
}

// Лучше: Использование интерфейса или создание абстрактного класса
public interface Shape {
    int getArea();
}

public class Rectangle implements Shape {
    protected int width;
    protected int height;

    public Rectangle(int width, int height) {
        this.width = width;
        this.height = height;
    }

    @Override
    public int getArea() {
        return width * height;
    }
}

public class Square implements Shape {
    private int side;

    public Square(int side) {
        this.side = side;
    }

    @Override
    public int getArea() {
        return side * side;
    }
}

Принцип разделения интерфейса (Interface Segregation Principle):

// Плохо: Клиентский класс должен реализовывать методы, которые ему не нужны
public interface Worker {
    void work();
    void eat();
}

public class Robot implements Worker {
    @Override
    public void work() {
        System.out.println("Robot is working");
    }

    @Override
    public void eat() {
        // Робот не может есть, но должен реализовать этот метод
    }
}

// Лучше: Интерфейс разделен на два более специфических интерфейса
public interface Workable {
    void work();
}

public interface Eatable {
    void eat();
}

public class Robot implements Workable {
    @Override
    public void work() {
        System.out.println("

Принцип инверсии зависимостей (Dependency Inversion Principle):

// Низкоуровневый модуль (модуль низкого уровня), который отвечает за доступ к данным
public interface Database {
    void saveData(String data);
}

public class MySQLDatabase implements Database {
    @Override
    public void saveData(String data) {
        // Логика сохранения данных в MySQL базе данных
        System.out.println("Saving data to MySQL: " + data);
    }
}

public class MongoDBDatabase implements Database {
    @Override
    public void saveData(String data) {
        // Логика сохранения данных в MongoDB базе данных
        System.out.println("Saving data to MongoDB: " + data);
    }
}

// Высокоуровневый модуль (модуль высокого уровня), который зависит от абстракции Database
public class DataManager {
    private final Database database;

    // Конструктор, внедряющий зависимость через интерфейс Database
    public DataManager(Database database) {
        this.database = database;
    }

    // Метод для сохранения данных
    public void saveData(String data) {
        database.saveData(data);
    }
}

// Использование
public class Main {
    public static void main(String[] args) {
        // Внедрение зависимости через конструктор
        Database mysqlDatabase = new MySQLDatabase();
        DataManager dataManager = new DataManager(mysqlDatabase);

        // Сохранение данных
        dataManager.saveData("Some data to save");
    }
}

В этом примере:

  • Существует интерфейс Database, представляющий абстракцию для доступа к базе данных.
  • Есть два низкоуровневых модуля MySQLDatabase и MongoDBDatabase, которые реализуют интерфейс Database и отвечают за сохранение данных в соответствующих базах данных.
  • Класс DataManager, который представляет высокоуровневый модуль, зависит от абстракции Database и использует эту зависимость для сохранения данных, но не зависит напрямую от конкретных реализаций баз данных.
  • Приложение в точке входа Main создает объект MySQLDatabase и передает его в конструктор DataManager, что позволяет использовать MySQL базу данных для сохранения данных.

Report Page