Что такое паттерны программирования?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
# Паттерны программирования (Design Patterns)
Определение
Паттерн программирования (Design Pattern) — это проверенное решение часто встречаемой проблемы в коде. Паттерны описывают структуру, поведение и отношения между элементами кода для решения типичных задач.
Паттерны — это как рецепты или шаблоны, которые помогают:
- Избежать ошибок при проектировании
- Сделать код более гибким и переиспользуемым
- Улучшить коммуникацию в команде (общий язык)
Категории паттернов
1. Порождающие паттерны (Creational)
Решают задачи создания объектов.
Singleton (Одиночка)
Гарантирует, что класс имеет только один экземпляр, и предоставляет глобальный доступ.
public class DatabaseConnection {
private static DatabaseConnection instance;
private DatabaseConnection() {}
public static synchronized DatabaseConnection getInstance() {
if (instance == null) {
instance = new DatabaseConnection();
}
return instance;
}
}
// Использование
DatabaseConnection db = DatabaseConnection.getInstance();
DatabaseConnection db2 = DatabaseConnection.getInstance();
assert db == db2; // true - один объект
Factory (Фабрика)
Создаёт объекты без указания точных классов.
public abstract class DatabaseFactory {
public static Database createDatabase(String type) {
switch (type) {
case "postgresql":
return new PostgresqlDatabase();
case "mysql":
return new MysqlDatabase();
default:
throw new IllegalArgumentException("Unknown type: " + type);
}
}
}
public interface Database {
void connect();
}
public class PostgresqlDatabase implements Database {
@Override
public void connect() {
System.out.println("Connected to PostgreSQL");
}
}
// Использование
Database db = DatabaseFactory.createDatabase("postgresql");
db.connect();
Builder (Строитель)
Строит сложные объекты пошагово.
public class User {
private String name;
private String email;
private int age;
private String phone;
public static class UserBuilder {
private String name;
private String email;
private int age;
private String phone;
public UserBuilder name(String name) {
this.name = name;
return this;
}
public UserBuilder email(String email) {
this.email = email;
return this;
}
public UserBuilder age(int age) {
this.age = age;
return this;
}
public UserBuilder phone(String phone) {
this.phone = phone;
return this;
}
public User build() {
User user = new User();
user.name = this.name;
user.email = this.email;
user.age = this.age;
user.phone = this.phone;
return user;
}
}
}
// Использование
User user = new User.UserBuilder()
.name("John")
.email("john@example.com")
.age(30)
.build();
Prototype (Прототип)
Создаёт объект путём копирования существующего объекта.
public class Document implements Cloneable {
private String title;
private String content;
@Override
public Document clone() throws CloneNotSupportedException {
return (Document) super.clone();
}
}
// Использование
Document original = new Document();
original.setTitle("Important");
Document copy = original.clone();
2. Структурные паттерны (Structural)
Решают задачи построения сложных структур из классов и объектов.
Adapter (Адаптер)
Позволяет несовместимым интерфейсам работать вместе.
public interface Target {
void request();
}
public class Adaptee {
public void specificRequest() {
System.out.println("Specific request");
}
}
public class Adapter implements Target {
private Adaptee adaptee;
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
adaptee.specificRequest(); // Преобразование интерфейса
}
}
Proxy (Прокси)
Предоставляет заменитель или место-держатель для другого объекта для управления доступом.
public interface Image {
void display();
}
public class ProxyImage implements Image {
private String filename;
private Image realImage;
public ProxyImage(String filename) {
this.filename = filename;
}
@Override
public void display() {
if (realImage == null) {
realImage = new RealImage(filename); // Ленивая загрузка
}
realImage.display();
}
}
Decorator (Декоратор)
Добавляет новую функциональность к существующему объекту динамически.
public interface Coffee {
double getCost();
String getDescription();
}
public class SimpleCoffee implements Coffee {
@Override
public double getCost() { return 2.0; }
@Override
public String getDescription() { return "Coffee"; }
}
public class CoffeeWithMilk implements Coffee {
private Coffee coffee;
public CoffeeWithMilk(Coffee coffee) {
this.coffee = coffee;
}
@Override
public double getCost() { return coffee.getCost() + 0.5; }
@Override
public String getDescription() { return coffee.getDescription() + ", Milk"; }
}
// Использование
Coffee coffee = new SimpleCoffee(); // 2.0
coffee = new CoffeeWithMilk(coffee); // 2.5
coffee = new CoffeeWithSugar(coffee); // 2.7
3. Поведенческие паттерны (Behavioral)
Решают задачи взаимодействия между объектами и распределения ответственности.
Observer (Наблюдатель)
Определяет отношение один-ко-многим между объектами так, чтобы при изменении состояния одного объекта все его зависимые объекты автоматически уведомлялись.
public class Subject {
private List<Observer> observers = new ArrayList<>();
public void attach(Observer observer) {
observers.add(observer);
}
public void notifyObservers(String message) {
for (Observer observer : observers) {
observer.update(message);
}
}
}
public interface Observer {
void update(String message);
}
public class ConcreteObserver implements Observer {
@Override
public void update(String message) {
System.out.println("Received: " + message);
}
}
Strategy (Стратегия)
Определяет семейство алгоритмов, инкапсулирует каждый и делает их взаимозаменяемыми.
public interface PaymentStrategy {
void pay(double amount);
}
public class CreditCardPayment implements PaymentStrategy {
@Override
public void pay(double amount) {
System.out.println("Paid " + amount + " with credit card");
}
}
public class PayPalPayment implements PaymentStrategy {
@Override
public void pay(double amount) {
System.out.println("Paid " + amount + " with PayPal");
}
}
public class ShoppingCart {
private PaymentStrategy paymentStrategy;
public void setPaymentStrategy(PaymentStrategy strategy) {
this.paymentStrategy = strategy;
}
public void checkout(double total) {
paymentStrategy.pay(total);
}
}
// Использование
ShoppingCart cart = new ShoppingCart();
cart.setPaymentStrategy(new CreditCardPayment());
cart.checkout(100);
Command (Команда)
Инкапсулирует запрос как объект для параметризации клиентов с различными запросами.
public interface Command {
void execute();
void undo();
}
public class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() { light.on(); }
@Override
public void undo() { light.off(); }
}
public class RemoteControl {
private Command lastCommand;
public void press(Command command) {
command.execute();
lastCommand = command;
}
public void undoLastCommand() {
lastCommand.undo();
}
}
Когда использовать какой паттерн
| Проблема | Паттерн |
|---|---|
| Нужен единственный экземпляр | Singleton |
| Создание объектов без указания класса | Factory |
| Сложное построение объекта | Builder |
| Несовместимые интерфейсы | Adapter |
| Управление доступом к объекту | Proxy |
| Добавление функции к объекту | Decorator |
| Уведомление нескольких объектов об изменениях | Observer |
| Разные способы выполнить задачу | Strategy |
| Инкапсуляция операции | Command |
Рекомендации
- Не переусложняй — используй паттерны когда они решают реальную проблему
- Знай SOLID — большинство паттернов применяют SOLID принципы
- Используй Spring для реализации — многие паттерны уже встроены в Spring (Singleton scope, Factory, Proxy через AOP)
- Читай исходный код — изучай как реализованы паттерны в известных фреймворках
Заключение
Паттерны программирования — это язык для описания решений. Они помогают:
- Быстрее разрабатывать
- Избегать ошибок
- Говорить с командой на одном языке
- Делать код гибким и переиспользуемым
Основные паттерны: Singleton, Factory, Builder, Adapter, Decorator, Observer, Strategy, Command. Изучи их и применяй разумно.