Зарегистрируйтесь, чтобы продолжить обучение

Паттерны Java: Основы ООП

Порождающие паттерны

Singleton (Одиночка)

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

public class SimpleSingleton {

    private final static SimpleSingleton INSTANCE = new SimpleSingleton();

    private SimpleSingleton() {}

    public static SimpleSingleton getInstance() {
        return INSTANCE;
    }
}
public class Main {
    public static void main(String[] args) {
        new SimpleSingleton(); // Ошибка. Конструктор SimpleSingleton() приватный

        var singletone1 = SimpleSingleton.getInstance();
        var singletone2 = SimpleSingleton.getInstance();

        // Это один и тот же объект
        System.out.println(singletone1 == singletone2); // true
    }
}

Ленивая разновидность паттерна Singleton — это вариация классического синглтона, которая создает экземпляр класса только при первом запросе, что может быть полезно для оптимизации ресурсов. В ленивом синглтоне экземпляр класса создается внутри статического метода, который проверяет, существует ли уже экземпляр, и если нет, создает его, что позволяет избежать создания экземпляра, если он не нужен.

public class LazySingleton {

    private static LazySingleton INSTANCE;

    private LazySingleton() {}

    public static LazySingleton getInstance() {

        if (INSTANCE == null) {
            INSTANCE = new LazySingleton();
        }

        return INSTANCE;
    }
}
public class Main {
    public static void main(String[] args) {

        var singletone1 = LazySingleton.getInstance();
        var singletone2 = LazySingleton.getInstance();

        // Это один и тот же объект
        System.out.println(singletone1 == singletone2); // true
    }
}

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

Builder (Строитель)

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

@Setter
@AllArgsConstructor
public class Computer {
    private int ram;
    private double cpu;
    private int ssd;
    private int hdd;
    private int power;
    private boolean hasCdDrive;
    private String videoAdapterType;
}

Отдельный класс билдера

public class ComputerBuilder {
    private int ram;
    private double cpu;
    private int ssd;
    private int hdd;
    private int power;
    private boolean hasCdDrive;
    private String videoAdapterType;

    public ComputerBuilder withRam(int ram) {
        this.ram = ram;
        return this;
    }

    public ComputerBuilder withCpu(double cpu) {
        this.cpu = cpu;
        return this;
    }

    public ComputerBuilder withSsd(int ssd) {
        this.ssd = ssd;
        return this;
    }

    public ComputerBuilder withHdd(int hdd) {
        this.hdd = hdd;
        return this;
    }

    public ComputerBuilder withPower(int hdd) {
        this.power = power;
        return this;
    }

    public ComputerBuilder withHasCdDrive(boolean hasCdDrive) {
        this.hasCdDrive = hasCdDrive;
        return this;
    }

    public ComputerBuilder withVideoAdapterType(String videoAdapterType) {
        this.videoAdapterType = videoAdapterType;
        return this;
    }

    public Computer build() {
        return new Computer(ram, cpu, ssd, hdd, power, hasCdDrive, videoAdapterType);
    }
}

Библиотека Lombok предоставляет аннотацию @Builder, которая позволяет автоматически генерировать код билдера для класса, избавляя разработчика от необходимости писать его вручную.

@Setter
@AllArgsConstructor
@Builder(setterPrefix = "with")
public class Computer {
    private int ram;
    private double cpu;
    private int ssd;
    private int hdd;
    private int power;
    private boolean hasCdDrive;
    private String videoAdapterType;
}
public class Main {
    public static void main (String[] args) {
        // Создание при помощи конструктора
        var computer1 = new Computer(10, 1.3, 100, 0, 1000, false, "");

        // Создание при помощи собственного билдера
        var computer2 = new ComputerBuilder()
            .withRam(10)
            .withPower(1000)
            .withCpu(1.3)
            .withSsd(100)
            .build();

        // Создание при помощи билдера, сгенерированного библиотекой Lombok
        var computer3 = Computer.builder()
            .withRam(10)
            .withPower(1000)
            .withCpu(1.3)
            .withSsd(100)
            .build();
    }
}

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

Prototype (Прототип)

Паттерн Прототип — это порождающий паттерн проектирования, который позволяет создавать новые объекты на основе существующих, копируя их

public interface Cloneable<T> {
    T clone();
}

public interface Shape extends Cloneable<Shape> {
    double getArea();
    Shape clone();
}


public class Circle implements Shape {

    private final int radius;

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

    @Override
    public Shape clone() {
        // Снаружи радиус не доступен
        // Поэтому сделать такой вызов снаружи не получится, не прибегая к рефлексии
        return new Circle(this.radius);
    }
}


@AllArgsConstructor
public class Square implements Shape {
    private final int a;

    @Override
    public double getArea() {
        return a * a;
    }

    @Override
    public Shape clone() {
        return new Square(a);
    }
}
public class Main {
    public static void main(String[] args) {
        List<Shape> shapes = List.of(
            new Square(10), new Circle(10)
        );

        var newShapes = shapes.stream()
            .map (Shape::clone)
            .collect(Collectors.toList);

        System.out .println(newShapes);
    }

}

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

Структурные паттерны

Composite (Компоновщик)

Паттерн "Компоновщик" (Composite) — это структурный паттерн, который позволяет объединять объекты в древовидные структуры для представления иерархий "часть-целое". Он позволяет клиентам работать с индивидуальными объектами и их композициями единообразно, что упрощает управление сложными структурами. Этот паттерн особенно полезен в ситуациях, когда необходимо обрабатывать группы объектов так же, как и отдельные экземпляры, например, в графических интерфейсах или файловых системах.

public interface Component {
    void draw();
}

@AllArgsConstructor
public class Window implements Component {
    List<Component> childComponents;

    public void draw() {
        System.out.println("drawWindow");
        childComponents.forEach(Component::draw);
    }
}

@AllArgsConstructor
public class Block implements Component {
    List<Component> childComponents;

    public void draw() {
        System.out.println("drawBlock");
        childComponents.forEach(Component::draw);
    }
}

@AllArgsConstructor
public class Button implements Component {
    public void draw() {
        System.out.println("draw button");
    }
}

@AllArgsConstructor
public class TextInput implements Component {
    public void draw() {
        System.out.println("draw text input");
    }
}
public class Main {
    public static void main(String[] args) {
        var window = new Window(
            List.of(
                new Button(),
                new Button(),
                new TextInput(),
                new Block(
                    List.of(
                        new Button()
                    )
                )
            )
        );

        window.draw();
    }
}

Decorator (Декоратор)

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

public interface Order {
    int getTotalPrice();
}

@AllArgsConstructor
public class BaseOrder implements Order {

    public List<Integer> prices;

    @Override
    public int getTotalPrice() {
        return prices.stream().mapToInt(x -> x).sum();
    }
}

@AllArgsConstructor
public class DeliveryOrder implements Order {

    private Order order;

    @Override
    public int getTotalPrice() {
        return order.getTotalPrice() + 1000;
    }
}

@AllArgsConstructor
public class InsOrder implements Order {

    private Order order;

    @Override
    public int getTotalPrice() {
        return order.getTotalPrice() + 200;
    }
}
public class Main {
    public static void main(String[] args) {
        Order order = new BaseOrder(List.of(100, 500, 100)); // 700
        order = new Delivery0rder(order); // 1700
        order = new InsOrder(order); // 1900

        System.out.println(order.getTotalPrice()); //=> 1900
    }
}

Facade (Фасад)

Паттерн "Фасад" (Facade) — это структурный паттерн проектирования, который предоставляет упрощенный интерфейс к сложной системе классов, библиотек или фреймворков. Он скрывает сложность внутренней реализации и облегчает взаимодействие с системой, позволяя клиентам использовать только необходимые методы без необходимости разбираться в деталях. Этот паттерн особенно полезен для упрощения работы с большими и сложными системами, делая их более доступными и удобными для использования

public interface HostingImageLoader {
    void loadImage(String filePath);
}

public class HostingImageLoaderImpl implements HostingImageLoade {
    public void loadImage(String filePath) {
        var image = Reader.readFile(filePath);
        var croppedImage = ImageProcessor.crop(image);
        var jpgImage = ImageConverter.convert(croppedImage, "jpg");
        var compressedImage = ImageProcessor.compress(jpgImage);
        ImageLoader.load(compressedImage);
    }
}
public class Client {
    private HostinaImageLoader loader;

    public void loadImage(String filePath) {
        loader.loadImage(filePath);
    }
}

Дополнительные материалы

  1. Рефакторинг.Гуру

Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff
Рекомендуемые программы
профессия
Программирование на Java, Разработка веб-приложений и микросервисов используя Spring Boot, проектирование REST API
10 месяцев
с нуля
Старт 27 февраля

Используйте Хекслет по-максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»