Какие паттерны проектирования вы применяли на практике?

«Какие паттерны проектирования вы применяли на практике?» — вопрос из категории Паттерны, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Часто используемые паттерны в Java:

  1. Singleton (Одиночка): Гарантирует существование только одного экземпляра класса. Важно учитывать потокобезопасность.

    public class ThreadSafeSingleton {
        private static volatile ThreadSafeSingleton instance;
    
        private ThreadSafeSingleton() {}
    
        public static ThreadSafeSingleton getInstance() {
            if (instance == null) {
                synchronized (ThreadSafeSingleton.class) {
                    if (instance == null) {
                        instance = new ThreadSafeSingleton();
                    }
                }
            }
            return instance;
        }
    }
  2. Factory Method (Фабричный метод): Определяет интерфейс для создания объекта, но оставляет подклассам решение о том, какой класс инстанцировать.

    public interface Notification {
        void notifyUser();
    }
    
    public class EmailNotification implements Notification {
        @Override
        public void notifyUser() {
            System.out.println("Sending an email...");
        }
    }
    
    public abstract class NotificationService {
        // Фабричный метод
        public abstract Notification createNotification();
    
        public void sendNotification() {
            Notification notification = createNotification();
            notification.notifyUser();
        }
    }
  3. Observer (Наблюдатель): Определяет зависимость "один ко многим", чтобы при изменении состояния одного объекта все зависящие от него объекты уведомлялись автоматически. Реализован в Java через java.util.Observable и java.util.Observer (устарели) или через собственную реализацию.

Также активно применяю:

  • Builder: Для пошагового создания сложных объектов (особенно полезен с Lombok @Builder).
  • Strategy: Для инкапсуляции семейства алгоритмов и их взаимозаменяемости.
  • Decorator: Для динамического добавления новой функциональности объектам (реализуется через композицию).
  • Adapter: Для обеспечения совместной работы классов с несовместимыми интерфейсами.