Java for Beginner
743 subscribers
707 photos
196 videos
12 files
1.14K links
Канал от новичков для новичков!
Изучайте Java вместе с нами!
Здесь мы обмениваемся опытом и постоянно изучаем что-то новое!

Наш YouTube канал - https://www.youtube.com/@Java_Beginner-Dev

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Основы ООП в Java

Глава 7. Принципы проектирования и хорошего кода

SOLID

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

1. Принцип единственной ответственности (S — Single Responsibility Principle)

Этот принцип гласит, что каждый класс или модуль в программе должен отвечать только за одну задачу или иметь только одну причину для изменения. Иными словами, класс не должен заниматься несколькими несвязанными вещами сразу. Если класс делает слишком много, он становится сложным, и изменения в одной части могут сломать другую.

Почему важно: Если класс отвечает за несколько задач, то при изменении одной из них (например, из-за новых требований бизнеса) вы рискуете сломать другие части. Это приводит к ошибкам и усложняет поддержку кода.

Подробное объяснение: Представьте, что класс одновременно читает данные из файла, обрабатывает их и выводит на экран. Если изменится формат файла, вам придется менять весь класс, даже если обработка и вывод не изменились. Лучше разделить: один класс для чтения, другой для обработки, третий для вывода.

Пример нарушения принципа:
public class Report {
// Нарушение: Класс делает всё сразу
public void readDataFromFile(String fileName) {
// Код для чтения данных
}

public void processData() {
// Код для обработки данных
}

public void printReport() {
// Код для печати отчета
}
}

Здесь класс имеет три ответственности. Если изменится способ чтения файлов, придется трогать весь класс.


Пример правильного применения:
public class DataReader {
public String readDataFromFile(String fileName) {
// Только чтение данных
return "Данные из файла";
}
}

public class DataProcessor {
public String processData(String data) {
// Только обработка
return "Обработанные данные: " + data;
}
}

public class ReportPrinter {
public void printReport(String processedData) {
// Только печать
System.out.println(processedData);
}
}

Теперь каждый класс имеет одну ответственность. Изменение в чтении не затронет печать.


Совет: Задайте вопрос: "Сколько причин для изменения у этого класса?" Если больше одной — разделите.


2. Принцип открытости-закрытости (O — Open-Closed Principle)

Этот принцип утверждает, что классы и модули должны быть открыты для расширения (то есть, вы можете добавлять новое поведение), но закрыты для изменения (то есть, существующий код не должен меняться при добавлении новых функций). Другими словами, чтобы добавить новую возможность, вы расширяете систему, не трогая старый код.

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

Подробное объяснение: Расширение происходит через наследование, интерфейсы или композицию. Например, если у вас есть класс для фигур, добавление новой фигуры не должно менять код для существующих.



#Java #для_новичков #beginner #OOP #SOLID
👍4🔥1
Пример нарушения принципа:
public class ShapeCalculator {
public double calculateArea(Object shape) {
if (shape instanceof Circle) {
// Код для круга
return Math.PI * ((Circle) shape).getRadius() * ((Circle) shape).getRadius();
} else if (shape instanceof Square) {
// Код для квадрата
return ((Square) shape).getSide() * ((Square) shape).getSide();
}
// Если добавить треугольник, придется менять метод
return 0;
}
}

Добавление новой фигуры требует изменения метода, что нарушает принцип.


Пример правильного применения:Используем абстракцию и полиморфизм.

public abstract class Shape {
public abstract double calculateArea(); // Метод для расширения
}

public class Circle extends Shape {
private double radius;

public Circle(double radius) {
this.radius = radius;
}

@Override
public double calculateArea() {
return Math.PI * radius * radius;
}
}

public class Square extends Shape {
private double side;

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

@Override
public double calculateArea() {
return side * side;
}
}

// Для новой фигуры просто добавьте класс, без изменения старого кода

Теперь ShapeCalculator может быть:
public class ShapeCalculator {
public double calculateArea(Shape shape) {
return shape.calculateArea(); // Полиморфизм, закрыт для изменений
}
}

Добавление треугольника — новый класс extends Shape, без изменений в калькуляторе.


Совет: Используйте абстрактные классы или интерфейсы для "открытости", а переопределение — для расширения.



3. Принцип подстановки Лисков (L — Liskov Substitution Principle)

Этот принцип, предложенный Барбарой Лисков, гласит, что объекты подкласса должны быть способны заменять объекты суперкласса без изменения правильности программы. Иными словами, подкласс не должен нарушать ожидания, установленные суперклассом.

Почему важно: Если подкласс меняет поведение суперкласса неожиданным образом, код, использующий суперкласс, может сломаться при подстановке подкласса. Это нарушает полиморфизм.

Подробное объяснение: Подкласс должен сохранять контракты суперкласса: предусловия (входные данные) не ужесточаются, постусловия (выход) не ослабляются, инварианты (постоянные свойства) сохраняются.


Пример нарушения принципа:
Представьте суперкласс Bird (Птица) с методом fly() (летать). Подкласс Penguin (Пингвин) переопределяет fly() и бросает исключение, потому что пингвины не летают. Тогда код Bird bird = new Penguin(); bird.fly(); сломается, хотя ожидалось, что любая птица может летать.
public class Bird {
public void fly() {
System.out.println("Летаю!");
}
}

public class Penguin extends Bird {
@Override
public void fly() {
throw new UnsupportedOperationException("Пингвины не летают!");
}
}

Это нарушение: подстановка пингвина ломает код.


Пример правильного применения:
Лучше разделить: суперкласс Animal, интерфейс Flyable для летающих. Пингвин extends Animal, но не implements Flyable.
public class Animal {
// Общие методы
}

public interface Flyable {
void fly();
}

public class Eagle extends Animal implements Flyable {
@Override
public void fly() {
System.out.println("Орел летает высоко!");
}
}

public class Penguin extends Animal {
// Нет fly(), так что не нарушает
}

Теперь код работает только с Flyable, без неожиданностей.


Совет: Проверяйте: "Можно ли заменить суперкласс подклассом без проблем?" Если нет — пересмотрите дизайн.


#Java #для_новичков #beginner #OOP #SOLID
👍4🔥1
4. Принцип разделения интерфейсов (I — Interface Segregation Principle)

Этот принцип говорит, что интерфейсы должны быть маленькими и специализированными, а не большими и универсальными. Клиенты (классы, использующие интерфейс) не должны зависеть от методов, которые они не используют.

Почему важно: Большой интерфейс заставляет классы реализовывать ненужные методы, что приводит к "пустым" реализациям и усложняет код.

Подробное объяснение: Лучше несколько маленьких интерфейсов, чем один большой. Это делает систему модульной и легче для понимания.


Пример нарушения принципа:
public interface Worker {
void work(); // Для всех
void eat(); // Только для людей
void charge(); // Только для роботов
}

Класс Human реализует charge() бесполезно, Robot — eat().


Пример правильного применения:

Разделите на маленькие интерфейсы.
public interface Workable {
void work();
}

public interface Eatable {
void eat();
}

public interface Chargeable {
void charge();
}

public class Human implements Workable, Eatable {
@Override
public void work() { /*...*/ }

@Override
public void eat() { /*...*/ }
}

public class Robot implements Workable, Chargeable {
@Override
public void work() { /*...*/ }

@Override
public void charge() { /*...*/ }
}

Теперь классы реализуют только нужное.


Совет: Интерфейс должен быть "толстым" для клиента, но "тонким" для реализатора — маленькие интерфейсы.


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


Этот принцип утверждает, что высокоуровневые модули (бизнес-логика) не должны зависеть от низкоуровневых (детали реализации), а оба должны зависеть от абстракций (интерфейсов). Абстракции не зависят от деталей, детали зависят от абстракций.

Почему важно: Это уменьшает связь между частями системы, делая код легче для изменения и тестирования (например, замена базы данных без изменения бизнес-логики).

Подробное объяснение: Используйте интерфейсы или абстрактные классы как "прослойку". Высокий уровень зависит от интерфейса, низкий — реализует его.


Пример нарушения принципа:
public class ReportService {
private MySQLDatabase db = new MySQLDatabase(); // Зависит от конкретной реализации

public void generateReport() {
db.connect();
// ...
}
}

Смена базы данных требует изменения сервиса.


Пример правильного применения:
public interface Database {
void connect();
// ...
}

public class MySQLDatabase implements Database {
@Override
public void connect() { /*...*/ }
}

public class ReportService {
private Database db; // Зависит от абстракции

public ReportService(Database db) { // Инъекция зависимости
this.db = db;
}

public void generateReport() {
db.connect();
// ...
}
}

Теперь можно передать любую реализацию: new ReportService(new MySQLDatabase()) или new ReportService(new PostgreSQLDatabase()).


Совет: Используйте dependency injection (внедрение зависимостей) для передачи абстракций.


Полезные советы для новичков

Применяйте SOLID постепенно: Начните с единственной ответственности, потом добавляйте другие.
Примеры в Java: Смотрите стандартную библиотеку — она следует
SOLID.
Ресурсы: Книга "Clean Code" Роберта Мартина для глубокого понимания.
Практика: Возьмите свой код и проверьте на соответствие каждому принципу.



#Java #для_новичков #beginner #OOP #SOLID
👍3🔥21