Примеры реализации 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 базу данных для сохранения данных.